mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
69 Commits
release-1.
...
beta-1.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2aa39e43ef | ||
|
|
efb294de53 | ||
|
|
99755619c5 | ||
|
|
6d26db3d66 | ||
|
|
51301968a5 | ||
|
|
feb1c1b655 | ||
|
|
15d29f6189 | ||
|
|
217a6dbf1c | ||
|
|
dfcd626081 | ||
|
|
347895f6b2 | ||
|
|
7a6d075984 | ||
|
|
68ab3b0e97 | ||
|
|
16fc4f4527 | ||
|
|
8657216345 | ||
|
|
42e2926d61 | ||
|
|
a261b8e739 | ||
|
|
1699d6b5f8 | ||
|
|
848872a803 | ||
|
|
04ed3a652a | ||
|
|
d20457f338 | ||
|
|
ab29aacce0 | ||
|
|
46bb2b8ec2 | ||
|
|
6b6880c1ff | ||
|
|
5defd500ae | ||
|
|
37ff06d192 | ||
|
|
85aa21ebf6 | ||
|
|
e448699895 | ||
|
|
200f83bcfe | ||
|
|
89cce89650 | ||
|
|
8982964fbf | ||
|
|
f3a3fa0ea8 | ||
|
|
0865a06ac8 | ||
|
|
f2738c8bc4 | ||
|
|
1321f8775e | ||
|
|
9764aba47d | ||
|
|
913e5da2f5 | ||
|
|
f2ce7a386b | ||
|
|
7607b65e82 | ||
|
|
c13c2d62f5 | ||
|
|
8ea7204cf6 | ||
|
|
6ec382cfc4 | ||
|
|
ad0b28a684 | ||
|
|
0ae94e9579 | ||
|
|
57bd5789d4 | ||
|
|
f7dde1250c | ||
|
|
13d96651b4 | ||
|
|
65029982ce | ||
|
|
380921ce25 | ||
|
|
87ee8cd653 | ||
|
|
d4810a6f71 | ||
|
|
aa56aba1a5 | ||
|
|
35438dbac1 | ||
|
|
543b1178a1 | ||
|
|
7f1071f5cd | ||
|
|
e8c694fe00 | ||
|
|
b58b0c74a9 | ||
|
|
9e5029917e | ||
|
|
12ca74f86a | ||
|
|
622683f45e | ||
|
|
e66f92f27e | ||
|
|
44acda2045 | ||
|
|
afd92dd916 | ||
|
|
2a969f8e0b | ||
|
|
ddc6606ccf | ||
|
|
a19a4f36c6 | ||
|
|
6765de992d | ||
|
|
b24a18b231 | ||
|
|
8e83743dd7 | ||
|
|
6a91d18003 |
10
CONTRIBUTING.md
Normal file
10
CONTRIBUTING.md
Normal file
@@ -0,0 +1,10 @@
|
||||
Folder-Description:
|
||||
===================
|
||||
* `briar-*`: Specifically for the Briar app (Phone/Desktop/Headless)
|
||||
* `bramble-*`: The protocol stack - not necessarily Briar-dependent
|
||||
|
||||
---
|
||||
|
||||
* `*-api`: public stuff that can be referenced from other packages and modules - mostly interfaces + a few utility classes
|
||||
* `*-core`: implementations of api that are portable across Android/Desktop/Headless
|
||||
* `*-java`: implementations of api that are specific to Desktop & Headless
|
||||
@@ -15,8 +15,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
versionCode 10411
|
||||
versionName "1.4.11"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -18,3 +18,7 @@
|
||||
-dontnote com.google.common.**
|
||||
|
||||
-dontwarn com.fasterxml.jackson.databind.ext.Java7SupportImpl
|
||||
|
||||
# Keep all Jackson-serialisable classes and their members
|
||||
-keep interface com.fasterxml.jackson.databind.annotation.JsonSerialize
|
||||
-keep @com.fasterxml.jackson.databind.annotation.JsonSerialize class * { *; }
|
||||
|
||||
@@ -349,13 +349,13 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
Metadata query) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of some messages received from the given contact that
|
||||
* need to be acknowledged, up to the given number of messages.
|
||||
* Returns the IDs of all messages received from the given contact that
|
||||
* need to be acknowledged.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
Collection<MessageId> getMessagesToAck(Transaction txn, ContactId c,
|
||||
int maxMessages) throws DbException;
|
||||
Collection<MessageId> getMessagesToAck(Transaction txn, ContactId c)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of some messages that are eligible to be sent to the
|
||||
@@ -492,6 +492,8 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
* Returns the message with the given ID for transmission to the given
|
||||
* contact over a transport with the given maximum latency. Returns null
|
||||
* if the message is no longer visible to the contact.
|
||||
* <p/>
|
||||
* Read-only if {@code markAsSent} is false.
|
||||
*
|
||||
* @param markAsSent True if the message should be marked as sent.
|
||||
* If false it can be marked as sent by calling
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.NullSafety;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -75,4 +76,23 @@ public class MailboxProperties {
|
||||
public MailboxFolderId getOutboxId() {
|
||||
return outboxId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o instanceof MailboxProperties) {
|
||||
MailboxProperties m = (MailboxProperties) o;
|
||||
return owner == m.owner &&
|
||||
onion.equals(m.onion) &&
|
||||
authToken.equals(m.authToken) &&
|
||||
NullSafety.equals(inboxId, m.inboxId) &&
|
||||
NullSafety.equals(outboxId, m.outboxId) &&
|
||||
serverSupports.equals(m.serverSupports);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return authToken.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,6 @@ public interface MailboxSettingsManager {
|
||||
|
||||
MailboxStatus getOwnMailboxStatus(Transaction txn) throws DbException;
|
||||
|
||||
void recordSuccessfulConnection(Transaction txn, long now)
|
||||
throws DbException;
|
||||
|
||||
void recordSuccessfulConnection(Transaction txn, long now,
|
||||
List<MailboxVersion> versions) throws DbException;
|
||||
|
||||
@@ -52,10 +49,8 @@ public interface MailboxSettingsManager {
|
||||
* Called when Briar is paired with a mailbox
|
||||
*
|
||||
* @param txn A read-write transaction
|
||||
* @param ownOnion Our new mailbox's onion (56 base32 chars)
|
||||
*/
|
||||
void mailboxPaired(Transaction txn, String ownOnion,
|
||||
List<MailboxVersion> serverSupports)
|
||||
void mailboxPaired(Transaction txn, MailboxProperties p)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,6 +67,13 @@ public class MailboxStatus {
|
||||
return attemptsSinceSuccess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mailbox's supported API versions.
|
||||
*/
|
||||
public List<MailboxVersion> getServerSupports() {
|
||||
return serverSupports;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this status indicates a problem with the mailbox.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.briarproject.bramble.api.mailbox.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a mailbox is paired.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MailboxPairedEvent extends Event {
|
||||
|
||||
private final MailboxProperties properties;
|
||||
private final Map<ContactId, MailboxUpdateWithMailbox> localUpdates;
|
||||
|
||||
public MailboxPairedEvent(MailboxProperties properties,
|
||||
Map<ContactId, MailboxUpdateWithMailbox> localUpdates) {
|
||||
this.properties = properties;
|
||||
this.localUpdates = localUpdates;
|
||||
}
|
||||
|
||||
public MailboxProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public Map<ContactId, MailboxUpdateWithMailbox> getLocalUpdates() {
|
||||
return localUpdates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package org.briarproject.bramble.api.mailbox.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a mailbox is unpaired.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MailboxUnpairedEvent extends Event {
|
||||
|
||||
private final Map<ContactId, MailboxUpdate> localUpdates;
|
||||
|
||||
public MailboxUnpairedEvent(Map<ContactId, MailboxUpdate> localUpdates) {
|
||||
this.localUpdates = localUpdates;
|
||||
}
|
||||
|
||||
public Map<ContactId, MailboxUpdate> getLocalUpdates() {
|
||||
return localUpdates;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package org.briarproject.bramble.api.mailbox.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when a mailbox update is sent to a contact.
|
||||
* <p>
|
||||
* Note that this event is not broadcast when a mailbox is paired or
|
||||
* unpaired, although updates are sent to all contacts in those situations.
|
||||
*
|
||||
* @see MailboxPairedEvent
|
||||
* @see MailboxUnpairedEvent
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MailboxUpdateSentEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final MailboxUpdate mailboxUpdate;
|
||||
|
||||
public MailboxUpdateSentEvent(ContactId contactId,
|
||||
MailboxUpdate mailboxUpdate) {
|
||||
this.contactId = contactId;
|
||||
this.mailboxUpdate = mailboxUpdate;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public MailboxUpdate getMailboxUpdate() {
|
||||
return mailboxUpdate;
|
||||
}
|
||||
}
|
||||
@@ -338,6 +338,17 @@ public class TestUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static <E extends Event> E getEvent(Transaction txn,
|
||||
Class<E> eventClass) {
|
||||
for (CommitAction action : txn.getActions()) {
|
||||
if (action instanceof EventAction) {
|
||||
Event event = ((EventAction) action).getEvent();
|
||||
if (eventClass.isInstance(event)) return eventClass.cast(event);
|
||||
}
|
||||
}
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
public static boolean isCryptoStrengthUnlimited() {
|
||||
try {
|
||||
return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding")
|
||||
|
||||
@@ -16,7 +16,7 @@ dependencies {
|
||||
implementation 'org.bitlet:weupnp:0.1.4'
|
||||
implementation 'net.i2p.crypto:eddsa:0.2.0'
|
||||
implementation 'org.whispersystems:curve25519-java:0.5.0'
|
||||
implementation 'org.briarproject:jtorctl:0.4'
|
||||
implementation 'org.briarproject:jtorctl:0.5'
|
||||
|
||||
//noinspection GradleDependency
|
||||
implementation "com.squareup.okhttp3:okhttp:$okhttp_version"
|
||||
|
||||
@@ -52,6 +52,7 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Collections.sort;
|
||||
import static org.briarproject.bramble.api.client.ContactGroupConstants.GROUP_KEY_CONTACT_ID;
|
||||
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
@@ -474,6 +475,8 @@ class ClientHelperImpl implements ClientHelper {
|
||||
list.add(new MailboxVersion(element.getLong(0).intValue(),
|
||||
element.getLong(1).intValue()));
|
||||
}
|
||||
// Sort the list of versions for easier comparison
|
||||
sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@@ -620,11 +620,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
|
||||
@Override
|
||||
public Collection<MessageId> getMessagesToAck(Transaction transaction,
|
||||
ContactId c, int maxMessages) throws DbException {
|
||||
ContactId c) throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
return db.getMessagesToAck(txn, c, maxMessages);
|
||||
return db.getMessagesToAck(txn, c, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -746,7 +746,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
public Message getMessageToSend(Transaction transaction, ContactId c,
|
||||
MessageId m, long maxLatency, boolean markAsSent)
|
||||
throws DbException {
|
||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||
if (markAsSent && transaction.isReadOnly()) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
@@ -1097,11 +1099,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsContact(txn, c))
|
||||
throw new NoSuchContactException();
|
||||
List<MessageId> visible = new ArrayList<>(acked.size());
|
||||
for (MessageId m : acked) {
|
||||
if (db.containsVisibleMessage(txn, c, m)) visible.add(m);
|
||||
}
|
||||
db.lowerAckFlag(txn, c, visible);
|
||||
db.lowerAckFlag(txn, c, acked);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,7 +10,6 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@@ -30,7 +29,6 @@ class ContactMailboxClient implements MailboxClient {
|
||||
@Nullable
|
||||
private MailboxWorker uploadWorker = null, downloadWorker = null;
|
||||
|
||||
@Inject
|
||||
ContactMailboxClient(MailboxWorkerFactory workerFactory,
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor reachabilityMonitor) {
|
||||
@@ -57,6 +55,10 @@ class ContactMailboxClient implements MailboxClient {
|
||||
}
|
||||
if (uploadWorker != null) uploadWorker.destroy();
|
||||
if (downloadWorker != null) downloadWorker.destroy();
|
||||
// The connectivity checker belongs to the client, so it should be
|
||||
// destroyed. The Tor reachability monitor is shared between clients,
|
||||
// so it should not be destroyed
|
||||
connectivityChecker.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -5,14 +5,23 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ContactMailboxConnectivityChecker.class.getName());
|
||||
|
||||
private final MailboxApi mailboxApi;
|
||||
|
||||
@Inject
|
||||
ContactMailboxConnectivityChecker(Clock clock,
|
||||
MailboxApiCaller mailboxApiCaller, MailboxApi mailboxApi) {
|
||||
super(clock, mailboxApiCaller);
|
||||
@@ -23,7 +32,9 @@ class ContactMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
||||
ApiCall createConnectivityCheckTask(MailboxProperties properties) {
|
||||
if (properties.isOwner()) throw new IllegalArgumentException();
|
||||
return new SimpleApiCall(() -> {
|
||||
LOG.info("Checking connectivity of contact's mailbox");
|
||||
if (!mailboxApi.checkStatus(properties)) throw new ApiException();
|
||||
LOG.info("Contact's mailbox is reachable");
|
||||
// Call the observers and cache the result
|
||||
onConnectivityCheckSucceeded(clock.currentTimeMillis());
|
||||
});
|
||||
|
||||
@@ -1,73 +1,25 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class ContactMailboxDownloadWorker implements MailboxWorker,
|
||||
ConnectivityObserver, TorReachabilityObserver {
|
||||
|
||||
/**
|
||||
* When the worker is started it waits for a connectivity check, then
|
||||
* starts its first download cycle: checking the inbox, downloading and
|
||||
* deleting any files, and checking again until the inbox is empty.
|
||||
* <p>
|
||||
* The worker then waits for our Tor hidden service to be reachable before
|
||||
* starting its second download cycle. This ensures that if a contact
|
||||
* tried and failed to connect to our hidden service before it was
|
||||
* reachable, and therefore uploaded a file to the mailbox instead, we'll
|
||||
* find the file in the second download cycle.
|
||||
*/
|
||||
private enum State {
|
||||
CREATED,
|
||||
CONNECTIVITY_CHECK,
|
||||
DOWNLOAD_CYCLE_1,
|
||||
WAITING_FOR_TOR,
|
||||
DOWNLOAD_CYCLE_2,
|
||||
FINISHED,
|
||||
DESTROYED
|
||||
}
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(ContactMailboxDownloadWorker.class.getName());
|
||||
|
||||
private final ConnectivityChecker connectivityChecker;
|
||||
private final TorReachabilityMonitor torReachabilityMonitor;
|
||||
private final MailboxApiCaller mailboxApiCaller;
|
||||
private final MailboxApi mailboxApi;
|
||||
private final MailboxFileManager mailboxFileManager;
|
||||
private final MailboxProperties mailboxProperties;
|
||||
private final Object lock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
private State state = State.CREATED;
|
||||
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private Cancellable apiCall = null;
|
||||
class ContactMailboxDownloadWorker extends MailboxDownloadWorker {
|
||||
|
||||
ContactMailboxDownloadWorker(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
@@ -76,57 +28,14 @@ class ContactMailboxDownloadWorker implements MailboxWorker,
|
||||
MailboxApi mailboxApi,
|
||||
MailboxFileManager mailboxFileManager,
|
||||
MailboxProperties mailboxProperties) {
|
||||
super(connectivityChecker, torReachabilityMonitor, mailboxApiCaller,
|
||||
mailboxApi, mailboxFileManager, mailboxProperties);
|
||||
if (mailboxProperties.isOwner()) throw new IllegalArgumentException();
|
||||
this.connectivityChecker = connectivityChecker;
|
||||
this.torReachabilityMonitor = torReachabilityMonitor;
|
||||
this.mailboxApiCaller = mailboxApiCaller;
|
||||
this.mailboxApi = mailboxApi;
|
||||
this.mailboxFileManager = mailboxFileManager;
|
||||
this.mailboxProperties = mailboxProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
LOG.info("Started");
|
||||
synchronized (lock) {
|
||||
// Don't allow the worker to be reused
|
||||
if (state != State.CREATED) return;
|
||||
state = State.CONNECTIVITY_CHECK;
|
||||
}
|
||||
// Avoid leaking observer in case destroy() is called concurrently
|
||||
// before observer is added
|
||||
connectivityChecker.checkConnectivity(mailboxProperties, this);
|
||||
boolean destroyed;
|
||||
synchronized (lock) {
|
||||
destroyed = state == State.DESTROYED;
|
||||
}
|
||||
if (destroyed) connectivityChecker.removeObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
LOG.info("Destroyed");
|
||||
Cancellable apiCall;
|
||||
synchronized (lock) {
|
||||
state = State.DESTROYED;
|
||||
apiCall = this.apiCall;
|
||||
this.apiCall = null;
|
||||
}
|
||||
if (apiCall != null) apiCall.cancel();
|
||||
connectivityChecker.removeObserver(this);
|
||||
torReachabilityMonitor.removeObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectivityCheckSucceeded() {
|
||||
LOG.info("Connectivity check succeeded");
|
||||
synchronized (lock) {
|
||||
if (state != State.CONNECTIVITY_CHECK) return;
|
||||
state = State.DOWNLOAD_CYCLE_1;
|
||||
// Start first download cycle
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(this::apiCallListInbox));
|
||||
}
|
||||
protected ApiCall createApiCallForDownloadCycle() {
|
||||
return new SimpleApiCall(this::apiCallListInbox);
|
||||
}
|
||||
|
||||
private void apiCallListInbox() throws IOException, ApiException {
|
||||
@@ -134,110 +43,23 @@ class ContactMailboxDownloadWorker implements MailboxWorker,
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
LOG.info("Listing inbox");
|
||||
List<MailboxFile> files = mailboxApi.getFiles(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()));
|
||||
if (files.isEmpty()) onDownloadCycleFinished();
|
||||
else downloadNextFile(new LinkedList<>(files));
|
||||
}
|
||||
|
||||
private void onDownloadCycleFinished() {
|
||||
boolean addObserver = false;
|
||||
synchronized (lock) {
|
||||
if (state == State.DOWNLOAD_CYCLE_1) {
|
||||
LOG.info("First download cycle finished");
|
||||
state = State.WAITING_FOR_TOR;
|
||||
apiCall = null;
|
||||
addObserver = true;
|
||||
} else if (state == State.DOWNLOAD_CYCLE_2) {
|
||||
LOG.info("Second download cycle finished");
|
||||
state = State.FINISHED;
|
||||
apiCall = null;
|
||||
}
|
||||
}
|
||||
if (addObserver) {
|
||||
// Avoid leaking observer in case destroy() is called concurrently
|
||||
// before observer is added
|
||||
torReachabilityMonitor.addOneShotObserver(this);
|
||||
boolean destroyed;
|
||||
synchronized (lock) {
|
||||
destroyed = state == State.DESTROYED;
|
||||
}
|
||||
if (destroyed) torReachabilityMonitor.removeObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
private void downloadNextFile(Queue<MailboxFile> queue) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
MailboxFile file = queue.remove();
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(() -> apiCallDownloadFile(file, queue)));
|
||||
}
|
||||
}
|
||||
|
||||
private void apiCallDownloadFile(MailboxFile file,
|
||||
Queue<MailboxFile> queue) throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
LOG.info("Downloading file");
|
||||
File tempFile = mailboxFileManager.createTempFileForDownload();
|
||||
MailboxFolderId folderId =
|
||||
requireNonNull(mailboxProperties.getInboxId());
|
||||
List<MailboxFile> files;
|
||||
try {
|
||||
mailboxApi.getFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()),
|
||||
file.name, tempFile);
|
||||
} catch (IOException | ApiException e) {
|
||||
if (!tempFile.delete()) {
|
||||
LOG.warning("Failed to delete temporary file");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
mailboxFileManager.handleDownloadedFile(tempFile);
|
||||
deleteFile(file, queue);
|
||||
}
|
||||
|
||||
private void deleteFile(MailboxFile file, Queue<MailboxFile> queue) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(() -> apiCallDeleteFile(file, queue)));
|
||||
}
|
||||
}
|
||||
|
||||
private void apiCallDeleteFile(MailboxFile file, Queue<MailboxFile> queue)
|
||||
throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
try {
|
||||
mailboxApi.deleteFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()), file.name);
|
||||
files = mailboxApi.getFiles(mailboxProperties, folderId);
|
||||
} catch (TolerableFailureException e) {
|
||||
// Catch this so we can continue to the next file
|
||||
logException(LOG, INFO, e);
|
||||
LOG.warning("Inbox folder does not exist");
|
||||
files = emptyList();
|
||||
}
|
||||
if (queue.isEmpty()) {
|
||||
// List the inbox again to check for files that may have arrived
|
||||
// while we were downloading
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(this::apiCallListInbox));
|
||||
}
|
||||
if (files.isEmpty()) {
|
||||
onDownloadCycleFinished();
|
||||
} else {
|
||||
Queue<FolderFile> queue = new LinkedList<>();
|
||||
for (MailboxFile file : files) {
|
||||
queue.add(new FolderFile(folderId, file.name));
|
||||
}
|
||||
downloadNextFile(queue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorReachable() {
|
||||
LOG.info("Our Tor hidden service is reachable");
|
||||
synchronized (lock) {
|
||||
if (state != State.WAITING_FOR_TOR) return;
|
||||
state = State.DOWNLOAD_CYCLE_2;
|
||||
// Start second download cycle
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(this::apiCallListInbox));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,9 +91,13 @@ interface MailboxApi {
|
||||
* Used by owner and contacts to list their files to retrieve.
|
||||
* <p>
|
||||
* Returns 200 OK with the list of files in JSON.
|
||||
*
|
||||
* @throws TolerableFailureException if response code is 404 (folder does
|
||||
* not exist or client is not authorised to download from it)
|
||||
*/
|
||||
List<MailboxFile> getFiles(MailboxProperties properties,
|
||||
MailboxFolderId folderId) throws IOException, ApiException;
|
||||
MailboxFolderId folderId)
|
||||
throws IOException, ApiException, TolerableFailureException;
|
||||
|
||||
/**
|
||||
* Used by owner and contacts to retrieve a file.
|
||||
@@ -102,17 +106,22 @@ interface MailboxApi {
|
||||
* in the response body.
|
||||
*
|
||||
* @param file the empty file the response bytes will be written into.
|
||||
* @throws TolerableFailureException if response code is 404 (folder does
|
||||
* not exist, client is not authorised to download from folder, or file
|
||||
* does not exist)
|
||||
*/
|
||||
void getFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||
MailboxFileId fileId, File file) throws IOException, ApiException;
|
||||
MailboxFileId fileId, File file)
|
||||
throws IOException, ApiException, TolerableFailureException;
|
||||
|
||||
/**
|
||||
* Used by owner and contacts to delete files.
|
||||
* <p>
|
||||
* Returns 200 OK (no exception) if deletion was successful.
|
||||
*
|
||||
* @throws TolerableFailureException on 404 response,
|
||||
* because file was most likely deleted already.
|
||||
* @throws TolerableFailureException if response code is 404 (folder does
|
||||
* not exist, client is not authorised to download from folder, or file
|
||||
* does not exist)
|
||||
*/
|
||||
void deleteFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||
MailboxFileId fileId)
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -35,6 +34,7 @@ import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import static com.fasterxml.jackson.databind.MapperFeature.BLOCK_UNSAFE_POLYMORPHIC_BASE_TYPES;
|
||||
import static java.util.Collections.sort;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static okhttp3.internal.Util.EMPTY_REQUEST;
|
||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
@@ -125,6 +125,8 @@ class MailboxApiImpl implements MailboxApi {
|
||||
if (major < 0 || minor < 0) throw new ApiException();
|
||||
serverSupports.add(new MailboxVersion(major, minor));
|
||||
}
|
||||
// Sort the list of versions for easier comparison
|
||||
sort(serverSupports);
|
||||
return serverSupports;
|
||||
}
|
||||
|
||||
@@ -216,9 +218,11 @@ class MailboxApiImpl implements MailboxApi {
|
||||
|
||||
@Override
|
||||
public List<MailboxFile> getFiles(MailboxProperties properties,
|
||||
MailboxFolderId folderId) throws IOException, ApiException {
|
||||
MailboxFolderId folderId)
|
||||
throws IOException, ApiException, TolerableFailureException {
|
||||
String path = "/files/" + folderId;
|
||||
Response response = sendGetRequest(properties, path);
|
||||
if (response.code() == 404) throw new TolerableFailureException();
|
||||
if (response.code() != 200) throw new ApiException();
|
||||
|
||||
ResponseBody body = response.body();
|
||||
@@ -243,7 +247,7 @@ class MailboxApiImpl implements MailboxApi {
|
||||
if (time < 1) throw new ApiException();
|
||||
list.add(new MailboxFile(MailboxFileId.fromString(name), time));
|
||||
}
|
||||
Collections.sort(list);
|
||||
sort(list);
|
||||
return list;
|
||||
} catch (JacksonException | InvalidMailboxIdException e) {
|
||||
throw new ApiException();
|
||||
@@ -252,9 +256,11 @@ class MailboxApiImpl implements MailboxApi {
|
||||
|
||||
@Override
|
||||
public void getFile(MailboxProperties properties, MailboxFolderId folderId,
|
||||
MailboxFileId fileId, File file) throws IOException, ApiException {
|
||||
MailboxFileId fileId, File file)
|
||||
throws IOException, ApiException, TolerableFailureException {
|
||||
String path = "/files/" + folderId + "/" + fileId;
|
||||
Response response = sendGetRequest(properties, path);
|
||||
if (response.code() == 404) throw new TolerableFailureException();
|
||||
if (response.code() != 200) throw new ApiException();
|
||||
|
||||
ResponseBody body = response.body();
|
||||
|
||||
@@ -24,6 +24,10 @@ interface MailboxClient {
|
||||
|
||||
/**
|
||||
* Assigns a contact to the client for upload.
|
||||
*
|
||||
* @param properties Properties for communicating with the mailbox
|
||||
* managed by this client.
|
||||
* @param folderId The ID of the folder to which files will be uploaded.
|
||||
*/
|
||||
void assignContactForUpload(ContactId c, MailboxProperties properties,
|
||||
MailboxFolderId folderId);
|
||||
@@ -35,6 +39,11 @@ interface MailboxClient {
|
||||
|
||||
/**
|
||||
* Assigns a contact to the client for download.
|
||||
*
|
||||
* @param properties Properties for communicating with the mailbox
|
||||
* managed by this client.
|
||||
* @param folderId The ID of the folder from which files will be
|
||||
* downloaded.
|
||||
*/
|
||||
void assignContactForDownload(ContactId c, MailboxProperties properties,
|
||||
MailboxFolderId folderId);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
@NotNullByDefault
|
||||
interface MailboxClientFactory {
|
||||
|
||||
/**
|
||||
* Creates a client for communicating with a contact's mailbox.
|
||||
*/
|
||||
MailboxClient createContactMailboxClient(
|
||||
TorReachabilityMonitor reachabilityMonitor);
|
||||
|
||||
/**
|
||||
* Creates a client for communicating with our own mailbox.
|
||||
*/
|
||||
MailboxClient createOwnMailboxClient(
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
MailboxProperties properties);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
@NotNullByDefault
|
||||
class MailboxClientFactoryImpl implements MailboxClientFactory {
|
||||
|
||||
private final MailboxWorkerFactory workerFactory;
|
||||
private final Provider<ContactMailboxConnectivityChecker>
|
||||
contactCheckerProvider;
|
||||
private final Provider<OwnMailboxConnectivityChecker> ownCheckerProvider;
|
||||
|
||||
@Inject
|
||||
MailboxClientFactoryImpl(MailboxWorkerFactory workerFactory,
|
||||
Provider<ContactMailboxConnectivityChecker> contactCheckerProvider,
|
||||
Provider<OwnMailboxConnectivityChecker> ownCheckerProvider) {
|
||||
this.workerFactory = workerFactory;
|
||||
this.contactCheckerProvider = contactCheckerProvider;
|
||||
this.ownCheckerProvider = ownCheckerProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxClient createContactMailboxClient(
|
||||
TorReachabilityMonitor reachabilityMonitor) {
|
||||
ConnectivityChecker connectivityChecker = contactCheckerProvider.get();
|
||||
return new ContactMailboxClient(workerFactory, connectivityChecker,
|
||||
reachabilityMonitor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxClient createOwnMailboxClient(
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
MailboxProperties properties) {
|
||||
ConnectivityChecker connectivityChecker = ownCheckerProvider.get();
|
||||
return new OwnMailboxClient(workerFactory, connectivityChecker,
|
||||
reachabilityMonitor, properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
import org.briarproject.bramble.mailbox.TorReachabilityMonitor.TorReachabilityObserver;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Queue;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
abstract class MailboxDownloadWorker implements MailboxWorker,
|
||||
ConnectivityObserver, TorReachabilityObserver {
|
||||
|
||||
/**
|
||||
* When the worker is started it waits for a connectivity check, then
|
||||
* starts its first download cycle: checking for files to download,
|
||||
* downloading and deleting the files, and checking again until all files
|
||||
* have been downloaded and deleted.
|
||||
* <p>
|
||||
* The worker then waits for our Tor hidden service to be reachable before
|
||||
* starting its second download cycle. This ensures that if a contact
|
||||
* tried and failed to connect to our hidden service before it was
|
||||
* reachable, and therefore uploaded a file to the mailbox instead, we'll
|
||||
* find the file in the second download cycle.
|
||||
*/
|
||||
protected enum State {
|
||||
CREATED,
|
||||
CONNECTIVITY_CHECK,
|
||||
DOWNLOAD_CYCLE_1,
|
||||
WAITING_FOR_TOR,
|
||||
DOWNLOAD_CYCLE_2,
|
||||
FINISHED,
|
||||
DESTROYED
|
||||
}
|
||||
|
||||
protected static final Logger LOG =
|
||||
getLogger(MailboxDownloadWorker.class.getName());
|
||||
|
||||
private final ConnectivityChecker connectivityChecker;
|
||||
private final TorReachabilityMonitor torReachabilityMonitor;
|
||||
protected final MailboxApiCaller mailboxApiCaller;
|
||||
protected final MailboxApi mailboxApi;
|
||||
private final MailboxFileManager mailboxFileManager;
|
||||
protected final MailboxProperties mailboxProperties;
|
||||
protected final Object lock = new Object();
|
||||
|
||||
@GuardedBy("lock")
|
||||
protected State state = State.CREATED;
|
||||
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
protected Cancellable apiCall = null;
|
||||
|
||||
/**
|
||||
* Creates the API call that starts the worker's download cycle.
|
||||
*/
|
||||
protected abstract ApiCall createApiCallForDownloadCycle();
|
||||
|
||||
MailboxDownloadWorker(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor torReachabilityMonitor,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
MailboxFileManager mailboxFileManager,
|
||||
MailboxProperties mailboxProperties) {
|
||||
this.connectivityChecker = connectivityChecker;
|
||||
this.torReachabilityMonitor = torReachabilityMonitor;
|
||||
this.mailboxApiCaller = mailboxApiCaller;
|
||||
this.mailboxApi = mailboxApi;
|
||||
this.mailboxFileManager = mailboxFileManager;
|
||||
this.mailboxProperties = mailboxProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
LOG.info("Started");
|
||||
synchronized (lock) {
|
||||
// Don't allow the worker to be reused
|
||||
if (state != State.CREATED) return;
|
||||
state = State.CONNECTIVITY_CHECK;
|
||||
}
|
||||
// Avoid leaking observer in case destroy() is called concurrently
|
||||
// before observer is added
|
||||
connectivityChecker.checkConnectivity(mailboxProperties, this);
|
||||
boolean destroyed;
|
||||
synchronized (lock) {
|
||||
destroyed = state == State.DESTROYED;
|
||||
}
|
||||
if (destroyed) connectivityChecker.removeObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
LOG.info("Destroyed");
|
||||
Cancellable apiCall;
|
||||
synchronized (lock) {
|
||||
state = State.DESTROYED;
|
||||
apiCall = this.apiCall;
|
||||
this.apiCall = null;
|
||||
}
|
||||
if (apiCall != null) apiCall.cancel();
|
||||
connectivityChecker.removeObserver(this);
|
||||
torReachabilityMonitor.removeObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectivityCheckSucceeded() {
|
||||
LOG.info("Connectivity check succeeded");
|
||||
synchronized (lock) {
|
||||
if (state != State.CONNECTIVITY_CHECK) return;
|
||||
state = State.DOWNLOAD_CYCLE_1;
|
||||
// Start first download cycle
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
createApiCallForDownloadCycle());
|
||||
}
|
||||
}
|
||||
|
||||
void onDownloadCycleFinished() {
|
||||
boolean addObserver = false;
|
||||
synchronized (lock) {
|
||||
if (state == State.DOWNLOAD_CYCLE_1) {
|
||||
LOG.info("First download cycle finished");
|
||||
state = State.WAITING_FOR_TOR;
|
||||
apiCall = null;
|
||||
addObserver = true;
|
||||
} else if (state == State.DOWNLOAD_CYCLE_2) {
|
||||
LOG.info("Second download cycle finished");
|
||||
state = State.FINISHED;
|
||||
apiCall = null;
|
||||
}
|
||||
}
|
||||
if (addObserver) {
|
||||
// Avoid leaking observer in case destroy() is called concurrently
|
||||
// before observer is added
|
||||
torReachabilityMonitor.addOneShotObserver(this);
|
||||
boolean destroyed;
|
||||
synchronized (lock) {
|
||||
destroyed = state == State.DESTROYED;
|
||||
}
|
||||
if (destroyed) torReachabilityMonitor.removeObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
void downloadNextFile(Queue<FolderFile> queue) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
if (queue.isEmpty()) {
|
||||
// Check for files again, as new files may have arrived while
|
||||
// we were downloading
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
createApiCallForDownloadCycle());
|
||||
} else {
|
||||
FolderFile file = queue.remove();
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(() ->
|
||||
apiCallDownloadFile(file, queue)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void apiCallDownloadFile(FolderFile file, Queue<FolderFile> queue)
|
||||
throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
LOG.info("Downloading file");
|
||||
File tempFile = mailboxFileManager.createTempFileForDownload();
|
||||
try {
|
||||
mailboxApi.getFile(mailboxProperties, file.folderId, file.fileId,
|
||||
tempFile);
|
||||
} catch (IOException | ApiException e) {
|
||||
if (!tempFile.delete()) {
|
||||
LOG.warning("Failed to delete temporary file");
|
||||
}
|
||||
throw e;
|
||||
} catch (TolerableFailureException e) {
|
||||
// File not found - continue to the next file
|
||||
LOG.warning("File does not exist");
|
||||
if (!tempFile.delete()) {
|
||||
LOG.warning("Failed to delete temporary file");
|
||||
}
|
||||
downloadNextFile(queue);
|
||||
return;
|
||||
}
|
||||
mailboxFileManager.handleDownloadedFile(tempFile);
|
||||
deleteFile(file, queue);
|
||||
}
|
||||
|
||||
private void deleteFile(FolderFile file, Queue<FolderFile> queue) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
new SimpleApiCall(() -> apiCallDeleteFile(file, queue)));
|
||||
}
|
||||
}
|
||||
|
||||
private void apiCallDeleteFile(FolderFile file, Queue<FolderFile> queue)
|
||||
throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
try {
|
||||
mailboxApi.deleteFile(mailboxProperties, file.folderId,
|
||||
file.fileId);
|
||||
} catch (TolerableFailureException e) {
|
||||
// File not found - continue to the next file
|
||||
LOG.warning("File does not exist");
|
||||
}
|
||||
downloadNextFile(queue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTorReachable() {
|
||||
LOG.info("Our Tor hidden service is reachable");
|
||||
synchronized (lock) {
|
||||
if (state != State.WAITING_FOR_TOR) return;
|
||||
state = State.DOWNLOAD_CYCLE_2;
|
||||
// Start second download cycle
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(
|
||||
createApiCallForDownloadCycle());
|
||||
}
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
static class FolderFile {
|
||||
|
||||
final MailboxFolderId folderId;
|
||||
final MailboxFileId fileId;
|
||||
|
||||
FolderFile(MailboxFolderId folderId, MailboxFileId fileId) {
|
||||
this.folderId = folderId;
|
||||
this.fileId = fileId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,8 @@ class MailboxPairingTaskImpl implements MailboxPairingTask {
|
||||
db.transaction(false, txn -> {
|
||||
mailboxSettingsManager
|
||||
.setOwnMailboxProperties(txn, ownerProperties);
|
||||
mailboxSettingsManager.recordSuccessfulConnection(txn, time);
|
||||
mailboxSettingsManager.recordSuccessfulConnection(txn, time,
|
||||
ownerProperties.getServerSupports());
|
||||
// A (possibly new) mailbox is paired. Reset message retransmission
|
||||
// timers for contacts who doesn't have their own mailbox. This way,
|
||||
// data stranded on our old mailbox will be re-uploaded to our new.
|
||||
|
||||
@@ -80,7 +80,7 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
encodeServerSupports(serverSupports, s);
|
||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||
for (MailboxHook hook : hooks) {
|
||||
hook.mailboxPaired(txn, p.getOnion(), p.getServerSupports());
|
||||
hook.mailboxPaired(txn, p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
Settings s = new Settings();
|
||||
s.put(SETTINGS_KEY_ONION, "");
|
||||
s.put(SETTINGS_KEY_TOKEN, "");
|
||||
s.put(SETTINGS_KEY_ATTEMPTS, "");
|
||||
s.put(SETTINGS_KEY_LAST_ATTEMPT, "");
|
||||
s.put(SETTINGS_KEY_LAST_SUCCESS, "");
|
||||
s.put(SETTINGS_KEY_SERVER_SUPPORTS, "");
|
||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||
for (MailboxHook hook : hooks) {
|
||||
hook.mailboxUnpaired(txn);
|
||||
@@ -112,34 +116,18 @@ class MailboxSettingsManagerImpl implements MailboxSettingsManager {
|
||||
serverSupports);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordSuccessfulConnection(Transaction txn, long now)
|
||||
throws DbException {
|
||||
recordSuccessfulConnection(txn, now, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordSuccessfulConnection(Transaction txn, long now,
|
||||
@Nullable List<MailboxVersion> versions) throws DbException {
|
||||
List<MailboxVersion> versions) throws DbException {
|
||||
Settings s = new Settings();
|
||||
// fetch version that the server supports first
|
||||
List<MailboxVersion> serverSupports;
|
||||
if (versions == null) {
|
||||
Settings oldSettings =
|
||||
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||
serverSupports = parseServerSupports(oldSettings);
|
||||
} else {
|
||||
serverSupports = versions;
|
||||
// store new versions
|
||||
encodeServerSupports(serverSupports, s);
|
||||
}
|
||||
// now record the successful connection
|
||||
// record the successful connection
|
||||
s.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
s.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||
s.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
encodeServerSupports(versions, s);
|
||||
settingsManager.mergeSettings(txn, s, SETTINGS_NAMESPACE);
|
||||
// broadcast status event
|
||||
MailboxStatus status = new MailboxStatus(now, now, 0, serverSupports);
|
||||
MailboxStatus status = new MailboxStatus(now, now, 0, versions);
|
||||
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@ import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxUpdateSentEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
@@ -38,6 +41,7 @@ import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -111,6 +115,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
}
|
||||
Group g = getContactGroup(c);
|
||||
storeMessageReplaceLatest(txn, g.getId(), updated);
|
||||
txn.attach(new MailboxUpdateSentEvent(c.getId(), updated));
|
||||
}
|
||||
} else {
|
||||
db.addGroup(txn, localGroup);
|
||||
@@ -143,14 +148,16 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
clientHelper.setContactId(txn, g.getId(), c.getId());
|
||||
MailboxProperties ownProps =
|
||||
mailboxSettingsManager.getOwnMailboxProperties(txn);
|
||||
MailboxUpdate u;
|
||||
if (ownProps != null) {
|
||||
// We are paired, create and send props to the newly added contact
|
||||
createAndSendUpdateWithMailbox(txn, c, ownProps.getServerSupports(),
|
||||
ownProps.getOnion());
|
||||
u = createAndSendUpdateWithMailbox(txn, c,
|
||||
ownProps.getServerSupports(), ownProps.getOnion());
|
||||
} else {
|
||||
// Not paired, but we still want to get our clientSupports sent
|
||||
sendUpdateNoMailbox(txn, c);
|
||||
u = sendUpdateNoMailbox(txn, c);
|
||||
}
|
||||
txn.attach(new MailboxUpdateSentEvent(c.getId(), u));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,18 +166,25 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mailboxPaired(Transaction txn, String ownOnion,
|
||||
List<MailboxVersion> serverSupports) throws DbException {
|
||||
public void mailboxPaired(Transaction txn, MailboxProperties p)
|
||||
throws DbException {
|
||||
Map<ContactId, MailboxUpdateWithMailbox> localUpdates = new HashMap<>();
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
createAndSendUpdateWithMailbox(txn, c, serverSupports, ownOnion);
|
||||
MailboxUpdateWithMailbox u = createAndSendUpdateWithMailbox(txn, c,
|
||||
p.getServerSupports(), p.getOnion());
|
||||
localUpdates.put(c.getId(), u);
|
||||
}
|
||||
txn.attach(new MailboxPairedEvent(p, localUpdates));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mailboxUnpaired(Transaction txn) throws DbException {
|
||||
Map<ContactId, MailboxUpdate> localUpdates = new HashMap<>();
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
sendUpdateNoMailbox(txn, c);
|
||||
MailboxUpdate u = sendUpdateNoMailbox(txn, c);
|
||||
localUpdates.put(c.getId(), u);
|
||||
}
|
||||
txn.attach(new MailboxUnpairedEvent(localUpdates));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,18 +253,19 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
* supported Mailbox API version(s). All of which the contact needs to
|
||||
* communicate with our Mailbox.
|
||||
*/
|
||||
private void createAndSendUpdateWithMailbox(Transaction txn, Contact c,
|
||||
List<MailboxVersion> serverSupports, String ownOnion)
|
||||
throws DbException {
|
||||
private MailboxUpdateWithMailbox createAndSendUpdateWithMailbox(
|
||||
Transaction txn, Contact c, List<MailboxVersion> serverSupports,
|
||||
String ownOnion) throws DbException {
|
||||
MailboxProperties properties = new MailboxProperties(ownOnion,
|
||||
new MailboxAuthToken(crypto.generateUniqueId().getBytes()),
|
||||
serverSupports,
|
||||
new MailboxFolderId(crypto.generateUniqueId().getBytes()),
|
||||
new MailboxFolderId(crypto.generateUniqueId().getBytes()));
|
||||
MailboxUpdate u =
|
||||
MailboxUpdateWithMailbox u =
|
||||
new MailboxUpdateWithMailbox(clientSupports, properties);
|
||||
Group g = getContactGroup(c);
|
||||
storeMessageReplaceLatest(txn, g.getId(), u);
|
||||
return u;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,11 +274,12 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
* Mailbox that they can use. It still includes the list of Mailbox API
|
||||
* version(s) that we support as a client.
|
||||
*/
|
||||
private void sendUpdateNoMailbox(Transaction txn, Contact c)
|
||||
private MailboxUpdate sendUpdateNoMailbox(Transaction txn, Contact c)
|
||||
throws DbException {
|
||||
Group g = getContactGroup(c);
|
||||
MailboxUpdate u = new MailboxUpdate(clientSupports);
|
||||
storeMessageReplaceLatest(txn, g.getId(), u);
|
||||
return u;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -328,13 +328,13 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
LOG.info("Uploading file");
|
||||
mailboxApi.addFile(mailboxProperties, folderId, file);
|
||||
markMessagesSentOrAcked(sessionRecord);
|
||||
delete(file);
|
||||
synchronized (lock) {
|
||||
if (state != State.WRITING_UPLOADING) return;
|
||||
state = State.CHECKING_FOR_DATA;
|
||||
apiCall = null;
|
||||
this.file = null;
|
||||
}
|
||||
delete(file);
|
||||
checkForDataToSend();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,4 +24,8 @@ interface MailboxWorkerFactory {
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
MailboxProperties properties);
|
||||
|
||||
MailboxWorker createContactListWorkerForOwnMailbox(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
MailboxProperties properties);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
@@ -27,6 +28,7 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
private final MailboxApiCaller mailboxApiCaller;
|
||||
private final MailboxApi mailboxApi;
|
||||
private final MailboxFileManager mailboxFileManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
|
||||
@Inject
|
||||
MailboxWorkerFactoryImpl(@IoExecutor Executor ioExecutor,
|
||||
@@ -36,7 +38,8 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
EventBus eventBus,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
MailboxFileManager mailboxFileManager) {
|
||||
MailboxFileManager mailboxFileManager,
|
||||
MailboxUpdateManager mailboxUpdateManager) {
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.db = db;
|
||||
this.clock = clock;
|
||||
@@ -45,6 +48,7 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
this.mailboxApiCaller = mailboxApiCaller;
|
||||
this.mailboxApi = mailboxApi;
|
||||
this.mailboxFileManager = mailboxFileManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,7 +79,19 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
MailboxProperties properties) {
|
||||
// TODO
|
||||
throw new UnsupportedOperationException();
|
||||
return new OwnMailboxDownloadWorker(connectivityChecker,
|
||||
reachabilityMonitor, mailboxApiCaller, mailboxApi,
|
||||
mailboxFileManager, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MailboxWorker createContactListWorkerForOwnMailbox(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
MailboxProperties properties) {
|
||||
OwnMailboxContactListWorker worker = new OwnMailboxContactListWorker(
|
||||
ioExecutor, db, eventBus, connectivityChecker, mailboxApiCaller,
|
||||
mailboxApi, mailboxUpdateManager, properties);
|
||||
eventBus.addListener(worker);
|
||||
return worker;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class OwnMailboxClient implements MailboxClient {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(OwnMailboxClient.class.getName());
|
||||
|
||||
private final MailboxWorkerFactory workerFactory;
|
||||
private final ConnectivityChecker connectivityChecker;
|
||||
private final TorReachabilityMonitor reachabilityMonitor;
|
||||
private final MailboxWorker contactListWorker;
|
||||
private final Object lock = new Object();
|
||||
|
||||
/**
|
||||
* Upload workers: one worker per contact assigned for upload.
|
||||
*/
|
||||
@GuardedBy("lock")
|
||||
private final Map<ContactId, MailboxWorker> uploadWorkers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Download worker: shared between all contacts assigned for download.
|
||||
* Null if no contacts are assigned for download.
|
||||
*/
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private MailboxWorker downloadWorker = null;
|
||||
|
||||
/**
|
||||
* IDs of contacts assigned for download, so that we know when to
|
||||
* create/destroy the download worker.
|
||||
*/
|
||||
@GuardedBy("lock")
|
||||
private final Set<ContactId> assignedForDownload = new HashSet<>();
|
||||
|
||||
OwnMailboxClient(MailboxWorkerFactory workerFactory,
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
MailboxProperties properties) {
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
this.workerFactory = workerFactory;
|
||||
this.connectivityChecker = connectivityChecker;
|
||||
this.reachabilityMonitor = reachabilityMonitor;
|
||||
contactListWorker = workerFactory.createContactListWorkerForOwnMailbox(
|
||||
connectivityChecker, properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
LOG.info("Started");
|
||||
contactListWorker.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
LOG.info("Destroyed");
|
||||
List<MailboxWorker> uploadWorkers;
|
||||
MailboxWorker downloadWorker;
|
||||
synchronized (lock) {
|
||||
uploadWorkers = new ArrayList<>(this.uploadWorkers.values());
|
||||
this.uploadWorkers.clear();
|
||||
downloadWorker = this.downloadWorker;
|
||||
this.downloadWorker = null;
|
||||
}
|
||||
// Destroy the workers (with apologies to Mr Marx and Mr Engels)
|
||||
for (MailboxWorker worker : uploadWorkers) worker.destroy();
|
||||
if (downloadWorker != null) downloadWorker.destroy();
|
||||
contactListWorker.destroy();
|
||||
// The connectivity checker belongs to the client, so it should be
|
||||
// destroyed. The Tor reachability monitor is shared between clients,
|
||||
// so it should not be destroyed
|
||||
connectivityChecker.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignContactForUpload(ContactId contactId,
|
||||
MailboxProperties properties, MailboxFolderId folderId) {
|
||||
LOG.info("Contact assigned for upload");
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
MailboxWorker uploadWorker = workerFactory.createUploadWorker(
|
||||
connectivityChecker, properties, folderId, contactId);
|
||||
synchronized (lock) {
|
||||
MailboxWorker old = uploadWorkers.put(contactId, uploadWorker);
|
||||
if (old != null) throw new IllegalStateException();
|
||||
}
|
||||
uploadWorker.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deassignContactForUpload(ContactId contactId) {
|
||||
LOG.info("Contact deassigned for upload");
|
||||
MailboxWorker uploadWorker;
|
||||
synchronized (lock) {
|
||||
uploadWorker = uploadWorkers.remove(contactId);
|
||||
}
|
||||
if (uploadWorker != null) uploadWorker.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void assignContactForDownload(ContactId contactId,
|
||||
MailboxProperties properties, MailboxFolderId folderId) {
|
||||
LOG.info("Contact assigned for download");
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
// Create a download worker if we don't already have one. The worker
|
||||
// will use the API to discover which folders have files to download,
|
||||
// so it doesn't need to track the set of assigned contacts
|
||||
MailboxWorker toStart = null;
|
||||
synchronized (lock) {
|
||||
if (!assignedForDownload.add(contactId)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (downloadWorker == null) {
|
||||
toStart = workerFactory.createDownloadWorkerForOwnMailbox(
|
||||
connectivityChecker, reachabilityMonitor, properties);
|
||||
downloadWorker = toStart;
|
||||
}
|
||||
}
|
||||
if (toStart != null) toStart.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deassignContactForDownload(ContactId contactId) {
|
||||
LOG.info("Contact deassigned for download");
|
||||
// If there are no more contacts assigned for download, destroy the
|
||||
// download worker
|
||||
MailboxWorker toDestroy = null;
|
||||
synchronized (lock) {
|
||||
if (!assignedForDownload.remove(contactId)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (assignedForDownload.isEmpty()) {
|
||||
toDestroy = downloadWorker;
|
||||
downloadWorker = null;
|
||||
}
|
||||
}
|
||||
if (toDestroy != null) toDestroy.destroy();
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
@@ -30,6 +31,7 @@ class OwnMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
||||
private final TransactionManager db;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
|
||||
@Inject
|
||||
OwnMailboxConnectivityChecker(Clock clock,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
@@ -57,6 +59,7 @@ class OwnMailboxConnectivityChecker extends ConnectivityCheckerImpl {
|
||||
private boolean checkConnectivityAndStoreResult(
|
||||
MailboxProperties properties) throws DbException {
|
||||
try {
|
||||
LOG.info("Checking whether own mailbox is reachable");
|
||||
List<MailboxVersion> serverSupports =
|
||||
mailboxApi.getServerSupports(properties);
|
||||
LOG.info("Own mailbox is reachable");
|
||||
|
||||
@@ -303,7 +303,7 @@ class OwnMailboxContactListWorker
|
||||
mailboxApi.deleteContact(mailboxProperties, c);
|
||||
} catch (TolerableFailureException e) {
|
||||
// Catch this so we can continue to the next update
|
||||
logException(LOG, INFO, e);
|
||||
LOG.warning("Contact does not exist");
|
||||
}
|
||||
updateContactList();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.ApiException;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.Collections.shuffle;
|
||||
import static java.util.logging.Level.INFO;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class OwnMailboxDownloadWorker extends MailboxDownloadWorker {
|
||||
|
||||
/**
|
||||
* The maximum number of files that will be downloaded before checking
|
||||
* again for folders with available files. This ensures that if a file
|
||||
* arrives during a download cycle, its folder will be checked within a
|
||||
* reasonable amount of time even if some other folder has a very large
|
||||
* number of files.
|
||||
* <p>
|
||||
* Package access for testing.
|
||||
*/
|
||||
static final int MAX_ROUND_ROBIN_FILES = 1000;
|
||||
|
||||
OwnMailboxDownloadWorker(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor torReachabilityMonitor,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
MailboxFileManager mailboxFileManager,
|
||||
MailboxProperties mailboxProperties) {
|
||||
super(connectivityChecker, torReachabilityMonitor, mailboxApiCaller,
|
||||
mailboxApi, mailboxFileManager, mailboxProperties);
|
||||
if (!mailboxProperties.isOwner()) throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ApiCall createApiCallForDownloadCycle() {
|
||||
return new SimpleApiCall(this::apiCallListFolders);
|
||||
}
|
||||
|
||||
private void apiCallListFolders() throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
LOG.info("Listing folders with available files");
|
||||
List<MailboxFolderId> folders =
|
||||
mailboxApi.getFolders(mailboxProperties);
|
||||
if (folders.isEmpty()) onDownloadCycleFinished();
|
||||
else listNextFolder(new LinkedList<>(folders), new HashMap<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the next folder from `queue` and starts a task to list the
|
||||
* files in the folder and add them to `available`.
|
||||
*/
|
||||
private void listNextFolder(Queue<MailboxFolderId> queue,
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
MailboxFolderId folder = queue.remove();
|
||||
apiCall = mailboxApiCaller.retryWithBackoff(new SimpleApiCall(() ->
|
||||
apiCallListFolder(folder, queue, available)));
|
||||
}
|
||||
}
|
||||
|
||||
private void apiCallListFolder(MailboxFolderId folder,
|
||||
Queue<MailboxFolderId> queue,
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available)
|
||||
throws IOException, ApiException {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
LOG.info("Listing folder");
|
||||
try {
|
||||
List<MailboxFile> files =
|
||||
mailboxApi.getFiles(mailboxProperties, folder);
|
||||
if (!files.isEmpty()) {
|
||||
available.put(folder, new LinkedList<>(files));
|
||||
}
|
||||
} catch (TolerableFailureException e) {
|
||||
LOG.warning("Folder does not exist");
|
||||
}
|
||||
if (queue.isEmpty()) {
|
||||
LOG.info("Finished listing folders");
|
||||
if (available.isEmpty()) onDownloadCycleFinished();
|
||||
else createDownloadQueue(available);
|
||||
} else {
|
||||
listNextFolder(queue, available);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Visits the given folders in round-robin order to create a queue of up to
|
||||
* {@link #MAX_ROUND_ROBIN_FILES} to download.
|
||||
*/
|
||||
private void createDownloadQueue(
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available) {
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
}
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(available.size() + " folders have available files");
|
||||
}
|
||||
Queue<FolderFile> queue = createRoundRobinQueue(available);
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Downloading " + queue.size() + " files");
|
||||
}
|
||||
downloadNextFile(queue);
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
Queue<FolderFile> createRoundRobinQueue(
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available) {
|
||||
List<MailboxFolderId> roundRobin = new ArrayList<>(available.keySet());
|
||||
// Shuffle the folders so we don't always favour the same folders
|
||||
shuffle(roundRobin);
|
||||
Queue<FolderFile> queue = new LinkedList<>();
|
||||
while (queue.size() < MAX_ROUND_ROBIN_FILES && !available.isEmpty()) {
|
||||
Iterator<MailboxFolderId> it = roundRobin.iterator();
|
||||
while (queue.size() < MAX_ROUND_ROBIN_FILES && it.hasNext()) {
|
||||
MailboxFolderId folder = it.next();
|
||||
Queue<MailboxFile> files = available.get(folder);
|
||||
MailboxFile file = files.remove();
|
||||
queue.add(new FolderFile(folder, file.name));
|
||||
if (files.isEmpty()) {
|
||||
available.remove(folder);
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
}
|
||||
@@ -800,6 +800,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void controlConnectionClosed() {
|
||||
if (state.isTorRunning()) {
|
||||
throw new RuntimeException("Control connection closed");
|
||||
}
|
||||
}
|
||||
|
||||
private String removeSeverity(String msg) {
|
||||
return msg.replaceFirst("[^ ]+ ", "");
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
@@ -61,26 +63,32 @@ class MailboxOutgoingSession extends SimplexOutgoingSession {
|
||||
|
||||
@Override
|
||||
void sendAcks() throws DbException, IOException {
|
||||
while (!isInterrupted()) {
|
||||
Collection<MessageId> idsToAck = loadMessageIdsToAck();
|
||||
if (idsToAck.isEmpty()) break;
|
||||
recordWriter.writeAck(new Ack(idsToAck));
|
||||
sessionRecord.onAckSent(idsToAck);
|
||||
List<MessageId> idsToAck = loadMessageIdsToAck();
|
||||
int idsSent = 0;
|
||||
while (idsSent < idsToAck.size() && !isInterrupted()) {
|
||||
int idsRemaining = idsToAck.size() - idsSent;
|
||||
long capacity = getRemainingCapacity();
|
||||
long idCapacity =
|
||||
(capacity - RECORD_HEADER_BYTES) / MessageId.LENGTH;
|
||||
if (idCapacity == 0) break; // Out of capacity
|
||||
int idsInRecord = (int) min(idCapacity, MAX_MESSAGE_IDS);
|
||||
int idsToSend = min(idsRemaining, idsInRecord);
|
||||
List<MessageId> acked =
|
||||
idsToAck.subList(idsSent, idsSent + idsToSend);
|
||||
recordWriter.writeAck(new Ack(acked));
|
||||
sessionRecord.onAckSent(acked);
|
||||
LOG.info("Sent ack");
|
||||
idsSent += idsToSend;
|
||||
}
|
||||
}
|
||||
|
||||
private Collection<MessageId> loadMessageIdsToAck() throws DbException {
|
||||
long idCapacity = (getRemainingCapacity() - RECORD_HEADER_BYTES)
|
||||
/ MessageId.LENGTH;
|
||||
if (idCapacity <= 0) return emptyList(); // Out of capacity
|
||||
int maxMessageIds = (int) min(idCapacity, MAX_MESSAGE_IDS);
|
||||
private List<MessageId> loadMessageIdsToAck() throws DbException {
|
||||
Collection<MessageId> ids = db.transactionWithResult(true, txn ->
|
||||
db.getMessagesToAck(txn, contactId, maxMessageIds));
|
||||
db.getMessagesToAck(txn, contactId));
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info(ids.size() + " messages to ack");
|
||||
}
|
||||
return ids;
|
||||
return new ArrayList<>(ids);
|
||||
}
|
||||
|
||||
private long getRemainingCapacity() {
|
||||
|
||||
@@ -13,21 +13,24 @@ d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=T
|
||||
d Bridge obfs4 51.222.13.177:80 5EDAC3B810E12B01F6FD8050D2FD3E277B289A08 cert=2uplIpLQ0q9+0qMFrK5pkaYRDOe460LL9WHBvatgkuRr/SL31wBOEupaMMJ6koRE6Ld0ew iat-mode=0
|
||||
n Bridge obfs4 46.226.107.197:10300 A38FD6BDFD902882F5F5B9B7CCC95602A20B0BC4 cert=t8tA9q2AeGlmp/dO6oW9bkY5RqqmvqjArCEM9wjJoDnk6XtnaejkF0JTA7VamdyOzcvuBg iat-mode=0
|
||||
n Bridge obfs4 185.181.11.86:443 A961609729E7FDF520B4E81F1F1B8FA1045285C3 cert=e5faG9Zk4Ni+e7z2YgGfevyKPQlMvkVGi4ublSsHYjaBovKeNXpOhbeFxzbZZoAzxAoGUQ iat-mode=0
|
||||
n Bridge obfs4 85.242.211.221:8042 A36A938DD7FDB8BACC846BA326EE0BA0D89A9252 cert=1AN6Pt1eFca3Y/WYD2TGAU3Al9cO4eouXE9SX63s66Z/ks3tVmgQ5GeXi1B5DOvx6Il7Zw iat-mode=0
|
||||
n Bridge obfs4 74.104.165.202:9002 EF432018A6AA5D970B2F84E39CD30A147030141C cert=PhppfUusY85dHGvWtGTybZ1fED4DtbHmALkNMIOIYrAz1B4xN7/2a5gyiZe1epju1BOHVg iat-mode=0
|
||||
n Bridge obfs4 93.95.226.151:41185 460B0CFFC0CF1D965F3DE064E08BA1915E7C916A cert=inluPzp5Jp5OzZar1eQb4dcQ/YlAj/v0kHAUCoCr3rmLt03+pVuVTjoH4mRy4+acXpn+Gw iat-mode=0
|
||||
n Bridge obfs4 120.29.217.52:5223 40FE3DB9800272F9CDC76422F8ED7883280EE96D cert=/71PS4l8c/XJ4DIItlH9xMqNvPFg2RUTrHvPlQWh48u5et8h/yyyjCcYphUadDsfBWpaGQ iat-mode=0
|
||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg iat-mode=0
|
||||
n Bridge obfs4 65.108.159.114:14174 E1AD374BA9F34BD98862D128AC54D40C7DC138AE cert=YMkxMSBN2OOd99AkJpFaEUyKkdqZgJt9oVJEgO1QnT37n/Vc2yR4nhx4k4VkPLfEP1f4eg iat-mode=0
|
||||
n Bridge obfs4 185.177.207.138:8443 53716FE26F23C8C6A71A2BC5D9D8DC64747278C7 cert=6jcYVilMEzxdsWghSrQFpYYJlkkir/GPIXw/EnddUK3S8ToVpMG8u1SwMOEdEs735RrMHw iat-mode=0
|
||||
n Bridge obfs4 176.123.2.253:1933 B855D141CE6C4DE0F7EA4AAED83EBA8373FA8191 cert=1rOoSaRagc6PPR//paIl+ukv1N+xWKCdBXMFxK0k/moEwH0lk5bURBrUDzIX35fVzaiicQ iat-mode=0
|
||||
n Bridge obfs4 5.252.176.61:9418 3E61130100AD827AB9CB33DAC052D9BC49A39509 cert=/aMyBPnKbQFISithD3i1KHUdpWeMpWG3SvUpq1YWCf2EQohFxQfw+646gW1knm4BI/DLRA iat-mode=0
|
||||
n Bridge obfs4 202.61.224.111:6902 A4F91299763DB925AE3BD29A0FC1A9821E5D9BAE cert=NBKm2MJ83wMvYShkqpD5RrbDtW5YpIZrFNnMw7Dj1XOM3plU60Bh4eziaQXe8fGtb8ZqKg iat-mode=0
|
||||
n Bridge obfs4 87.121.72.109:9002 C8081D4731C953FA4AE166946E72B29153351E34 cert=bikAqxKV6Ch5gFCBTdPI28VeShYa1ZgkLmvc7YZNLWFsFZoaCULL/3AQKjpIfvSiJs5jGQ iat-mode=0
|
||||
n Bridge obfs4 172.104.17.96:17900 B6B37AC96E163D0A5ECE55826D17B50B70F0A7F8 cert=gUz7svhPxoALgeN4lMYrXK7NBnaDqwu6SKRJOhaO9IIMBpnB8UhMCMKzzMho3b0RxWzBVg iat-mode=0
|
||||
n Bridge obfs4 70.34.249.113:443 F441B16ABB1055794C2CE01821FC05047B2C8CFC cert=MauLNoyq8EwjY4Qe0oASYzs2hXdSjNgy+BtP9oo1naHhRsyKTtAZzeNv08RnzWjMJrTwcg iat-mode=0
|
||||
v Bridge 135.181.113.164:54444 74AF4CCA614C454B7D3E81FF8BACD78CEBC7D7DE
|
||||
n Bridge obfs4 85.212.104.44:27623 9000BDF7063F808E554C320545BCF264222CDFE9 cert=eKSXeJLnFv6RDAap5vM2sqsi8GIWhlMi5prs8hu0HTKWTH9KjZhhoj3Cg/gIuoL8n8TXGQ iat-mode=0
|
||||
n Bridge obfs4 104.168.68.90:443 ED55B3C321E44EA7E50EF568C8A63CF75E89A58C cert=fgonxDvltTp8nmcOE9sUG94eOAALxETVVXAwnTZJLPpf7rjPuTp+abKl4VyFkxfcLRr5KQ iat-mode=0
|
||||
n Bridge obfs4 158.247.207.151:443 6170ADBBB6C1859A8E7E4416BB8AB3AF471AE47F cert=Od4izlwLnXcq7LMSOJtnZLtklaUn+X+gPcBwN7RUEkk9rqxRRYNHW7as8g6+jheDsazxAQ iat-mode=0
|
||||
n Bridge obfs4 45.142.181.131:42069 6EBCF6B02DA2B982F4080A7119D737366AFB74FA cert=9HyWH/BCwWzNirZdTQtluCgJk+gFhpOqydIuyQ1iDvpnqsAynKF+zcPE/INZFefm86UlBg iat-mode=0
|
||||
n Bridge obfs4 85.214.28.204:47111 78A36E46BB082A471848239D3F4390A8F8C6084D cert=96sr3eaUFBDu4wFVAQIfNFElh0UNuutJ/3/Fh2Vu2PHfacQ8bwfY02bwG351U8BZpLnfUQ iat-mode=0
|
||||
n Bridge obfs4 152.67.77.101:4096 B82DB9CDDF887AB8A859420E07DF298E30AF8A6E cert=21OWn3yFo+hulmQNAOtF5uwwOqWtdT5PrLhk8BG9DpOd0/k5DEkQEYPyDdXbS9nZ0E5BJA iat-mode=0
|
||||
v Bridge 92.243.15.235:9001 477EAD3C04036B48235F1F27FC91420A286A4B7F
|
||||
v Bridge 77.96.91.103:443 ED000A75B79A58F1D83A4D1675C2A9395B71BE8E
|
||||
v Bridge 213.108.108.145:17674 A39C0FE47963B6E8CFE9815549864DE544935A31
|
||||
v Bridge 185.189.195.124:8199 A1F3EE78F9C2343668E68FEB84358A4C742831A5
|
||||
v Bridge 135.23.182.26:11393 6E9A100D1E0570F0012735D64EEB4CB62AC258D9
|
||||
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||
|
||||
@@ -389,7 +389,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessagesToAck(transaction, contactId, 123));
|
||||
db.getMessagesToAck(transaction, contactId));
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
@@ -1396,14 +1396,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
// First message is still visible to the contact - flag lowered
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId);
|
||||
will(returnValue(true));
|
||||
// Second message is no longer visible - flag not lowered
|
||||
oneOf(database).containsVisibleMessage(txn, contactId, messageId1);
|
||||
will(returnValue(false));
|
||||
oneOf(database)
|
||||
.lowerAckFlag(txn, contactId, singletonList(messageId));
|
||||
oneOf(database).lowerAckFlag(txn, contactId, acked);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
|
||||
@@ -40,6 +40,8 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testStartAndDestroyWithNoContactsAssigned() {
|
||||
client.start();
|
||||
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@@ -54,6 +56,7 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(uploadWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@@ -71,11 +74,12 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
client.deassignContactForUpload(contactId);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignContactForDownloadAndDestroyClient() {
|
||||
public void testAssignContactForDownloadAndDestroyClient() {
|
||||
client.start();
|
||||
|
||||
// When the contact is assigned, the worker should be created and
|
||||
@@ -85,11 +89,12 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(downloadWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void assignAndDeassignContactForDownload() {
|
||||
public void testAssignAndDeassignContactForDownload() {
|
||||
client.start();
|
||||
|
||||
// When the contact is assigned, the worker should be created and
|
||||
@@ -102,6 +107,7 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
client.deassignContactForDownload(contactId);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@@ -128,4 +134,10 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
oneOf(worker).destroy();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyConnectivityChecker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).destroy();
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,88 +1,68 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.action.DoAllAction;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class ContactMailboxDownloadWorkerTest extends BrambleMockTestCase {
|
||||
public class ContactMailboxDownloadWorkerTest
|
||||
extends MailboxDownloadWorkerTest<ContactMailboxDownloadWorker> {
|
||||
|
||||
private final ConnectivityChecker connectivityChecker =
|
||||
context.mock(ConnectivityChecker.class);
|
||||
private final TorReachabilityMonitor torReachabilityMonitor =
|
||||
context.mock(TorReachabilityMonitor.class);
|
||||
private final MailboxApiCaller mailboxApiCaller =
|
||||
context.mock(MailboxApiCaller.class);
|
||||
private final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||
private final MailboxFileManager mailboxFileManager =
|
||||
context.mock(MailboxFileManager.class);
|
||||
private final Cancellable apiCall = context.mock(Cancellable.class);
|
||||
|
||||
private final MailboxProperties mailboxProperties =
|
||||
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
private final long now = System.currentTimeMillis();
|
||||
private final MailboxFile file1 =
|
||||
new MailboxFile(new MailboxFileId(getRandomId()), now - 1);
|
||||
private final MailboxFile file2 =
|
||||
new MailboxFile(new MailboxFileId(getRandomId()), now);
|
||||
private final List<MailboxFile> files = asList(file1, file2);
|
||||
|
||||
private File testDir, tempFile;
|
||||
private ContactMailboxDownloadWorker worker;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir = getTestDirectory();
|
||||
tempFile = new File(testDir, "temp");
|
||||
public ContactMailboxDownloadWorkerTest() {
|
||||
mailboxProperties = getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
worker = new ContactMailboxDownloadWorker(connectivityChecker,
|
||||
torReachabilityMonitor, mailboxApiCaller, mailboxApi,
|
||||
mailboxFileManager, mailboxProperties);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksConnectivityWhenStartedAndRemovesObserverWhenDestroyed() {
|
||||
// When the worker is started it should start a connectivity check
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).checkConnectivity(
|
||||
with(mailboxProperties), with(worker));
|
||||
}});
|
||||
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).removeObserver(worker);
|
||||
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||
}});
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksForFilesWhenConnectivityCheckSucceeds()
|
||||
throws Exception {
|
||||
// When the worker is started it should start a connectivity check
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the connectivity check succeeds, a list-inbox task should be
|
||||
// started for the first download cycle
|
||||
AtomicReference<ApiCall> listTask = new AtomicReference<>();
|
||||
expectStartTask(listTask);
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the list-inbox tasks runs and finds no files to download,
|
||||
// it should add a Tor reachability observer
|
||||
expectCheckForFiles(mailboxProperties.getInboxId(), emptyList());
|
||||
expectAddReachabilityObserver();
|
||||
assertFalse(listTask.get().callApi());
|
||||
|
||||
// When the reachability observer is called, a list-inbox task should
|
||||
// be started for the second download cycle
|
||||
expectStartTask(listTask);
|
||||
worker.onTorReachable();
|
||||
|
||||
// When the list-inbox tasks runs and finds no files to download,
|
||||
// it should finish the second download cycle
|
||||
expectCheckForFiles(mailboxProperties.getInboxId(), emptyList());
|
||||
assertFalse(listTask.get().callApi());
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@@ -90,150 +70,67 @@ public class ContactMailboxDownloadWorkerTest extends BrambleMockTestCase {
|
||||
public void testDownloadsFilesWhenConnectivityCheckSucceeds()
|
||||
throws Exception {
|
||||
// When the worker is started it should start a connectivity check
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).checkConnectivity(
|
||||
with(mailboxProperties), with(worker));
|
||||
}});
|
||||
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the connectivity check succeeds, a list-inbox task should be
|
||||
// started for the first download cycle
|
||||
AtomicReference<ApiCall> listTask = new AtomicReference<>();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(listTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectStartTask(listTask);
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the list-inbox tasks runs and finds some files to download,
|
||||
// it should start a download task for the first file
|
||||
AtomicReference<ApiCall> downloadTask = new AtomicReference<>();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()));
|
||||
will(returnValue(files));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectCheckForFiles(mailboxProperties.getInboxId(), files);
|
||||
expectStartTask(downloadTask);
|
||||
assertFalse(listTask.get().callApi());
|
||||
|
||||
// When the first download task runs it should download the file to the
|
||||
// location provided by the file manager and start a delete task
|
||||
AtomicReference<ApiCall> deleteTask = new AtomicReference<>();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||
will(returnValue(tempFile));
|
||||
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()),
|
||||
file1.name, tempFile);
|
||||
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectDownloadFile(mailboxProperties.getInboxId(), file1);
|
||||
expectStartTask(deleteTask);
|
||||
assertFalse(downloadTask.get().callApi());
|
||||
|
||||
// When the first delete task runs it should delete the file, ignore
|
||||
// the tolerable failure, and start a download task for the next file
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()), file1.name);
|
||||
will(throwException(new TolerableFailureException()));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(downloadTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectDeleteFile(mailboxProperties.getInboxId(), file1, true);
|
||||
expectStartTask(downloadTask);
|
||||
assertFalse(deleteTask.get().callApi());
|
||||
|
||||
// When the second download task runs it should download the file to
|
||||
// the location provided by the file manager and start a delete task
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||
will(returnValue(tempFile));
|
||||
oneOf(mailboxApi).getFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()),
|
||||
file2.name, tempFile);
|
||||
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(deleteTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectDownloadFile(mailboxProperties.getInboxId(), file2);
|
||||
expectStartTask(deleteTask);
|
||||
assertFalse(downloadTask.get().callApi());
|
||||
|
||||
// When the second delete task runs it should delete the file and
|
||||
// start a list-inbox task to check for files that may have arrived
|
||||
// since the first download cycle started
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).deleteFile(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()), file2.name);
|
||||
will(throwException(new TolerableFailureException()));
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(listTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectDeleteFile(mailboxProperties.getInboxId(), file2, false);
|
||||
expectStartTask(listTask);
|
||||
assertFalse(deleteTask.get().callApi());
|
||||
|
||||
// When the list-inbox tasks runs and finds no more files to download,
|
||||
// it should add a Tor reachability observer
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()));
|
||||
will(returnValue(emptyList()));
|
||||
oneOf(torReachabilityMonitor).addOneShotObserver(worker);
|
||||
}});
|
||||
|
||||
expectCheckForFiles(mailboxProperties.getInboxId(), emptyList());
|
||||
expectAddReachabilityObserver();
|
||||
assertFalse(listTask.get().callApi());
|
||||
|
||||
// When the reachability observer is called, a list-inbox task should
|
||||
// be started for the second download cycle
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(listTask, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
|
||||
expectStartTask(listTask);
|
||||
worker.onTorReachable();
|
||||
|
||||
// When the list-inbox tasks runs and finds no more files to download,
|
||||
// it should finish the second download cycle
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(mailboxProperties,
|
||||
requireNonNull(mailboxProperties.getInboxId()));
|
||||
will(returnValue(emptyList()));
|
||||
}});
|
||||
|
||||
expectCheckForFiles(mailboxProperties.getInboxId(), emptyList());
|
||||
assertFalse(listTask.get().callApi());
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).removeObserver(worker);
|
||||
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||
}});
|
||||
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -630,6 +630,7 @@ public class MailboxApiTest extends BrambleTestCase {
|
||||
server.enqueue(new MockResponse().setBody(invalidResponse3));
|
||||
server.enqueue(new MockResponse().setBody(invalidResponse4));
|
||||
server.enqueue(new MockResponse().setResponseCode(401));
|
||||
server.enqueue(new MockResponse().setResponseCode(404));
|
||||
server.enqueue(new MockResponse().setResponseCode(500));
|
||||
server.start();
|
||||
String baseUrl = getBaseUrl(server);
|
||||
@@ -706,13 +707,21 @@ public class MailboxApiTest extends BrambleTestCase {
|
||||
assertEquals("GET", request8.getMethod());
|
||||
assertToken(request8, token);
|
||||
|
||||
// 500 internal server error
|
||||
assertThrows(ApiException.class,
|
||||
() -> api.getFiles(properties, contactInboxId));
|
||||
// 404 not found
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFiles(properties, contactInboxId));
|
||||
RecordedRequest request9 = server.takeRequest();
|
||||
assertEquals("/files/" + contactInboxId, request9.getPath());
|
||||
assertEquals("GET", request9.getMethod());
|
||||
assertToken(request9, token);
|
||||
|
||||
// 500 internal server error
|
||||
assertThrows(ApiException.class,
|
||||
() -> api.getFiles(properties, contactInboxId));
|
||||
RecordedRequest request10 = server.takeRequest();
|
||||
assertEquals("/files/" + contactInboxId, request10.getPath());
|
||||
assertEquals("GET", request10.getMethod());
|
||||
assertToken(request10, token);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.action.DoAllAction;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
|
||||
abstract class MailboxDownloadWorkerTest<W extends MailboxDownloadWorker>
|
||||
extends BrambleMockTestCase {
|
||||
|
||||
final ConnectivityChecker connectivityChecker =
|
||||
context.mock(ConnectivityChecker.class);
|
||||
final TorReachabilityMonitor torReachabilityMonitor =
|
||||
context.mock(TorReachabilityMonitor.class);
|
||||
final MailboxApiCaller mailboxApiCaller =
|
||||
context.mock(MailboxApiCaller.class);
|
||||
final MailboxApi mailboxApi = context.mock(MailboxApi.class);
|
||||
final MailboxFileManager mailboxFileManager =
|
||||
context.mock(MailboxFileManager.class);
|
||||
private final Cancellable apiCall = context.mock(Cancellable.class);
|
||||
|
||||
private final long now = System.currentTimeMillis();
|
||||
final MailboxFile file1 =
|
||||
new MailboxFile(new MailboxFileId(getRandomId()), now - 1);
|
||||
final MailboxFile file2 =
|
||||
new MailboxFile(new MailboxFileId(getRandomId()), now);
|
||||
final List<MailboxFile> files = asList(file1, file2);
|
||||
|
||||
private File testDir, tempFile;
|
||||
MailboxProperties mailboxProperties;
|
||||
W worker;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testDir = getTestDirectory();
|
||||
tempFile = new File(testDir, "temp");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
|
||||
void expectStartConnectivityCheck() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).checkConnectivity(
|
||||
with(mailboxProperties), with(worker));
|
||||
}});
|
||||
}
|
||||
|
||||
void expectStartTask(AtomicReference<ApiCall> task) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApiCaller).retryWithBackoff(with(any(ApiCall.class)));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(task, ApiCall.class, 0),
|
||||
returnValue(apiCall)
|
||||
));
|
||||
}});
|
||||
}
|
||||
|
||||
void expectCheckForFoldersWithAvailableFiles(
|
||||
List<MailboxFolderId> folderIds) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFolders(mailboxProperties);
|
||||
will(returnValue(folderIds));
|
||||
}});
|
||||
}
|
||||
|
||||
void expectCheckForFiles(MailboxFolderId folderId,
|
||||
List<MailboxFile> files) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).getFiles(mailboxProperties, folderId);
|
||||
will(returnValue(files));
|
||||
}});
|
||||
}
|
||||
|
||||
void expectDownloadFile(MailboxFolderId folderId,
|
||||
MailboxFile file)
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxFileManager).createTempFileForDownload();
|
||||
will(returnValue(tempFile));
|
||||
oneOf(mailboxApi).getFile(mailboxProperties, folderId, file.name,
|
||||
tempFile);
|
||||
oneOf(mailboxFileManager).handleDownloadedFile(tempFile);
|
||||
}});
|
||||
}
|
||||
|
||||
void expectDeleteFile(MailboxFolderId folderId, MailboxFile file,
|
||||
boolean tolerableFailure) throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxApi).deleteFile(mailboxProperties, folderId,
|
||||
file.name);
|
||||
if (tolerableFailure) {
|
||||
will(throwException(new TolerableFailureException()));
|
||||
}
|
||||
}});
|
||||
}
|
||||
|
||||
void expectAddReachabilityObserver() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(torReachabilityMonitor).addOneShotObserver(worker);
|
||||
}});
|
||||
}
|
||||
|
||||
void expectRemoveObservers() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).removeObserver(worker);
|
||||
oneOf(torReachabilityMonitor).removeObserver(worker);
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -186,7 +186,7 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
MailboxFileId fileName1 = files1.get(0).name;
|
||||
|
||||
// owner can't check files
|
||||
assertThrows(ApiException.class, () ->
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFiles(ownerProperties, contact.inboxId));
|
||||
|
||||
// contact downloads file
|
||||
@@ -197,12 +197,13 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
|
||||
// owner can't download file, even if knowing name
|
||||
File file1forbidden = folder.newFile();
|
||||
assertThrows(ApiException.class, () -> api.getFile(ownerProperties,
|
||||
contact.inboxId, fileName1, file1forbidden));
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFile(ownerProperties, contact.inboxId, fileName1,
|
||||
file1forbidden));
|
||||
assertEquals(0, file1forbidden.length());
|
||||
|
||||
// owner can't delete file
|
||||
assertThrows(ApiException.class, () ->
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.deleteFile(ownerProperties, contact.inboxId, fileName1));
|
||||
|
||||
// contact deletes file
|
||||
@@ -232,7 +233,7 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
MailboxFileId file3name = files2.get(1).name;
|
||||
|
||||
// contact can't list files in contact's outbox
|
||||
assertThrows(ApiException.class, () ->
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFiles(contactProperties, contact.outboxId));
|
||||
|
||||
// owner downloads both files from contact's outbox
|
||||
@@ -252,17 +253,19 @@ public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
// contact can't download files again, even if knowing name
|
||||
File file2forbidden = folder.newFile();
|
||||
File file3forbidden = folder.newFile();
|
||||
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
|
||||
contact.outboxId, file2name, file2forbidden));
|
||||
assertThrows(ApiException.class, () -> api.getFile(contactProperties,
|
||||
contact.outboxId, file3name, file3forbidden));
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFile(contactProperties, contact.outboxId, file2name,
|
||||
file2forbidden));
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.getFile(contactProperties, contact.outboxId, file3name,
|
||||
file3forbidden));
|
||||
assertEquals(0, file1forbidden.length());
|
||||
assertEquals(0, file2forbidden.length());
|
||||
|
||||
// contact can't delete files in outbox
|
||||
assertThrows(ApiException.class, () ->
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.deleteFile(contactProperties, contact.outboxId, file2name));
|
||||
assertThrows(ApiException.class, () ->
|
||||
assertThrows(TolerableFailureException.class, () ->
|
||||
api.deleteFile(contactProperties, contact.outboxId, file3name));
|
||||
|
||||
// owner deletes files
|
||||
|
||||
@@ -110,7 +110,8 @@ public class MailboxPairingTaskImplTest extends BrambleMockTestCase {
|
||||
oneOf(db).transaction(with(false), withDbRunnable(txn));
|
||||
oneOf(mailboxSettingsManager).setOwnMailboxProperties(
|
||||
with(txn), with(matches(ownerProperties)));
|
||||
oneOf(mailboxSettingsManager).recordSuccessfulConnection(txn, time);
|
||||
oneOf(mailboxSettingsManager).recordSuccessfulConnection(txn, time,
|
||||
ownerProperties.getServerSupports());
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(singletonList(contact1)));
|
||||
oneOf(mailboxUpdateManager).getRemoteUpdate(txn,
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager.MailboxHook;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
|
||||
@@ -18,7 +19,6 @@ import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_ATTEMPTS;
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_LAST_ATTEMPT;
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_LAST_SUCCESS;
|
||||
@@ -27,10 +27,11 @@ import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTIN
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_KEY_TOKEN;
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_NAMESPACE;
|
||||
import static org.briarproject.bramble.mailbox.MailboxSettingsManagerImpl.SETTINGS_UPLOADS_NAMESPACE;
|
||||
import static org.briarproject.bramble.test.TestUtils.getEvent;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.hasEvent;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -39,6 +40,7 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final SettingsManager settingsManager =
|
||||
context.mock(SettingsManager.class);
|
||||
private final MailboxHook hook = context.mock(MailboxHook.class);
|
||||
|
||||
private final MailboxSettingsManager manager =
|
||||
new MailboxSettingsManagerImpl(settingsManager);
|
||||
@@ -47,6 +49,8 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
private final MailboxAuthToken token = new MailboxAuthToken(getRandomId());
|
||||
private final List<MailboxVersion> serverSupports =
|
||||
asList(new MailboxVersion(1, 0), new MailboxVersion(1, 1));
|
||||
private final MailboxProperties properties = new MailboxProperties(onion,
|
||||
token, serverSupports);
|
||||
private final int[] serverSupportsInts = {1, 0, 1, 1};
|
||||
private final ContactId contactId1 = new ContactId(random.nextInt());
|
||||
private final ContactId contactId2 = new ContactId(random.nextInt());
|
||||
@@ -98,17 +102,40 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
expectedSettings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||
expectedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS,
|
||||
serverSupportsInts);
|
||||
MailboxProperties properties = new MailboxProperties(onion, token,
|
||||
serverSupports);
|
||||
|
||||
manager.registerMailboxHook(hook);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
oneOf(hook).mailboxPaired(txn, properties);
|
||||
}});
|
||||
|
||||
manager.setOwnMailboxProperties(txn, properties);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovesProperties() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Settings expectedSettings = new Settings();
|
||||
expectedSettings.put(SETTINGS_KEY_ONION, "");
|
||||
expectedSettings.put(SETTINGS_KEY_TOKEN, "");
|
||||
expectedSettings.put(SETTINGS_KEY_ATTEMPTS, "");
|
||||
expectedSettings.put(SETTINGS_KEY_LAST_ATTEMPT, "");
|
||||
expectedSettings.put(SETTINGS_KEY_LAST_SUCCESS, "");
|
||||
expectedSettings.put(SETTINGS_KEY_SERVER_SUPPORTS, "");
|
||||
|
||||
manager.registerMailboxHook(hook);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
oneOf(hook).mailboxUnpaired(txn);
|
||||
}});
|
||||
|
||||
manager.removeOwnMailboxProperties(txn);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnsDefaultStatusIfSettingsAreEmpty() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
@@ -147,45 +174,26 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testRecordsSuccess() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Settings oldSettings = new Settings();
|
||||
oldSettings
|
||||
.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, serverSupportsInts);
|
||||
Settings expectedSettings = new Settings();
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(oldSettings));
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
}});
|
||||
|
||||
manager.recordSuccessfulConnection(txn, now);
|
||||
assertTrue(hasEvent(txn, OwnMailboxConnectionStatusEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordsSuccessWithVersions() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
List<MailboxVersion> versions = singletonList(new MailboxVersion(2, 1));
|
||||
Settings expectedSettings = new Settings();
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, now);
|
||||
expectedSettings.putInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
expectedSettings.putInt(SETTINGS_KEY_SERVER_SUPPORTS, 0);
|
||||
int[] newVersionsInts = {2, 1};
|
||||
expectedSettings
|
||||
.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, newVersionsInts);
|
||||
expectedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS,
|
||||
serverSupportsInts);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
}});
|
||||
|
||||
manager.recordSuccessfulConnection(txn, now, versions);
|
||||
hasEvent(txn, OwnMailboxConnectionStatusEvent.class);
|
||||
manager.recordSuccessfulConnection(txn, now, serverSupports);
|
||||
OwnMailboxConnectionStatusEvent e =
|
||||
getEvent(txn, OwnMailboxConnectionStatusEvent.class);
|
||||
MailboxStatus status = e.getStatus();
|
||||
assertEquals(now, status.getTimeOfLastAttempt());
|
||||
assertEquals(now, status.getTimeOfLastSuccess());
|
||||
assertEquals(0, status.getAttemptsSinceSuccess());
|
||||
assertFalse(status.hasProblem(now));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.mailbox;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.client.ContactGroupFactory;
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfEntry;
|
||||
@@ -16,6 +17,9 @@ import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdate;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateWithMailbox;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxPairedEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxUnpairedEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.MailboxUpdateSentEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
@@ -32,6 +36,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.GROUP_KEY_SENT_CLIENT_SUPPORTS;
|
||||
@@ -45,6 +50,7 @@ import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.PROP_KEY
|
||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getEvent;
|
||||
import static org.briarproject.bramble.test.TestUtils.getGroup;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMessage;
|
||||
@@ -71,6 +77,11 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
private final MailboxSettingsManager mailboxSettingsManager =
|
||||
context.mock(MailboxSettingsManager.class);
|
||||
|
||||
private final Contact contact = getContact();
|
||||
private final List<Contact> contacts = singletonList(contact);
|
||||
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
private final GroupId contactGroupId = contactGroup.getId();
|
||||
private final Message message = getMessage(contactGroupId);
|
||||
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
private final BdfDictionary propsDict;
|
||||
private final BdfDictionary emptyPropsDict = new BdfDictionary();
|
||||
@@ -78,7 +89,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
private final BdfList someClientSupports;
|
||||
private final List<MailboxVersion> newerClientSupportsList;
|
||||
private final BdfList newerClientSupports;
|
||||
private final List<MailboxVersion> someServerSupportsList;
|
||||
private final BdfList someServerSupports;
|
||||
private final BdfList emptyServerSupports = new BdfList();
|
||||
private final MailboxProperties updateProps;
|
||||
@@ -100,8 +110,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
newerClientSupportsList.get(0).getMajor(),
|
||||
newerClientSupportsList.get(0).getMinor()));
|
||||
|
||||
someServerSupportsList = singletonList(new MailboxVersion(
|
||||
rnd.nextInt(), rnd.nextInt()));
|
||||
List<MailboxVersion> someServerSupportsList =
|
||||
singletonList(new MailboxVersion(rnd.nextInt(), rnd.nextInt()));
|
||||
someServerSupports = BdfList.of(BdfList.of(
|
||||
someServerSupportsList.get(0).getMajor(),
|
||||
someServerSupportsList.get(0).getMinor()));
|
||||
@@ -135,8 +145,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testCreatesGroupsAtUnpairedStartup() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
BdfDictionary sentDict = BdfDictionary.of(new BdfEntry(
|
||||
GROUP_KEY_SENT_CLIENT_SUPPORTS,
|
||||
@@ -158,8 +166,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
will(returnValue(null));
|
||||
@@ -167,9 +175,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
@@ -178,14 +186,15 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
|
||||
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
|
||||
assertNoMailboxPropertiesSent(e, someClientSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatesGroupsAndCreatesAndSendsAtPairedStartup()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
BdfDictionary sentDict = BdfDictionary.of(new BdfEntry(
|
||||
GROUP_KEY_SENT_CLIENT_SUPPORTS,
|
||||
@@ -207,8 +216,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
will(returnValue(ownProps));
|
||||
@@ -222,9 +231,9 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 1, someClientSupports,
|
||||
someServerSupports, propsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
@@ -233,14 +242,15 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
|
||||
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
|
||||
assertMailboxPropertiesSent(e, someClientSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnchangedClientSupportsOnSecondStartup() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Transaction txn1 = new Transaction(null, false);
|
||||
Transaction txn2 = new Transaction(null, false);
|
||||
|
||||
Map<MessageId, BdfDictionary> emptyMessageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
@@ -249,46 +259,49 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
someClientSupports));
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||
oneOf(db).containsGroup(txn1, localGroup.getId());
|
||||
will(returnValue(false));
|
||||
oneOf(db).addGroup(txn, localGroup);
|
||||
oneOf(db).getContacts(txn);
|
||||
oneOf(db).addGroup(txn1, localGroup);
|
||||
oneOf(db).getContacts(txn1);
|
||||
will(returnValue(singletonList(contact)));
|
||||
|
||||
// addingContact()
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).addGroup(txn, contactGroup);
|
||||
oneOf(clientVersioningManager).getClientVisibility(txn,
|
||||
oneOf(db).addGroup(txn1, contactGroup);
|
||||
oneOf(clientVersioningManager).getClientVisibility(txn1,
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
oneOf(db).setGroupVisibility(txn1, contact.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn1, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn1);
|
||||
will(returnValue(null));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn1,
|
||||
contactGroupId);
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn1, contactGroupId, 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn1, localGroup.getId(),
|
||||
sentDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
t.onDatabaseOpened(txn1);
|
||||
|
||||
MailboxUpdateSentEvent e = getEvent(txn1, MailboxUpdateSentEvent.class);
|
||||
assertNoMailboxPropertiesSent(e, someClientSupportsList);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||
oneOf(db).containsGroup(txn2, localGroup.getId());
|
||||
will(returnValue(true));
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn2,
|
||||
localGroup.getId());
|
||||
will(returnValue(sentDict));
|
||||
oneOf(clientHelper).parseMailboxVersionList(someClientSupports);
|
||||
@@ -296,16 +309,16 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
t.onDatabaseOpened(txn2);
|
||||
|
||||
assertFalse(hasEvent(txn2, MailboxUpdateSentEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendsUpdateWhenClientSupportsChangedOnSecondStartup()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Transaction txn1 = new Transaction(null, false);
|
||||
Transaction txn2 = new Transaction(null, false);
|
||||
|
||||
Map<MessageId, BdfDictionary> emptyMessageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
@@ -314,41 +327,45 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
someClientSupports));
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||
oneOf(db).containsGroup(txn1, localGroup.getId());
|
||||
will(returnValue(false));
|
||||
oneOf(db).addGroup(txn, localGroup);
|
||||
oneOf(db).getContacts(txn);
|
||||
oneOf(db).addGroup(txn1, localGroup);
|
||||
oneOf(db).getContacts(txn1);
|
||||
will(returnValue(singletonList(contact)));
|
||||
|
||||
// addingContact()
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(db).addGroup(txn, contactGroup);
|
||||
oneOf(clientVersioningManager).getClientVisibility(txn,
|
||||
oneOf(db).addGroup(txn1, contactGroup);
|
||||
oneOf(clientVersioningManager).getClientVisibility(txn1,
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
oneOf(db).setGroupVisibility(txn1, contact.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn1, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn1);
|
||||
will(returnValue(null));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn1,
|
||||
contactGroupId);
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn1, contactGroupId, 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn1, localGroup.getId(),
|
||||
sentDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
t.onDatabaseOpened(txn1);
|
||||
|
||||
MailboxUpdateSentEvent e1 =
|
||||
getEvent(txn1, MailboxUpdateSentEvent.class);
|
||||
assertNoMailboxPropertiesSent(e1, someClientSupportsList);
|
||||
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
@@ -357,66 +374,68 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
MessageId messageId = new MessageId(getRandomId());
|
||||
messageMetadata.put(messageId, metaDictionary);
|
||||
BdfList body = BdfList.of(1, someClientSupports, someServerSupports,
|
||||
propsDict);
|
||||
BdfList oldBody = BdfList.of(1, someClientSupports, emptyServerSupports,
|
||||
emptyPropsDict);
|
||||
BdfDictionary newerSentDict = BdfDictionary.of(new BdfEntry(
|
||||
GROUP_KEY_SENT_CLIENT_SUPPORTS,
|
||||
newerClientSupports));
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||
oneOf(db).containsGroup(txn2, localGroup.getId());
|
||||
will(returnValue(true));
|
||||
|
||||
// Find out that we are now on newerClientSupportsList
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn2,
|
||||
localGroup.getId());
|
||||
will(returnValue(sentDict));
|
||||
oneOf(clientHelper).parseMailboxVersionList(someClientSupports);
|
||||
will(returnValue(someClientSupportsList));
|
||||
|
||||
oneOf(db).getContacts(txn);
|
||||
oneOf(db).getContacts(txn2);
|
||||
will(returnValue(singletonList(contact)));
|
||||
oneOf(db).getContact(txn, contact.getId());
|
||||
oneOf(db).getContact(txn2, contact.getId());
|
||||
will(returnValue(contact));
|
||||
|
||||
// getLocalUpdate()
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn2,
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, messageId);
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).getMessageAsList(txn2, messageId);
|
||||
will(returnValue(oldBody));
|
||||
oneOf(clientHelper).parseAndValidateMailboxUpdate(
|
||||
someClientSupports, someServerSupports, propsDict);
|
||||
will(returnValue(updateWithMailbox));
|
||||
someClientSupports, emptyServerSupports, emptyPropsDict);
|
||||
will(returnValue(updateNoMailbox));
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
|
||||
// storeMessageReplaceLatest()
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn2,
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2,
|
||||
newerClientSupports, someServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, messageId);
|
||||
expectStoreMessage(txn2, contactGroupId, 2,
|
||||
newerClientSupports, emptyServerSupports, emptyPropsDict);
|
||||
oneOf(db).removeMessage(txn2, messageId);
|
||||
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn2, localGroup.getId(),
|
||||
newerSentDict);
|
||||
}});
|
||||
|
||||
t = createInstance(newerClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
t.onDatabaseOpened(txn2);
|
||||
|
||||
MailboxUpdateSentEvent e2 =
|
||||
getEvent(txn2, MailboxUpdateSentEvent.class);
|
||||
assertNoMailboxPropertiesSent(e2, newerClientSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatesContactGroupWhenAddingContactUnpaired()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
@@ -429,8 +448,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
will(returnValue(null));
|
||||
@@ -438,22 +457,23 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 1, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.addingContact(txn, contact);
|
||||
|
||||
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
|
||||
assertNoMailboxPropertiesSent(e, someClientSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatesContactGroupAndCreatesAndSendsWhenAddingContactPaired()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
@@ -466,8 +486,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
contact.getId(), CLIENT_ID, MAJOR_VERSION);
|
||||
will(returnValue(SHARED));
|
||||
oneOf(db).setGroupVisibility(txn, contact.getId(),
|
||||
contactGroup.getId(), SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroup.getId(),
|
||||
contactGroupId, SHARED);
|
||||
oneOf(clientHelper).setContactId(txn, contactGroupId,
|
||||
contact.getId());
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
will(returnValue(ownProps));
|
||||
@@ -481,21 +501,22 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 1, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 1, someClientSupports,
|
||||
someServerSupports, propsDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.addingContact(txn, contact);
|
||||
|
||||
MailboxUpdateSentEvent e = getEvent(txn, MailboxUpdateSentEvent.class);
|
||||
assertMailboxPropertiesSent(e, someClientSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovesGroupWhenRemovingContact() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
@@ -512,9 +533,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
public void testDoesNotDeleteAnythingWhenFirstUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, someClientSupports, someServerSupports,
|
||||
propsDict);
|
||||
Metadata meta = new Metadata();
|
||||
@@ -549,16 +567,23 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||
t.incomingMessage(txn, message, meta));
|
||||
assertTrue(hasEvent(txn, RemoteMailboxUpdateEvent.class));
|
||||
|
||||
RemoteMailboxUpdateEvent e =
|
||||
getEvent(txn, RemoteMailboxUpdateEvent.class);
|
||||
assertEquals(contact.getId(), e.getContact());
|
||||
MailboxUpdate u = e.getMailboxUpdate();
|
||||
assertTrue(u.hasMailbox());
|
||||
MailboxUpdateWithMailbox uMailbox = (MailboxUpdateWithMailbox) u;
|
||||
assertEquals(updateWithMailbox.getClientSupports(),
|
||||
uMailbox.getClientSupports());
|
||||
assertEquals(updateWithMailbox.getMailboxProperties(),
|
||||
uMailbox.getMailboxProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletesOlderUpdateWhenUpdateIsDelivered()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
BdfList body = BdfList.of(1, someClientSupports, someServerSupports,
|
||||
propsDict);
|
||||
Metadata meta = new Metadata();
|
||||
@@ -601,14 +626,22 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||
t.incomingMessage(txn, message, meta));
|
||||
assertTrue(hasEvent(txn, RemoteMailboxUpdateEvent.class));
|
||||
|
||||
RemoteMailboxUpdateEvent e =
|
||||
getEvent(txn, RemoteMailboxUpdateEvent.class);
|
||||
assertEquals(contact.getId(), e.getContact());
|
||||
MailboxUpdate u = e.getMailboxUpdate();
|
||||
assertTrue(u.hasMailbox());
|
||||
MailboxUpdateWithMailbox uMailbox = (MailboxUpdateWithMailbox) u;
|
||||
assertEquals(updateWithMailbox.getClientSupports(),
|
||||
uMailbox.getClientSupports());
|
||||
assertEquals(updateWithMailbox.getMailboxProperties(),
|
||||
uMailbox.getMailboxProperties());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletesObsoleteUpdateWhenDelivered() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
GroupId contactGroupId = new GroupId(getRandomId());
|
||||
Message message = getMessage(contactGroupId);
|
||||
Metadata meta = new Metadata();
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||
@@ -635,16 +668,13 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
assertEquals(ACCEPT_DO_NOT_SHARE,
|
||||
t.incomingMessage(txn, message, meta));
|
||||
|
||||
assertFalse(hasEvent(txn, RemoteMailboxUpdateEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatesAndStoresLocalUpdateWithNewVersionOnPairing()
|
||||
throws Exception {
|
||||
Contact contact = getContact();
|
||||
List<Contact> contacts = singletonList(contact);
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
MessageId latestId = new MessageId(getRandomId());
|
||||
@@ -671,23 +701,33 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 2, someClientSupports,
|
||||
someServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.mailboxPaired(txn, ownProps.getOnion(), someServerSupportsList);
|
||||
t.mailboxPaired(txn, ownProps);
|
||||
|
||||
MailboxPairedEvent e = getEvent(txn, MailboxPairedEvent.class);
|
||||
assertEquals(ownProps, e.getProperties());
|
||||
Map<ContactId, MailboxUpdateWithMailbox> localUpdates =
|
||||
e.getLocalUpdates();
|
||||
assertEquals(singleton(contact.getId()), localUpdates.keySet());
|
||||
MailboxUpdateWithMailbox u = localUpdates.get(contact.getId());
|
||||
assertEquals(updateWithMailbox.getClientSupports(),
|
||||
u.getClientSupports());
|
||||
assertEquals(updateWithMailbox.getMailboxProperties(),
|
||||
u.getMailboxProperties());
|
||||
|
||||
assertFalse(hasEvent(txn, MailboxUpdateSentEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresLocalUpdateNoMailboxWithNewVersionOnUnpairing()
|
||||
throws Exception {
|
||||
Contact contact = getContact();
|
||||
List<Contact> contacts = singletonList(contact);
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
@@ -709,22 +749,28 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
expectStoreMessage(txn, contactGroupId, 2, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.mailboxUnpaired(txn);
|
||||
|
||||
MailboxUnpairedEvent e = getEvent(txn, MailboxUnpairedEvent.class);
|
||||
Map<ContactId, MailboxUpdate> localUpdates = e.getLocalUpdates();
|
||||
assertEquals(singleton(contact.getId()), localUpdates.keySet());
|
||||
MailboxUpdate u = localUpdates.get(contact.getId());
|
||||
assertFalse(u.hasMailbox());
|
||||
|
||||
assertFalse(hasEvent(txn, MailboxUpdateSentEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRemoteUpdate() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||
@@ -742,7 +788,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
.createContactGroup(CLIENT_ID, MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, messageId);
|
||||
will(returnValue(body));
|
||||
@@ -760,8 +806,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
public void testGetRemoteUpdateReturnsNullBecauseNoUpdate()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
Map<MessageId, BdfDictionary> emptyMessageMetadata =
|
||||
new LinkedHashMap<>();
|
||||
|
||||
@@ -772,7 +816,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
}});
|
||||
|
||||
@@ -783,8 +827,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testGetRemoteUpdateNoMailbox() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||
@@ -802,7 +844,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, messageId);
|
||||
will(returnValue(body));
|
||||
@@ -819,8 +861,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testGetLocalUpdate() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||
@@ -838,7 +878,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, messageId);
|
||||
will(returnValue(body));
|
||||
@@ -855,8 +895,6 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testGetLocalUpdateNoMailbox() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Contact contact = getContact();
|
||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||
@@ -874,7 +912,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
oneOf(clientHelper).getMessageAsList(txn, messageId);
|
||||
will(returnValue(body));
|
||||
@@ -909,4 +947,22 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
false);
|
||||
}});
|
||||
}
|
||||
|
||||
private void assertNoMailboxPropertiesSent(MailboxUpdateSentEvent e,
|
||||
List<MailboxVersion> clientSupports) {
|
||||
assertEquals(contact.getId(), e.getContactId());
|
||||
MailboxUpdate u = e.getMailboxUpdate();
|
||||
assertEquals(clientSupports, u.getClientSupports());
|
||||
assertFalse(u.hasMailbox());
|
||||
}
|
||||
|
||||
private void assertMailboxPropertiesSent(MailboxUpdateSentEvent e,
|
||||
List<MailboxVersion> clientSupports) {
|
||||
assertEquals(contact.getId(), e.getContactId());
|
||||
MailboxUpdate u = e.getMailboxUpdate();
|
||||
assertEquals(clientSupports, u.getClientSupports());
|
||||
assertTrue(u.hasMailbox());
|
||||
MailboxUpdateWithMailbox uMailbox = (MailboxUpdateWithMailbox) u;
|
||||
assertEquals(updateProps, uMailbox.getMailboxProperties());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
|
||||
public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
|
||||
private final MailboxWorkerFactory workerFactory =
|
||||
context.mock(MailboxWorkerFactory.class);
|
||||
private final ConnectivityChecker connectivityChecker =
|
||||
context.mock(ConnectivityChecker.class);
|
||||
private final TorReachabilityMonitor reachabilityMonitor =
|
||||
context.mock(TorReachabilityMonitor.class);
|
||||
private final MailboxWorker contactListWorker =
|
||||
context.mock(MailboxWorker.class, "contactListWorker");
|
||||
private final MailboxWorker uploadWorker1 =
|
||||
context.mock(MailboxWorker.class, "uploadWorker1");
|
||||
private final MailboxWorker uploadWorker2 =
|
||||
context.mock(MailboxWorker.class, "uploadWorker2");
|
||||
private final MailboxWorker downloadWorker =
|
||||
context.mock(MailboxWorker.class, "downloadWorker");
|
||||
|
||||
private final MailboxProperties properties =
|
||||
getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
private final MailboxFolderId folderId = new MailboxFolderId(getRandomId());
|
||||
private final ContactId contactId1 = getContactId();
|
||||
private final ContactId contactId2 = getContactId();
|
||||
|
||||
private final OwnMailboxClient client;
|
||||
|
||||
public OwnMailboxClientTest() {
|
||||
expectCreateContactListWorker();
|
||||
client = new OwnMailboxClient(workerFactory, connectivityChecker,
|
||||
reachabilityMonitor, properties);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAndDestroyWithNoContactsAssigned() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignContactForUploadAndDestroyClient() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the contact is assigned, the worker should be created and
|
||||
// started
|
||||
expectCreateUploadWorker(contactId1, uploadWorker1);
|
||||
expectStartWorker(uploadWorker1);
|
||||
client.assignContactForUpload(contactId1, properties, folderId);
|
||||
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(uploadWorker1);
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignContactForUpload() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the contact is assigned, the worker should be created and
|
||||
// started
|
||||
expectCreateUploadWorker(contactId1, uploadWorker1);
|
||||
expectStartWorker(uploadWorker1);
|
||||
client.assignContactForUpload(contactId1, properties, folderId);
|
||||
|
||||
// When the contact is deassigned, the worker should be destroyed
|
||||
expectDestroyWorker(uploadWorker1);
|
||||
client.deassignContactForUpload(contactId1);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignTwoContactsForUpload() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the first contact is assigned, the first worker should be
|
||||
// created and started
|
||||
expectCreateUploadWorker(contactId1, uploadWorker1);
|
||||
expectStartWorker(uploadWorker1);
|
||||
client.assignContactForUpload(contactId1, properties, folderId);
|
||||
|
||||
// When the second contact is assigned, the second worker should be
|
||||
// created and started
|
||||
expectCreateUploadWorker(contactId2, uploadWorker2);
|
||||
expectStartWorker(uploadWorker2);
|
||||
client.assignContactForUpload(contactId2, properties, folderId);
|
||||
|
||||
// When the second contact is deassigned, the second worker should be
|
||||
// destroyed
|
||||
expectDestroyWorker(uploadWorker2);
|
||||
client.deassignContactForUpload(contactId2);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
// When the first contact is deassigned, the first worker should be
|
||||
// destroyed
|
||||
expectDestroyWorker(uploadWorker1);
|
||||
client.deassignContactForUpload(contactId1);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignContactForDownloadAndDestroyClient() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the contact is assigned, the worker should be created and
|
||||
// started
|
||||
expectCreateDownloadWorker();
|
||||
expectStartWorker(downloadWorker);
|
||||
client.assignContactForDownload(contactId1, properties, folderId);
|
||||
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(downloadWorker);
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignTwoContactsForDownload() {
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the first contact is assigned, the worker should be created and
|
||||
// started
|
||||
expectCreateDownloadWorker();
|
||||
expectStartWorker(downloadWorker);
|
||||
client.assignContactForDownload(contactId1, properties, folderId);
|
||||
|
||||
// When the second contact is assigned, nothing should happen to the
|
||||
// worker
|
||||
client.assignContactForDownload(contactId2, properties, folderId);
|
||||
|
||||
// When the first contact is deassigned, nothing should happen to the
|
||||
// worker
|
||||
client.deassignContactForDownload(contactId1);
|
||||
|
||||
// When the second contact is deassigned, the worker should be
|
||||
// destroyed
|
||||
expectDestroyWorker(downloadWorker);
|
||||
client.deassignContactForDownload(contactId2);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
private void expectCreateContactListWorker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(workerFactory).createContactListWorkerForOwnMailbox(
|
||||
connectivityChecker, properties);
|
||||
will(returnValue(contactListWorker));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCreateUploadWorker(ContactId contactId,
|
||||
MailboxWorker worker) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(workerFactory).createUploadWorker(connectivityChecker,
|
||||
properties, folderId, contactId);
|
||||
will(returnValue(worker));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCreateDownloadWorker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(workerFactory).createDownloadWorkerForOwnMailbox(
|
||||
connectivityChecker, reachabilityMonitor, properties);
|
||||
will(returnValue(downloadWorker));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectStartWorker(MailboxWorker worker) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(worker).start();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyWorker(MailboxWorker worker) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(worker).destroy();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyConnectivityChecker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).destroy();
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.mailbox.MailboxDownloadWorker.FolderFile;
|
||||
import static org.briarproject.bramble.mailbox.OwnMailboxDownloadWorker.MAX_ROUND_ROBIN_FILES;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class OwnMailboxDownloadWorkerTest
|
||||
extends MailboxDownloadWorkerTest<OwnMailboxDownloadWorker> {
|
||||
|
||||
private final MailboxFolderId folderId1 =
|
||||
new MailboxFolderId(getRandomId());
|
||||
private final MailboxFolderId folderId2 =
|
||||
new MailboxFolderId(getRandomId());
|
||||
private final List<MailboxFolderId> folderIds =
|
||||
asList(folderId1, folderId2);
|
||||
|
||||
public OwnMailboxDownloadWorkerTest() {
|
||||
mailboxProperties = getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
worker = new OwnMailboxDownloadWorker(connectivityChecker,
|
||||
torReachabilityMonitor, mailboxApiCaller, mailboxApi,
|
||||
mailboxFileManager, mailboxProperties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksConnectivityWhenStartedAndRemovesObserverWhenDestroyed() {
|
||||
// When the worker is started it should start a connectivity check
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksForFilesWhenConnectivityCheckSucceeds()
|
||||
throws Exception {
|
||||
// When the worker is started it should start a connectivity check
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the connectivity check succeeds, a list-folders task should be
|
||||
// started for the first download cycle
|
||||
AtomicReference<ApiCall> listFoldersTask = new AtomicReference<>();
|
||||
expectStartTask(listFoldersTask);
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the list-folders tasks runs and finds no folders with files
|
||||
// to download, it should add a Tor reachability observer
|
||||
expectCheckForFoldersWithAvailableFiles(emptyList());
|
||||
expectAddReachabilityObserver();
|
||||
assertFalse(listFoldersTask.get().callApi());
|
||||
|
||||
// When the reachability observer is called, a list-folders task should
|
||||
// be started for the second download cycle
|
||||
expectStartTask(listFoldersTask);
|
||||
worker.onTorReachable();
|
||||
|
||||
// When the list-folders tasks runs and finds no folders with files
|
||||
// to download, it should finish the second download cycle
|
||||
expectCheckForFoldersWithAvailableFiles(emptyList());
|
||||
assertFalse(listFoldersTask.get().callApi());
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDownloadsFilesWhenConnectivityCheckSucceeds()
|
||||
throws Exception {
|
||||
// When the worker is started it should start a connectivity check
|
||||
expectStartConnectivityCheck();
|
||||
worker.start();
|
||||
|
||||
// When the connectivity check succeeds, a list-folders task should be
|
||||
// started for the first download cycle
|
||||
AtomicReference<ApiCall> listFoldersTask = new AtomicReference<>();
|
||||
expectStartTask(listFoldersTask);
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the list-folders tasks runs and finds some folders with files
|
||||
// to download, it should start a list-files task for the first folder
|
||||
AtomicReference<ApiCall> listFilesTask = new AtomicReference<>();
|
||||
expectCheckForFoldersWithAvailableFiles(folderIds);
|
||||
expectStartTask(listFilesTask);
|
||||
assertFalse(listFoldersTask.get().callApi());
|
||||
|
||||
// When the first list-files task runs and finds no files to download,
|
||||
// it should start a second list-files task for the next folder
|
||||
expectCheckForFiles(folderId1, emptyList());
|
||||
expectStartTask(listFilesTask);
|
||||
assertFalse(listFilesTask.get().callApi());
|
||||
|
||||
// When the second list-files task runs and finds some files to
|
||||
// download, it should create the round-robin queue and start a
|
||||
// download task for the first file
|
||||
AtomicReference<ApiCall> downloadTask = new AtomicReference<>();
|
||||
expectCheckForFiles(folderId2, files);
|
||||
expectStartTask(downloadTask);
|
||||
assertFalse(listFilesTask.get().callApi());
|
||||
|
||||
// When the first download task runs it should download the file to the
|
||||
// location provided by the file manager and start a delete task
|
||||
AtomicReference<ApiCall> deleteTask = new AtomicReference<>();
|
||||
expectDownloadFile(folderId2, file1);
|
||||
expectStartTask(deleteTask);
|
||||
assertFalse(downloadTask.get().callApi());
|
||||
|
||||
// When the first delete task runs it should delete the file, ignore
|
||||
// the tolerable failure, and start a download task for the next file
|
||||
expectDeleteFile(folderId2, file1, true); // Delete fails tolerably
|
||||
expectStartTask(downloadTask);
|
||||
assertFalse(deleteTask.get().callApi());
|
||||
|
||||
// When the second download task runs it should download the file to
|
||||
// the location provided by the file manager and start a delete task
|
||||
expectDownloadFile(folderId2, file2);
|
||||
expectStartTask(deleteTask);
|
||||
assertFalse(downloadTask.get().callApi());
|
||||
|
||||
// When the second delete task runs it should delete the file and
|
||||
// start a list-inbox task to check for files that may have arrived
|
||||
// since the first download cycle started
|
||||
expectDeleteFile(folderId2, file2, false); // Delete succeeds
|
||||
expectStartTask(listFoldersTask);
|
||||
assertFalse(deleteTask.get().callApi());
|
||||
|
||||
// When the list-inbox tasks runs and finds no more files to download,
|
||||
// it should add a Tor reachability observer
|
||||
expectCheckForFoldersWithAvailableFiles(emptyList());
|
||||
expectAddReachabilityObserver();
|
||||
assertFalse(listFoldersTask.get().callApi());
|
||||
|
||||
// When the reachability observer is called, a list-inbox task should
|
||||
// be started for the second download cycle
|
||||
expectStartTask(listFoldersTask);
|
||||
worker.onTorReachable();
|
||||
|
||||
// When the list-inbox tasks runs and finds no more files to download,
|
||||
// it should finish the second download cycle
|
||||
expectCheckForFoldersWithAvailableFiles(emptyList());
|
||||
assertFalse(listFoldersTask.get().callApi());
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// and reachability observers
|
||||
expectRemoveObservers();
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRoundRobinQueueVisitsAllFolders() {
|
||||
// Ten folders with two files each
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available =
|
||||
createAvailableFiles(10, 2);
|
||||
Queue<FolderFile> queue = worker.createRoundRobinQueue(available);
|
||||
// Check that all files were queued
|
||||
for (MailboxFolderId folderId : available.keySet()) {
|
||||
assertEquals(2, countFilesWithFolderId(queue, folderId));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSizeOfRoundRobinQueueIsLimited() {
|
||||
// Two folders with MAX_ROUND_ROBIN_FILES each
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available =
|
||||
createAvailableFiles(2, MAX_ROUND_ROBIN_FILES);
|
||||
Queue<FolderFile> queue = worker.createRoundRobinQueue(available);
|
||||
// Check that half the files in each folder were queued
|
||||
for (MailboxFolderId folderId : available.keySet()) {
|
||||
assertEquals(MAX_ROUND_ROBIN_FILES / 2,
|
||||
countFilesWithFolderId(queue, folderId));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<MailboxFolderId, Queue<MailboxFile>> createAvailableFiles(
|
||||
int numFolders, int numFiles) {
|
||||
Map<MailboxFolderId, Queue<MailboxFile>> available = new HashMap<>();
|
||||
List<MailboxFolderId> folderIds = createFolderIds(numFolders);
|
||||
for (MailboxFolderId folderId : folderIds) {
|
||||
available.put(folderId, createFiles(numFiles));
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
private List<MailboxFolderId> createFolderIds(int size) {
|
||||
List<MailboxFolderId> folderIds = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
folderIds.add(new MailboxFolderId(getRandomId()));
|
||||
}
|
||||
return folderIds;
|
||||
}
|
||||
|
||||
private Queue<MailboxFile> createFiles(int size) {
|
||||
Queue<MailboxFile> files = new LinkedList<>();
|
||||
for (int i = 0; i < size; i++) {
|
||||
files.add(new MailboxFile(new MailboxFileId(getRandomId()), i));
|
||||
}
|
||||
return files;
|
||||
}
|
||||
|
||||
private int countFilesWithFolderId(Queue<FolderFile> queue,
|
||||
MailboxFolderId folderId) {
|
||||
int count = 0;
|
||||
for (FolderFile file : queue) {
|
||||
if (file.folderId.equals(folderId)) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -14,11 +14,13 @@ import org.briarproject.bramble.api.sync.SyncRecordWriter;
|
||||
import org.briarproject.bramble.api.sync.Versions;
|
||||
import org.briarproject.bramble.api.transport.StreamWriter;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.CaptureArgumentAction;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
@@ -68,13 +70,10 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
oneOf(eventBus).addListener(session);
|
||||
// Send the protocol versions
|
||||
oneOf(recordWriter).writeVersions(with(any(Versions.class)));
|
||||
// Calculate capacity for acks
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes));
|
||||
// No messages to ack
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(noAckIdTxn));
|
||||
oneOf(db).getMessagesToAck(noAckIdTxn, contactId, MAX_MESSAGE_IDS);
|
||||
oneOf(db).getMessagesToAck(noAckIdTxn, contactId);
|
||||
will(returnValue(emptyList()));
|
||||
// Calculate capacity for messages
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
@@ -106,7 +105,6 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
MAX_FILE_PAYLOAD_BYTES);
|
||||
|
||||
Transaction ackIdTxn = new Transaction(null, true);
|
||||
Transaction noAckIdTxn = new Transaction(null, true);
|
||||
Transaction msgIdTxn = new Transaction(null, true);
|
||||
Transaction msgTxn = new Transaction(null, true);
|
||||
|
||||
@@ -114,28 +112,24 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
long capacityForMessages =
|
||||
MAX_FILE_PAYLOAD_BYTES - versionRecordBytes - ackRecordBytes;
|
||||
|
||||
AtomicReference<Ack> ack = new AtomicReference<>();
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
// Add listener
|
||||
oneOf(eventBus).addListener(session);
|
||||
// Send the protocol versions
|
||||
oneOf(recordWriter).writeVersions(with(any(Versions.class)));
|
||||
// Load the IDs to ack
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(ackIdTxn));
|
||||
oneOf(db).getMessagesToAck(ackIdTxn, contactId);
|
||||
will(returnValue(singletonList(message.getId())));
|
||||
// Calculate capacity for acks
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes));
|
||||
// One message to ack
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(ackIdTxn));
|
||||
oneOf(db).getMessagesToAck(ackIdTxn, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(singletonList(message.getId())));
|
||||
// Send the ack
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes));
|
||||
oneOf(recordWriter).writeAck(with(any(Ack.class)));
|
||||
// No more messages to ack
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(noAckIdTxn));
|
||||
oneOf(db).getMessagesToAck(noAckIdTxn, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(emptyList()));
|
||||
will(new CaptureArgumentAction<>(ack, Ack.class, 0));
|
||||
// Calculate capacity for messages
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes + ackRecordBytes));
|
||||
@@ -162,6 +156,7 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
|
||||
assertEquals(singletonList(message.getId()),
|
||||
sessionRecord.getAckedIds());
|
||||
assertEquals(singletonList(message.getId()), ack.get().getMessageIds());
|
||||
assertEquals(singletonList(message1.getId()),
|
||||
sessionRecord.getSentIds());
|
||||
}
|
||||
@@ -178,48 +173,50 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
eventBus, contactId, transportId, MAX_LATENCY,
|
||||
streamWriter, recordWriter, sessionRecord, capacity);
|
||||
|
||||
Transaction ackIdTxn1 = new Transaction(null, true);
|
||||
Transaction ackIdTxn2 = new Transaction(null, true);
|
||||
Transaction ackIdTxn = new Transaction(null, true);
|
||||
|
||||
int firstAckRecordBytes =
|
||||
RECORD_HEADER_BYTES + MessageId.LENGTH * MAX_MESSAGE_IDS;
|
||||
int secondAckRecordBytes = RECORD_HEADER_BYTES + MessageId.LENGTH;
|
||||
|
||||
List<MessageId> idsInFirstAck = new ArrayList<>(MAX_MESSAGE_IDS);
|
||||
for (int i = 0; i < MAX_MESSAGE_IDS; i++) {
|
||||
idsInFirstAck.add(new MessageId(getRandomId()));
|
||||
// There are MAX_MESSAGE_IDS + 2 messages that need to be acked, but
|
||||
// only enough capacity to ack MAX_MESSAGE_IDS + 1 messages
|
||||
List<MessageId> idsToAck = new ArrayList<>(MAX_MESSAGE_IDS + 2);
|
||||
for (int i = 0; i < MAX_MESSAGE_IDS + 2; i++) {
|
||||
idsToAck.add(new MessageId(getRandomId()));
|
||||
}
|
||||
// The first ack contains MAX_MESSAGE_IDS IDs
|
||||
List<MessageId> idsInFirstAck = idsToAck.subList(0, MAX_MESSAGE_IDS);
|
||||
// The second ack contains one ID
|
||||
List<MessageId> idsInSecondAck =
|
||||
singletonList(new MessageId(getRandomId()));
|
||||
List<MessageId> allIds = new ArrayList<>(MAX_MESSAGE_IDS + 1);
|
||||
allIds.addAll(idsInFirstAck);
|
||||
allIds.addAll(idsInSecondAck);
|
||||
idsToAck.subList(MAX_MESSAGE_IDS, MAX_MESSAGE_IDS + 1);
|
||||
List<MessageId> idsAcked = idsToAck.subList(0, MAX_MESSAGE_IDS + 1);
|
||||
|
||||
AtomicReference<Ack> firstAck = new AtomicReference<>();
|
||||
AtomicReference<Ack> secondAck = new AtomicReference<>();
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
// Add listener
|
||||
oneOf(eventBus).addListener(session);
|
||||
// Send the protocol versions
|
||||
oneOf(recordWriter).writeVersions(with(any(Versions.class)));
|
||||
// Load the IDs to ack
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(ackIdTxn));
|
||||
oneOf(db).getMessagesToAck(ackIdTxn, contactId);
|
||||
will(returnValue(idsToAck));
|
||||
// Calculate capacity for acks
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes));
|
||||
// Load the IDs for the first ack record
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(ackIdTxn1));
|
||||
oneOf(db).getMessagesToAck(ackIdTxn1, contactId, MAX_MESSAGE_IDS);
|
||||
will(returnValue(idsInFirstAck));
|
||||
// Send the first ack record
|
||||
oneOf(recordWriter).writeAck(with(any(Ack.class)));
|
||||
will(new CaptureArgumentAction<>(firstAck, Ack.class, 0));
|
||||
// Calculate remaining capacity for acks
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes + firstAckRecordBytes));
|
||||
// Load the IDs for the second ack record
|
||||
oneOf(db).transactionWithResult(with(true),
|
||||
withDbCallable(ackIdTxn2));
|
||||
oneOf(db).getMessagesToAck(ackIdTxn2, contactId, 1);
|
||||
will(returnValue(idsInSecondAck));
|
||||
// Send the second ack record
|
||||
oneOf(recordWriter).writeAck(with(any(Ack.class)));
|
||||
will(new CaptureArgumentAction<>(secondAck, Ack.class, 0));
|
||||
// Not enough capacity left for another ack
|
||||
oneOf(recordWriter).getBytesWritten();
|
||||
will(returnValue((long) versionRecordBytes + firstAckRecordBytes
|
||||
@@ -236,7 +233,9 @@ public class MailboxOutgoingSessionTest extends BrambleMockTestCase {
|
||||
|
||||
session.run();
|
||||
|
||||
assertEquals(allIds, sessionRecord.getAckedIds());
|
||||
assertEquals(idsAcked, sessionRecord.getAckedIds());
|
||||
assertEquals(idsInFirstAck, firstAck.get().getMessageIds());
|
||||
assertEquals(idsInSecondAck, secondAck.get().getMessageIds());
|
||||
assertEquals(emptyList(), sessionRecord.getSentIds());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ dependencyVerification {
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||
'org.bouncycastle:bcprov-jdk15to18:1.70:bcprov-jdk15to18-1.70.jar:7df4c54f29ce2dd616dc3b198ca4db3dfcc79e3cb397c084a0aff97b85c0bf38',
|
||||
'org.briarproject:jtorctl:0.4:jtorctl-0.4.jar:4e61f59dc9f3984438a7151c4df8d7c1f83d5fb3eb8c151acfc794a8fef85a36',
|
||||
'org.briarproject:jtorctl:0.5:jtorctl-0.5.jar:43f8c7d390169772b9a2c82ab806c8414c136a2a8636c555e22754bb7260793b',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
|
||||
|
||||
@@ -26,9 +26,10 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
versionCode 10411
|
||||
versionName "1.4.11"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
buildConfigField "String", "GitHash",
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package org.briarproject.briar.android.settings;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.BuildConfig;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class AboutFragment extends Fragment {
|
||||
|
||||
final static String TAG = AboutFragment.class.getName();
|
||||
private static final Logger LOG = getLogger(TAG);
|
||||
|
||||
private TextView briarVersion;
|
||||
private TextView torVersion;
|
||||
private TextView briarWebsite;
|
||||
private TextView briarSourceCode;
|
||||
private TextView briarChangelog;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_about, container,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
requireActivity().setTitle(R.string.about_title);
|
||||
briarVersion = requireActivity().findViewById(R.id.BriarVersion);
|
||||
briarVersion.setText(
|
||||
getString(R.string.briar_version, BuildConfig.VERSION_NAME));
|
||||
torVersion = requireActivity().findViewById(R.id.TorVersion);
|
||||
torVersion.setText(
|
||||
getString(R.string.tor_version, BuildConfig.TorVersion));
|
||||
briarWebsite = requireActivity().findViewById(R.id.BriarWebsite);
|
||||
briarSourceCode = requireActivity().findViewById(R.id.BriarSourceCode);
|
||||
briarChangelog = requireActivity().findViewById(R.id.BriarChangelog);
|
||||
briarWebsite.setOnClickListener(View -> {
|
||||
String url = "https://briarproject.org/";
|
||||
goToUrl(url);
|
||||
});
|
||||
briarSourceCode.setOnClickListener(View -> {
|
||||
String url = "https://code.briarproject.org/briar/briar";
|
||||
goToUrl(url);
|
||||
});
|
||||
briarChangelog.setOnClickListener(View -> {
|
||||
String url =
|
||||
"https://code.briarproject.org/briar/briar/-/wikis/changelog";
|
||||
goToUrl(url);
|
||||
});
|
||||
}
|
||||
|
||||
private void goToUrl(String url) {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse(url));
|
||||
try {
|
||||
startActivity(i);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
Toast.makeText(requireContext(),
|
||||
R.string.error_start_activity, LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
12
briar-android/src/main/res/drawable/ic_info_dark.xml
Normal file
12
briar-android/src/main/res/drawable/ic_info_dark.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0"
|
||||
tools:ignore="NewApi">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z" />
|
||||
</vector>
|
||||
73
briar-android/src/main/res/layout/fragment_about.xml
Normal file
73
briar-android/src/main/res/layout/fragment_about.xml
Normal file
@@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/BriarVersion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/briar_version"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/TorVersion"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/tor_version"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarVersion" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/LinksTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/links"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/TorVersion" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/BriarWebsite"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/briar_website"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/LinksTitle" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/BriarSourceCode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/briar_source_code"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarWebsite" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/BriarChangelog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/briar_changelog"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarSourceCode" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/TranslatorThanks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/translator_thanks"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarChangelog" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -26,6 +26,7 @@
|
||||
<string name="dnkm_xiaomi_button">Предпазване на Briar</string>
|
||||
<string name="dnkm_xiaomi_help">Ако Briar не е заключен в списъка с последно използваните приложения, няма да работи на заден план.</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_old">1. Отворете списъка с отворени приложения (списък за превключване на приложения)\n\n2. Плъзнете надолу върху изображението на Briar докато се покаже икона на катинар\n\n3. Ако катинарът е отключен го докоснете, за да го заключите</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_new">1. Отворете списъка с последните приложения\n\n2. Ако до името на Briar има значка на катинарче, не е необходимо да правите нищо\n\n3. Ако няма – натиснете и задръжте изображението на Briar, докато се появи бутон за катинарче, след което го докоснете</string>
|
||||
<string name="dnkm_warning_dozed_1">Briar не може да работи във фонов режим</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Парола</string>
|
||||
@@ -135,7 +136,7 @@
|
||||
<string name="accept">Приемане</string>
|
||||
<string name="decline">Отказване</string>
|
||||
<string name="online">На линия</string>
|
||||
<string name="offline">Извън линия</string>
|
||||
<string name="offline">Не е свързано</string>
|
||||
<string name="send">Изпращане</string>
|
||||
<string name="allow">Разрешаване</string>
|
||||
<string name="open">Отваряне</string>
|
||||
@@ -294,6 +295,11 @@
|
||||
<string name="different_person_button">Не</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s и %2$s са изпратили еднакви препратки.\n\nЕдиният от двамата вероятно се опитва да разбере кои са контактите ви.\n\nНе им споделяйте, че сте получили същата препратка от друг човек.</string>
|
||||
<string name="pending_contact_updated_toast">Обновена чакаща заявка за контакт</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Непроверен контакт</string>
|
||||
<string name="peer_trust_level_verified">Проверен контакт</string>
|
||||
<string name="peer_trust_level_ourselves">Аз</string>
|
||||
<string name="peer_trust_level_stranger">Непознат</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Запознаване на контакти</string>
|
||||
<string name="introduction_onboarding_text">Можете да запознавате контактите си един с друг, за да се свържат в Briar.</string>
|
||||
@@ -592,7 +598,9 @@
|
||||
<string name="mailbox_setup_camera_error_description">Няма достъп до камерата. Опитайте отново и след рестарт на усройството.</string>
|
||||
<string name="mailbox_setup_paired_title">Свързан</string>
|
||||
<string name="mailbox_setup_paired_description">Пощенската кутия е свързана с Briar.\n\nЗа да е винаги на линия, я дръжте включена в захранване и свързана с безжична мрежа.</string>
|
||||
<string name="tor_offline_title">Извън линия</string>
|
||||
<string name="tor_offline_title">Не е свързано</string>
|
||||
<string name="tor_offline_description">Уверете се, че устройството е свързано с интернет.\n
|
||||
\nСлед това изчакайте иконата на земното кълбо в екрана за настройки на връзката да стане зелена.</string>
|
||||
<string name="tor_offline_button_check">Проверете настройките на връзката</string>
|
||||
<string name="mailbox_status_title">Състояние на пощенаската кутия</string>
|
||||
<string name="mailbox_status_connected_title">Пощенската кутия работи</string>
|
||||
@@ -607,12 +615,41 @@
|
||||
<string name="mailbox_status_connected_info">Последно свързване: %s</string>
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Никога</string>
|
||||
<string name="mailbox_status_unlink_button">Прекъсване на връзка</string>
|
||||
<string name="mailbox_status_unlink_dialog_title">Ще прекъснете ли връзката с пощенската кутия?</string>
|
||||
<string name="mailbox_status_unlink_button">Прекъсване на връзката</string>
|
||||
<string name="mailbox_status_unlink_dialog_title">Желаете ли да прекъснете връзката с пощенската кутия?</string>
|
||||
<string name="mailbox_status_unlink_dialog_question">Сигурни ли сте, че желаете да прекъснете връзката с пощенската кутия?</string>
|
||||
<string name="mailbox_status_unlink_dialog_warning">Ако прекъснете връзката с пощенската кутия, няма да получавате съобщения докато Briar е без мрежа.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Връзката с пощенската кутия е прекъсната</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">За да завършите процеса, следващия път, когато имате достъп до устройството с пощенската кутия, отворете приложението на пощенската кутия и докоснете бутона „Прекъсване на връзката“.\n\nДаже и вече нямате достъп до устройството с пощенската кутия - не се притеснявайте. Вашата информация е шифрована, така че ще остане защитена, дори и да не завършите този процес.</string>
|
||||
<string name="mailbox_error_notification_channel_title">Проблем с пощенската кутия на Briar</string>
|
||||
<string name="mailbox_error_notification_title">Пощенската кутия на Briar не е достъпна</string>
|
||||
<string name="mailbox_error_notification_text">Докоснете за отстраняване на проблема.</string>
|
||||
<string name="mailbox_error_wizard_button">Отстраняване на проблема</string>
|
||||
<string name="mailbox_error_wizard_title">Помощник за отстраняване на неизправнисти на Briar</string>
|
||||
<string name="mailbox_error_wizard_question1">Имате ли достъп до устройството с пощенската кутия?</string>
|
||||
<string name="mailbox_error_wizard_answer1">Да, в момента имам.</string>
|
||||
<string name="mailbox_error_wizard_answer2">В момента не, но по-късно ще имам.</string>
|
||||
<string name="mailbox_error_wizard_answer3">Не, вече нямам достъп до него.</string>
|
||||
<string name="mailbox_error_wizard_info1_1">Проверете да ли устройството с пощенската кутия е включено и има връзка с интернет.</string>
|
||||
<string name="mailbox_error_wizard_question1_1">Отворете приложението за пощенска кутия. Какво виждате?</string>
|
||||
<string name="mailbox_error_wizard_answer1_1">Виждам инструкциите за настройка на пощенска кутия</string>
|
||||
<string name="mailbox_error_wizard_answer1_2">Виждам код за QR</string>
|
||||
<string name="mailbox_error_wizard_answer1_3">Виждам „Пощенската кутия работи“</string>
|
||||
<string name="mailbox_error_wizard_answer1_4">Виждам „Устройството не е свързано“</string>
|
||||
<string name="mailbox_error_wizard_info1_1_1">Прекъснете връзката с пощенската кутия, чрез бутона отдолу, а след това, за да се свържете отново с нея, следвайте инструкциите.</string>
|
||||
<string name="mailbox_error_wizard_info_1_1_2">Прекъснете връзката с пощенската кутия, чрез бутона отдолу, а след това, за да се свържете отново с нея, сканирайте кода за QR..</string>
|
||||
<string name="mailbox_error_wizard_info1_1_3">Използвайте бутона по-долу, за да проверите връзката между Briar и пощенската кутия.\n\n
|
||||
Ако връзката отново е неуспешна:\n
|
||||
\u2022 Проверете дали приложенията Пощенска кутия и Briar са последно издание.\n
|
||||
\u2022 Рестартирайте устройствата с Пощенска кутия и Briar и опитайте отново.</string>
|
||||
<string name="mailbox_error_wizard_info1_1_4">Проверете дали устройството с пощенска кутия е свързано с интернет.\n\nПроверете дали часовникът на това устройство показва точното време, дата и часови пояс.\n\nПроверете дали приложенията Пощенска кутия и Briar са последно издание.\n\nРестартирайте устройствата с Пощенска кутия и Briar и опитайте отново.</string>
|
||||
<string name="mailbox_error_wizard_info2">Върнете се на този екран, когато получите достъп до устройството.</string>
|
||||
<string name="mailbox_error_wizard_info3">Прекъснете връзката с пощенската кутия, чрез бутона отдолу.\n\nСлед като прекъснете връзката със старата пощенска кутия можете да настроите нова по всяко време.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Относно</string>
|
||||
<string name="links">Препратки</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Благодарим на всички сътрудници на Localization Lab</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Изчезващи съобщения</string>
|
||||
<string name="disappearing_messages_explanation_long">При включване, тази настройка прави бъдещите съобщения в този разговор да изчезват след 7\u00A0дни.
|
||||
@@ -730,6 +767,8 @@
|
||||
<string name="website_download_outro">След като файлът бъде изтеглен, го отворете и го инсталирайте.</string>
|
||||
<string name="website_troubleshooting_title">Отстраняване на неизправности</string>
|
||||
<string name="website_troubleshooting_1">Ако не можете да изтеглите приложението пробвайте с друг мрежов четец.</string>
|
||||
<string name="website_troubleshooting_2_old">За да инсталирате изтегленото приложение, може да се наложи да разрешите инсталирането на приложения от „Неизвестни източници“ в системните настройки. След това може да се наложи да изтеглите приложението отново. Препоръчваме ви да изключете настройката \"Неизвестни източници\", след като инсталирате приложението.</string>
|
||||
<string name="website_troubleshooting_2_new">За да инсталирате изтегленото приложение, може да се наложи да разрешите на мрежовия си четец да инсталира непознати приложения. След като инсталирате приложението, ви препоръчваме да премахнете разрешението за инсталиране на неизвестни приложения.</string>
|
||||
<string name="hotspot_help_wifi_title">Проблеми при свързване чрез Wi-Fi:</string>
|
||||
<string name="hotspot_help_wifi_1">Изключете и включете Wi-Fi и на двете устройства, и опитайте отново.</string>
|
||||
<string name="hotspot_help_wifi_2">Ако устройството се оплаква, че безжичната мрежа няма достъп до интернет, останете свързани въпреки това.</string>
|
||||
@@ -750,6 +789,7 @@
|
||||
<string name="hotspot_error_start_callback_failed_unknown">Безжичната точка не може да стартира поради неизвестна грешка: причината е %d</string>
|
||||
<string name="hotspot_error_start_callback_no_group_info">Безжичната точка не може да стартира: няма информация за група</string>
|
||||
<string name="hotspot_error_web_server_start">Грешка при стартиране на уеб сървър</string>
|
||||
<string name="hotspot_error_web_server_serve">Грешка при показване на страницата.n\nАко проблемът продължи да се появява, изпратете обратна връзка (с анонимизирани данни) през приложението Briar.</string>
|
||||
<string name="hotspot_flag_test">Внимание: Това приложение е инсталирано с Android Studio и НЕ може да бъде инсталирано на друго устройство.</string>
|
||||
<string name="hotspot_error_framework_busy">Безжичната точка не може да бъде стартирана.\n\nАко има включена друга безжична точка за достъп или споделяте мобилните си данни по безжичен път, опитайте да ги спрете и пробвайте отново.</string>
|
||||
<!--Transfer Data via Removable Drives-->
|
||||
|
||||
@@ -297,6 +297,11 @@
|
||||
<string name="different_person_button">Andere Person</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s und %2$s haben dir denselben Link geschickt.\n\nMöglicherweise versucht einer der beiden mehr über deine Kontakte zu erfahren.\n\nDu solltest deswegen niemandem sagen, dass du diesen Link auch von jemand anderem erhalten hast.</string>
|
||||
<string name="pending_contact_updated_toast">Ausstehender Kontakt aktualisiert</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Nicht überprüfter Kontakt</string>
|
||||
<string name="peer_trust_level_verified">Überprüfter Kontakt</string>
|
||||
<string name="peer_trust_level_ourselves">Ich</string>
|
||||
<string name="peer_trust_level_stranger">Fremder</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Mache deine Kontakte untereinander bekannt</string>
|
||||
<string name="introduction_onboarding_text">Stelle deine Kontakte einander vor, damit sie miteinander über Briar in Kontakt treten können.</string>
|
||||
@@ -644,6 +649,16 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">Überprüfe, ob das Mailbox-Gerät ordnungsgemäß mit dem Internet verbunden ist.\n\nÜberprüfe, ob die Uhr auf dem Mailbox-Gerät die richtige Uhrzeit, das richtige Datum und die richtige Zeitzone anzeigt.\n\nÜberprüfe, ob die Mailbox- und Briar-Apps auf die neueste Version aktualisiert sind.\n\nStarte deine Mailbox- und Briar-Geräte neu und versuche es erneut.</string>
|
||||
<string name="mailbox_error_wizard_info2">Bitte rufe diesen Bildschirm wieder auf, wenn du Zugriff auf das Gerät hast.</string>
|
||||
<string name="mailbox_error_wizard_info3">Bitte trenne die Verknüpfung deiner Mailbox über die Schaltfläche unten.\n\nNach der Trennung deiner alten Mailbox kannst du jederzeit eine neue Mailbox einrichten.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Über</string>
|
||||
<string name="briar_version">Briar Version: %s</string>
|
||||
<string name="tor_version">Tor Version: %s</string>
|
||||
<string name="links">Links</string>
|
||||
<string name="briar_website">\u2022 <a href="">Webseite</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Quellcode</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Changelog</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Dank an alle Mitwirkenden des Localization Lab</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Selbstlöschende Nachrichten</string>
|
||||
<string name="disappearing_messages_explanation_long">Wenn diese Einstellung aktiviert ist, werden neue
|
||||
@@ -787,7 +802,7 @@
|
||||
<string name="hotspot_error_start_callback_failed_unknown">Hotspot konnte aus einem unbekannten Grund nicht gestartet werden, Grund %d</string>
|
||||
<string name="hotspot_error_start_callback_no_group_info">Hotspot konnte nicht gestartet werden: keine Gruppeninformation</string>
|
||||
<string name="hotspot_error_web_server_start">Fehler beim Starten des Webservers</string>
|
||||
<string name="hotspot_error_web_server_serve">Fehler bei der Darstellung der Website.\n\nBitte sende eine Rückmeldung (mit anonymen Daten) über die Briar-App, wenn das Problem weiterhin besteht.</string>
|
||||
<string name="hotspot_error_web_server_serve">Fehler bei der Darstellung der Webseite.\n\nBitte sende eine Rückmeldung (mit anonymen Daten) über die Briar-App, wenn das Problem weiterhin besteht.</string>
|
||||
<string name="hotspot_flag_test">Warnung: Diese App wurde mit Android Studio installiert und kann NICHT auf einem anderen Gerät installiert werden.</string>
|
||||
<string name="hotspot_error_framework_busy">Hotspot kann nicht gestartet werden.\n\nWenn du einen anderen Hotspot betreibst oder deine Internetverbindung über WLAN teilst, beende dies und versuche es danach erneut.</string>
|
||||
<!--Transfer Data via Removable Drives-->
|
||||
|
||||
@@ -301,6 +301,8 @@
|
||||
<string name="different_person_button">Diferente Persona</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s y %2$s te enviaron el mismo enlace.\n\nUno de ellos puede estar tratando de descubrir quiénes son tus contactos.\n\nNo les digas que recibiste el mismo enlace de otra persona.</string>
|
||||
<string name="pending_contact_updated_toast">Contacto pendiente actualizado</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_ourselves">Yo</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Presenta a tus contactos</string>
|
||||
<string name="introduction_menu_item">Hacer presentación</string>
|
||||
@@ -617,6 +619,10 @@
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Nunca</string>
|
||||
<string name="mailbox_status_unlink_button">Desvincular</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Acerca de</string>
|
||||
<string name="links">Enlaces</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Mensajes con caducidad</string>
|
||||
<string name="disappearing_messages_explanation_long">Activar este ajuste hará que los nuevos
|
||||
|
||||
@@ -307,6 +307,11 @@
|
||||
<string name="different_person_button">شخص متفاوت</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s و %2$s یک پیوند یکسان را به شما ارسال کردند.\n\nشاید یکی از آنها قصد شناسایی مخاطبین شما را دارد.\n\nبه آنها نگویید که همان لینک را از فرد دیگری نیز دریافت کردهاید.</string>
|
||||
<string name="pending_contact_updated_toast">مخاطب معلق به روز رسانی شد</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">مخاطب تایید نشده</string>
|
||||
<string name="peer_trust_level_verified">مخاطب تایید شده</string>
|
||||
<string name="peer_trust_level_ourselves">من</string>
|
||||
<string name="peer_trust_level_stranger">غریبه</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">معرفی مخاطبان</string>
|
||||
<string name="introduction_onboarding_text">مخاطبین خود را به یکدیگر معرفی کنید تا بتوانند در Briar متصل شوند.</string>
|
||||
@@ -676,6 +681,16 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">بررسی کنید که دستگاه Mailbox به درستی به اینترنت متصل باشد.\n\nبررسی کنید که ساعت در دستگاه Mailbox زمان، تاریخ و منطقه زمانی مناسب را نشان دهد.\n\nبررسی کنید که برنامههای Mailbox و Briar به آخرین نسخه بهروزرسانی شده باشند. \n\nدستگاههای Mailbox و Briar خود را راهاندازی مجدد کنید و دوباره امتحان کنید.</string>
|
||||
<string name="mailbox_error_wizard_info2">لطفا هنگامی که به دستگاه دسترسی دارید به این صفحه بازگردید.</string>
|
||||
<string name="mailbox_error_wizard_info3">لطفا با استفاده از دکمه زیر، پیوند Mailbox خود را لغو کنید.\n\nپس از لغو پیوند Mailbox قدیمی، میتوانید هر زمان که خواستید یک Mailbox جدید راهاندازی کنید.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">دربارهی Psiphon</string>
|
||||
<string name="briar_version">نسخه Briar: %s</string>
|
||||
<string name="tor_version">نسخه Tor: %s</string>
|
||||
<string name="links">لینک ها</string>
|
||||
<string name="briar_website">\u2022 <a href="">وبسایت</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">کد منبع</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">گزارش تغییرات</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">با تشکر از همه مشارکت کنندگان در Localization Lab بخصوص erinm، Reza Ghasemi، Mohsen Eghbal و Vox.</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">پیامهای ناپدید شونده</string>
|
||||
<string name="disappearing_messages_explanation_long">روشن کردن این تنظیمات موجب خواهد شد تا پیامهای جدید
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
<string name="different_person_button">Une personne différente</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s et %2$s vous ont envoyé le même lien.\n\nL\'une de ces personnes pourrait tenter de découvrir qui sont vos contacts.\n\nNe lui dites pas que vous avez reçu le même lien de quelqu’un d’autre.</string>
|
||||
<string name="pending_contact_updated_toast">Le contact en attente a été mis à jour</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Présenter vos contacts</string>
|
||||
<string name="introduction_onboarding_text">Présentez vos contacts l\'un à l\'autre ainsi ils pourront se contacter via Briar.</string>
|
||||
@@ -611,6 +612,10 @@
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Jamais</string>
|
||||
<!--About-->
|
||||
<string name="about_title">À propos</string>
|
||||
<string name="links">Liens</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Messages éphémères</string>
|
||||
<string name="disappearing_messages_explanation_long">L’activation de ce paramètre fera disparaître
|
||||
|
||||
@@ -291,6 +291,7 @@
|
||||
<string name="different_person_button">Annar einstaklingur</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s og %2$s sendu þér sama tengilinn.\n\nAnnar þeirra gæti verið að reyna að finna út hverjir tengiliðirnir þínir eru.\n\nEkki segja þeim að þú hafir fengið sama tengil frá einhverjum öðrum.</string>
|
||||
<string name="pending_contact_updated_toast">Tengiliður í bið uppfærður</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Kynntu tengiliðina þína</string>
|
||||
<string name="introduction_menu_item">Útbúa kynningu</string>
|
||||
@@ -603,6 +604,10 @@
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Aldrei</string>
|
||||
<string name="mailbox_status_unlink_button">Aftengja</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Um hugbúnaðinn</string>
|
||||
<string name="links">Hlekkir</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Sjálfeyðandi skilaboð</string>
|
||||
<string name="disappearing_messages_explanation_long">Ef kveikt er á þessari stillingu munu ný
|
||||
|
||||
@@ -284,6 +284,11 @@
|
||||
<string name="different_person_button">別の人</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$sと%2$sから同じリンクを受信しました。\n\nどちらかがあなたの連絡先の内容を知ろうとしている可能性があります。\n\n他の人から同じリンクを受け取ったことを伝えないでください。</string>
|
||||
<string name="pending_contact_updated_toast">保留中の連絡先が更新されました</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">検証されてない連絡先</string>
|
||||
<string name="peer_trust_level_verified">検証された連絡先</string>
|
||||
<string name="peer_trust_level_ourselves">私</string>
|
||||
<string name="peer_trust_level_stranger">見知らぬ人</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">連絡先を紹介</string>
|
||||
<string name="introduction_onboarding_text">連絡先をお互いに紹介することで、Briarで繋がることができます。</string>
|
||||
@@ -627,6 +632,16 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">メールボックス端末がインターネットに適切に接続されているか、確認してください。\n\nメールボックス端末の時計が正しい時刻、日付、時間帯を表示しているか、確認してください。\n\nメールボックスとBriarアプリが最新版に更新されているか、確認してください。\n\nメールボックスとBriarの端末を再起動し、再度お試しください。</string>
|
||||
<string name="mailbox_error_wizard_info2">端末にアクセスしたら、この画面に戻って来てください。</string>
|
||||
<string name="mailbox_error_wizard_info3">以下のボタンを使用してメールボックスをリンク解除してください。\n\n古いメールボックスをリンク解除した後、いつでも新しいメールボックスをセットアップできます。</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Tor Project について</string>
|
||||
<string name="briar_version">Briar バージョン: %s</string>
|
||||
<string name="tor_version">Tor バージョン: %s</string>
|
||||
<string name="links">リンク</string>
|
||||
<string name="briar_website">\u2022 <a href="">ウェブサイト</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">ソースコード</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">変更履歴</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Localization Labの全貢献者へ感謝</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">消えるメッセージ</string>
|
||||
<string name="disappearing_messages_explanation_long">この設定を有効にすると、
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
<string name="different_person_button">Kitas asmuo</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s ir %2$s išsiuntė jums tą pačią nuorodą.\n\nGali būti, kad vienas iš šių asmenų bando sužinoti kas yra jūsų adresatų sąraše.\n\nNesakykite šiems asmenims, kad gavote tokią pačią nuorodą iš kito asmens.</string>
|
||||
<string name="pending_contact_updated_toast">Laukiantis adresatas atnaujintas</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Supažindinkite savo adresatus</string>
|
||||
<string name="introduction_menu_item">Supažindinti</string>
|
||||
@@ -623,6 +624,10 @@
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Niekada</string>
|
||||
<string name="mailbox_status_unlink_button">Atsieti</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Apie</string>
|
||||
<string name="links">Nuorodos</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Išnykstančios žinutės</string>
|
||||
<string name="disappearing_messages_explanation_long">Įjungus šį nustatymą, naujos žinutės
|
||||
@@ -740,6 +745,8 @@
|
||||
<string name="hotspot_manual_site_address">Adresas (URL)</string>
|
||||
<string name="hotspot_qr_site">Jūsų telefonas teikia belaidį (Wi-Fi) prieigos tašką. Žmonės, prisijungę prie prieigos taško, gali atsisiųsti Briar, skenuodami šį QR kodą.</string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">Atsisiųsti Briar %s</string>
|
||||
<string name="website_download_button">Atsisiųsti Briar</string>
|
||||
<string name="website_download_outro">Kai atsisiuntimas pasibaigs, atverkite atsisiųstą failą ir jį įdiekite.</string>
|
||||
<string name="website_troubleshooting_title">Nesklandumų šalinimas</string>
|
||||
<string name="website_troubleshooting_1">Jei negalite atsisiųsti programėlės, pabandykite naudoti kitą saityno naršyklės programėlę.</string>
|
||||
|
||||
@@ -281,6 +281,8 @@
|
||||
<string name="different_person_button">နောက်တစ်ယောက် </string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$sနှင့်%2$s တူညီသောလင့်ခ်ကို သင့်အား ပေးပို့ခဲ့သည်။ \n\n သူတို့ထဲမှ တစ်ဦးက သင့်အဆက်အသွယ်များသည် မည်သူဖြစ်သည်ကို ရှာဖွေရန် ကြိုးစားနေပေမည်။ \n\nသင်သည် အခြားသူတစ်ဦးထံမှ တူညီသောလင့်ခ်ကို ရရှိထားကြောင်း ၎င်းတို့အား မပြောပါနှင့်။</string>
|
||||
<string name="pending_contact_updated_toast">ဆိုင်းငံ့ထားသော အဆက်အသွယ်ကို အပ်ဒိတ်လုပ်ပြီး</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_ourselves">ကျွနု်ပ်ကို</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">သင့်ရဲ့ အဆက်အသွယ်လိပ်စာများကို မိတ်ဆက်ပါ</string>
|
||||
<string name="introduction_menu_item">မိတ်ဆက်ခြင်း ပြုလုပ်ပါ</string>
|
||||
@@ -570,6 +572,9 @@
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">ဘယ်တော့မှ</string>
|
||||
<!--About-->
|
||||
<string name="about_title">အကြောင်း</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">ပျောက်ကွယ် မက်ဆေ့ချ်များ</string>
|
||||
<string name="disappearing_messages_explanation_long">၎င်းအပြင်အဆင်ကို ဖွင့်ထားလျှင်
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
<string name="different_person_button">Inna osoba</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$si %2$s wysłał ci ten sam link.\n\nJeden z nich może próbować odkryć, kim są twoje kontakty.\n\nNie mów im, że otrzymałeś ten sam link od kogoś innego.</string>
|
||||
<string name="pending_contact_updated_toast">Oczekujący kontakt zaktualizowany</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Przedstaw swoje kontakty.</string>
|
||||
<string name="introduction_onboarding_text">Przedstaw kontakty sobie nawzajem, aby mogły się połączyć w Briar.</string>
|
||||
@@ -645,6 +646,10 @@ Brak dostępu do aparatu. Spróbuj ponownie, może po ponownym uruchomieniu urz
|
||||
<string name="mailbox_error_wizard_answer1">Tak, mam teraz dostęp</string>
|
||||
<string name="mailbox_error_wizard_answer2">Nie w tej chwili, ale będę miał(a) dostęp później</string>
|
||||
<string name="mailbox_error_wizard_answer3">Nie, nie mam już dostępu do urządzenia.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">O programie...</string>
|
||||
<string name="links">Odnośniki</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Znikające wiadomości</string>
|
||||
<string name="disappearing_messages_explanation_long">Włączenie tego ustawienia spowoduje, że
|
||||
|
||||
@@ -301,6 +301,7 @@
|
||||
<string name="different_person_button">Pessoa diferente</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s e %2$s enviaram o mesmo link para você.\n\nUm deles pode estar tentando descobrir quem são seus contatos.\n\nNão diga a eles que você recebeu o mesmo link de outra pessoa.</string>
|
||||
<string name="pending_contact_updated_toast">Contato pendente atualizado</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Apresente seus contatos</string>
|
||||
<string name="introduction_onboarding_text">Apresente seus contatos entre si para que eles possam se conectar no Briar.</string>
|
||||
@@ -624,6 +625,10 @@
|
||||
<string name="mailbox_status_unlink_dialog_warning">Se você desvincular seu Mailbox, você não poderá receber mensagens enquanto o Briar estiver offline.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Seu Mailbox foi desvinculado</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">Na próxima vez que você acessar seu dispositivo de Mailbox, por favor, abra o app Mailbox e toque no botão \"Desvincular\" para completar o processo. \n\nSe você não tem mais acesso ao seu dispositivo de Mailbox, não se preocupe. Seus dados estão criptografados, logo, continuarão seguros mesmo que você não complete o processo.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Sobre</string>
|
||||
<string name="links">Linques</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Mensagens efêmeras</string>
|
||||
<string name="disappearing_messages_explanation_long">Ativar essa opção fará novas
|
||||
|
||||
@@ -259,9 +259,9 @@
|
||||
<string name="connection_error_title">Не удалось подключиться к контакту</string>
|
||||
<string name="connection_error_feedback">Если эта проблема сохраняется, пожалуйста <a href="feedback">отправьте отзыв</a>, чтобы помочь нам улучшить приложение.</string>
|
||||
<!--Adding Contacts Remotely-->
|
||||
<string name="add_contact_remotely_title_case">Добавление контакта на расстоянии</string>
|
||||
<string name="add_contact_remotely_title_case">Добавление контакта удаленно</string>
|
||||
<string name="add_contact_nearby_title">Добавить контакт поблизости</string>
|
||||
<string name="add_contact_remotely_title">Добавить контакт на расстоянии</string>
|
||||
<string name="add_contact_remotely_title"> Добавление контакта удаленно</string>
|
||||
<string name="contact_link_intro">Введите ссылку от вашего контакта здесь</string>
|
||||
<string name="contact_link_hint">Ссылка контакта</string>
|
||||
<string name="paste_button">Вставить</string>
|
||||
@@ -271,7 +271,7 @@
|
||||
<string name="send_link_title">Обмен ссылками</string>
|
||||
<string name="add_contact_choose_nickname">Выберите псевдоним</string>
|
||||
<string name="add_contact_choose_a_nickname">Введите псевдоним</string>
|
||||
<string name="nickname_intro">Дайте вашему контакту псевдоним. Увидеть его сможете только вы.</string>
|
||||
<string name="nickname_intro">Задайте этому контакту псевдоним. Его можете видеть только вы.</string>
|
||||
<string name="your_link">Передайте эту ссылку контакту, который вы хотите добавить.</string>
|
||||
<string name="link_clip_label">Ссылка Briar</string>
|
||||
<string name="link_copied_toast">Ссылка скопирована</string>
|
||||
@@ -317,6 +317,11 @@
|
||||
<string name="different_person_button">Другой человек</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s и %2$s отправили вам ту же ссылку.\n\nОдин из них, возможно, пытается выяснить, кто ваши контакты.\n\nНе говорите им, что вы получили такую же ссылку от кого-то еще.</string>
|
||||
<string name="pending_contact_updated_toast">Ожидающий контакт обновлен</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Неверифицированный контакт</string>
|
||||
<string name="peer_trust_level_verified">Верифицированный контакт</string>
|
||||
<string name="peer_trust_level_ourselves">Я</string>
|
||||
<string name="peer_trust_level_stranger">Незнакомец</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Представление ваших контактов</string>
|
||||
<string name="introduction_onboarding_text">Представьте ваши контакты друг другу, чтобы они могли общаться на Briar.</string>
|
||||
@@ -672,6 +677,16 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">Убедитесь, что устройство Mailbox корректно подключено к интернету.\n\nУбедитесь, что часы на устройстве Mailbox показывают правильное время, дату и часовой пояс.\n\nУбедитесь, что приложения Mailbox и Briar обновлены до последней версии.\n\nПерезагрузите устройства Mailbox и Briar и повторите попытку.</string>
|
||||
<string name="mailbox_error_wizard_info2">Пожалуйста, вернитесь к этому экрану, когда у вас будет доступ к устройству.</string>
|
||||
<string name="mailbox_error_wizard_info3">Пожалуйста, отвяжите свой Mailbox с помощью кнопки ниже.\n\nПосле отсоединения старого Mailbox вы в любое время сможете настроить новый.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">О проекте</string>
|
||||
<string name="briar_version">Версия Briar: %s</string>
|
||||
<string name="tor_version">Версия Tor: %s</string>
|
||||
<string name="links">Ссылки</string>
|
||||
<string name="briar_website">\u2022 <a href="">Сайт</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Исходный код</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Список изменений</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Благодарим всех участников проекта Localization Lab</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Исчезающие сообщения</string>
|
||||
<string name="disappearing_messages_explanation_long">Включение этого параметра приведет к созданию нового
|
||||
|
||||
@@ -298,6 +298,11 @@ dhe s’mund të hapet me këtë version.\n\nJu lutemi, përmirësojeni me versi
|
||||
<string name="different_person_button">Person i Ndryshëm</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s dhe %2$s ju dërguan të njëjtën lidhje.\n\nNjë prej tyre mund të jetë duke u rrekur të zbulojë cilët janë kontaktet tuaja.\n\nMos u thoni se morët të njëjtën lidhje nga dikush tjetër.</string>
|
||||
<string name="pending_contact_updated_toast">Kontakti pezull u përditësua</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Kontakt i paverifikuar</string>
|
||||
<string name="peer_trust_level_verified">Kontakt i verifikuar</string>
|
||||
<string name="peer_trust_level_ourselves">Unë</string>
|
||||
<string name="peer_trust_level_stranger">I huaj</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Prezantoni kontaktet tuaja</string>
|
||||
<string name="introduction_onboarding_text">Prezantoni kontaktet tuaj me njëri-tjetrin, që të mund të lidhen me Briar.</string>
|
||||
@@ -645,6 +650,16 @@ dhe s’mund të hapet me këtë version.\n\nJu lutemi, përmirësojeni me versi
|
||||
<string name="mailbox_error_wizard_info1_1_4">Shihni që pajisja kuti postare të jetë lidhur si duhet në Internet.\n\nKontrolloni që sahati te pajisja Kuti postare shfaq kohën, datën dhe zonën kohore të saktë.\n\nShihni që aplikacionet Kuti postare dhe Briar të jenë të përditësuar me versionin më të ri.\n\nRinisni pajisjet tuaja Kuti postare dhe Briar dhe riprovoni.</string>
|
||||
<string name="mailbox_error_wizard_info2">Ju lutemi, rikthejuni kësaj skene kur të keni mundësi të përdorni pajisjen.</string>
|
||||
<string name="mailbox_error_wizard_info3">Ju lutemi, shkëputeni kutinë tuaj postare duke përdorur butonin më poshtë.\n\nPasi të shkëputni Kutinë tuaj të vjetër postare, mund të ujdisni kurdo një Kuti të re postare.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Mbi</string>
|
||||
<string name="briar_version">Version Briar-i: %s</string>
|
||||
<string name="tor_version">Version Tor-i: %s</string>
|
||||
<string name="links">Lidhje</string>
|
||||
<string name="briar_website">\u2022 <a href="">Sajt</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Kod burim</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Regjistër ndryshimesh</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Faleminderit krejt kontribuesve prej Localization Lab</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Mesazhe që treten</string>
|
||||
<string name="disappearing_messages_explanation_long">Aktivizimi i këtij rregullimi do të bëjë që
|
||||
|
||||
@@ -292,6 +292,7 @@ Vänlige installera Briar på en nyare enhet.</string>
|
||||
<string name="different_person_button">Olika personer</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s och %2$s skickade dig samma länk.\n\nEn av dem kanske försöker ta reda på vilka dina kontakter är.\n\nBerätta inte för dem att du fått samma länk från någon annan.</string>
|
||||
<string name="pending_contact_updated_toast">Väntande kontakt har uppdaterats</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Presentera dina kontakter</string>
|
||||
<string name="introduction_onboarding_text">Introducera dina kontakter för varandra så att de kan ansluta sig till varandra via Briar.</string>
|
||||
@@ -606,6 +607,10 @@ Vänlige installera Briar på en nyare enhet.</string>
|
||||
<!--Indicates that there never was a connection to the mailbox. Last connection: Never-->
|
||||
<string name="mailbox_status_connected_never">Aldrig</string>
|
||||
<string name="mailbox_status_unlink_button">Avlänka</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Om</string>
|
||||
<string name="links">Länkar</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Försvinnande meddelanden</string>
|
||||
<string name="disappearing_messages_explanation_long">Att slå på denna inställning kommer att få nya
|
||||
|
||||
@@ -291,6 +291,11 @@
|
||||
<string name="different_person_button">Farklı Kişi</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s ve %2$s size aynı bağlantıyı gönderdi.\n\nAralarından biri kişilerinizin kim olduğunu keşfetmeye çalışıyor olabilir.\n\nOnlara başkasından aynı bağlantıyı aldığınızı söylemeyin.</string>
|
||||
<string name="pending_contact_updated_toast">Bekleyen kişi güncellendi</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Doğrulanmamış kişi</string>
|
||||
<string name="peer_trust_level_verified">Doğrulanmış kişi</string>
|
||||
<string name="peer_trust_level_ourselves">Benim</string>
|
||||
<string name="peer_trust_level_stranger">Yabancı</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Kişilerinizi tanıştırın</string>
|
||||
<string name="introduction_onboarding_text">Kişilerinizi birbirlerine tanıtın, böylece Briar üzerinden bağlantı kurabilirler.</string>
|
||||
@@ -599,6 +604,10 @@
|
||||
<string name="mailbox_status_connected_title">Mailbox çalışıyor</string>
|
||||
<string name="mailbox_status_problem_title">Briar Mailbox\'a bağlantı kurmakta zorluk yaşıyor.</string>
|
||||
<string name="mailbox_status_failure_title">Mailbox kullanılamıyor</string>
|
||||
<string name="mailbox_status_app_too_old_title">Briar çok eski</string>
|
||||
<string name="mailbox_status_app_too_old_message">Briar\'ı uygulamanın son sürümüne güncelleyin ve tekrar deneyin.</string>
|
||||
<string name="mailbox_status_mailbox_too_old_title">Mailbox çok eski</string>
|
||||
<string name="mailbox_status_mailbox_too_old_message">Mailbox\'unuzu uygulamanın son sürümüne güncelleyin ve tekrar deneyin.</string>
|
||||
<string name="mailbox_status_check_button">Bağlantıyı denetle</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Son bağlantı: %s</string>
|
||||
@@ -610,6 +619,37 @@
|
||||
<string name="mailbox_status_unlink_dialog_warning">Mailbox bağlantısını kaldırırsanız, Briar çevrimdışı iken ileti alamayacaksınız.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Mailbox bağlantısı kaldırıldı.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">Mailbox aygıtınıza sonraki erişiminizde lütfen Mailbox uygulamasını açın ve \"Bağlantıyı Kaldır\" düğmesine dokunarak işlemi tamamlayın.\n\nEğer Mailbox aygıtınıza artık erişiminiz yoksa endişelenmeyin. Veriniz şifrelenmiştir ve bu süreci tamamlamasanız bile güvenli kalacaktır.</string>
|
||||
<string name="mailbox_error_notification_channel_title">Briar Mailbox sorunu</string>
|
||||
<string name="mailbox_error_notification_title">Briar Mailbox kullanılamıyor</string>
|
||||
<string name="mailbox_error_notification_text">Sorunu çözmek için dokunun</string>
|
||||
<string name="mailbox_error_wizard_button">Sorunu çöz</string>
|
||||
<string name="mailbox_error_wizard_title">Mailbox sorun çözme sihirbazı</string>
|
||||
<string name="mailbox_error_wizard_question1">Mailbox aygıtınıza erişiminiz var mı?</string>
|
||||
<string name="mailbox_error_wizard_answer1">Evet, şu an ona erişimim var.</string>
|
||||
<string name="mailbox_error_wizard_answer2">Şimdi değil ama daha sonra erişebilirim.</string>
|
||||
<string name="mailbox_error_wizard_answer3">Hayır, artık ona erişimim yok.</string>
|
||||
<string name="mailbox_error_wizard_info1_1">Mailbox aygıtının açık ve İnternet\'e bağlı olduğunu denetleyin.</string>
|
||||
<string name="mailbox_error_wizard_question1_1">Mailbox uygulamasını açın. Ne görüyorsunuz?</string>
|
||||
<string name="mailbox_error_wizard_answer1_1">Mailbox kurulumu yönergelerini görüyorum.</string>
|
||||
<string name="mailbox_error_wizard_answer1_2">Bir Karekod görüyorum.</string>
|
||||
<string name="mailbox_error_wizard_answer1_3">\"Mailbox çalışıyor\" görüyorum</string>
|
||||
<string name="mailbox_error_wizard_answer1_4">\"Aygıt çevrimdışı\" görüyorum</string>
|
||||
<string name="mailbox_error_wizard_info1_1_1">Lütfen Mailbox\'ınızın bağlantısını aşağıdaki düğmeyi kullanarak kesin, daha sonra tekrar bağlamak için Mailbox aygıtındaki yönergeleri izleyin.</string>
|
||||
<string name="mailbox_error_wizard_info_1_1_2">Lütfen Mailbox\'ınızın bağlantısını aşağıdaki düğmeyi kullanarak kesin, daha sonra tekrar bağlamak için Karekodu tarayın.</string>
|
||||
<string name="mailbox_error_wizard_info1_1_3">Briar ve Mailbox arasındaki bağlantıyı denetlemek için aşağıdaki düğmeyi kullanın.\n\n
|
||||
Eğer bağlantı tekrar başarısız olursa:\n
|
||||
\u2022 Mailbox ve Briar\'ın son sürüme güncellendiğini denetleyin.\n
|
||||
Mailbox ve Briar aygıtlarını yeniden başlatın ve tekrar deneyin.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Hakkında</string>
|
||||
<string name="briar_version">Briar sürümü: %s</string>
|
||||
<string name="tor_version">Tor sürümü: %s</string>
|
||||
<string name="links">Bağlantılar</string>
|
||||
<string name="briar_website">\u2022 <a href="">Web Site</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Kaynak kod</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Değişiklik günlüğü</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">Localization Lab\'taki tüm katkı sağlayanlara teşekkürler</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Kaybolan iletiler</string>
|
||||
<string name="disappearing_messages_explanation_long">Bu ayarı etkinleştirmeniz durumunda
|
||||
@@ -725,6 +765,9 @@
|
||||
<string name="hotspot_manual_site_address">Adres (URL)</string>
|
||||
<string name="hotspot_qr_site">Telefonunuz bir kablosuz erişim noktası sunuyor. Erişim noktasına bağlı kişiler Briar\'ı QR kodunu tarayarak indirebilirler. </string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">Briar\'ı indir %s</string>
|
||||
<string name="website_download_intro_1">Yakınlarınızdaki biri sizinle Briar\'ı paylaştı</string>
|
||||
<string name="website_download_button">Briar\'ı indir</string>
|
||||
<string name="website_download_outro">İndirme tamamlandıktan sonra, indirdiğiniz dosyayı açın ve kurun.</string>
|
||||
<string name="website_troubleshooting_title">Sorun çözme</string>
|
||||
<string name="website_troubleshooting_1">Uygulamayı indiremiyorsanız, başka bir tarayıcı uygulamasıyla deneyin.</string>
|
||||
|
||||
@@ -311,6 +311,7 @@
|
||||
<string name="different_person_button">Різні особи</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s та %2$s надіслали вам однакові посилання.\n\nХтось із них, можливо, намагається виявити ваші контакти.\n\nНе кажіть їм, що вже отримували це посилання від когось іще.</string>
|
||||
<string name="pending_contact_updated_toast">Оновлено нерозглянутий контакт</string>
|
||||
<!--Peer trust levels-->
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Поділитися своїми контактами</string>
|
||||
<string name="introduction_onboarding_text">Представте свої контакти навзаєм, щоб вони могли спілкуватись у Briar.</string>
|
||||
@@ -636,6 +637,10 @@
|
||||
<string name="mailbox_error_wizard_question1">Ви маєте доступ до вашого пристрою Mailbox?</string>
|
||||
<string name="mailbox_error_wizard_answer1_1">Я бачу вказівки щодо налаштування Mailbox</string>
|
||||
<string name="mailbox_error_wizard_answer1_2">Я бачу QR-код</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Про</string>
|
||||
<string name="links">Посилання</string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Самознищувані повідомлення</string>
|
||||
<string name="disappearing_messages_explanation_long">Коли цей параметр увімкнено, нові
|
||||
|
||||
@@ -287,6 +287,11 @@
|
||||
<string name="different_person_button">不同的人</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s 和 %2$s 给你发送了同样的链接。\n\n其中一个人可能企图找出谁是你的联系人。\n\n 不要告诉他们你从其他人那里收到了相同的链接。</string>
|
||||
<string name="pending_contact_updated_toast">待处理联系人已更新</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">未验证的联系人</string>
|
||||
<string name="peer_trust_level_verified">已验证的联系人</string>
|
||||
<string name="peer_trust_level_ourselves">我</string>
|
||||
<string name="peer_trust_level_stranger">陌生人</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">介绍您的联系人</string>
|
||||
<string name="introduction_onboarding_text">向你的联系人互相介绍彼此,这样他们可以在 Briar 上建立联系</string>
|
||||
@@ -630,6 +635,16 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">检查装有 Mailbox 的设备是否已正确连接到互联网。\n\n检查装有 Mailboxde 设备上的时钟是否显示正确的时间、日期和时区。\n\n检查 Mailbox 和 Briar 应用是否更新到最新版本。\n\n重新启动装有 Mailbox 和Briar 应用的设备,然后重试。</string>
|
||||
<string name="mailbox_error_wizard_info2">当你可以使用该设备时,请回到此屏幕。</string>
|
||||
<string name="mailbox_error_wizard_info3">请使用下面的按钮取消你的 Mailbox 链接。\n\n在取消对旧 Mailbox 的链接后,你可以随时设置一个新的 Mailbox。</string>
|
||||
<!--About-->
|
||||
<string name="about_title">关于</string>
|
||||
<string name="briar_version">Briar 版本:%s</string>
|
||||
<string name="tor_version">Tor 版本:%s</string>
|
||||
<string name="links">链接</string>
|
||||
<string name="briar_website">\u2022 <a href="">网站</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">源代码</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">变更记录</a></string>
|
||||
<!--Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry")-->
|
||||
<string name="translator_thanks">感谢所有 Localization Lab 的贡献者们</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">让消息自动消失</string>
|
||||
<string name="disappearing_messages_explanation_long">打开此设置将使
|
||||
|
||||
@@ -314,6 +314,13 @@
|
||||
<string name="different_person_button">Different Person</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s and %2$s sent you the same link.\n\nOne of them may be trying to discover who your contacts are.\n\nDon\'t tell them you received the same link from someone else.</string>
|
||||
<string name="pending_contact_updated_toast">Pending contact updated</string>
|
||||
|
||||
<!-- Peer trust levels -->
|
||||
|
||||
<string name="peer_trust_level_unverified">Unverified contact</string>
|
||||
<string name="peer_trust_level_verified">Verified contact</string>
|
||||
<string name="peer_trust_level_ourselves">Me</string>
|
||||
<string name="peer_trust_level_stranger">Stranger</string>
|
||||
|
||||
<!-- Introductions -->
|
||||
|
||||
@@ -684,6 +691,17 @@
|
||||
<string name="mailbox_error_wizard_info2">Please come back to this screen when you have access to the device.</string>
|
||||
<string name="mailbox_error_wizard_info3">Please unlink your mailbox using the button below.\n\nAfter unlinking your old Mailbox, you can set up a new Mailbox at any time.</string>
|
||||
|
||||
<!-- About -->
|
||||
<string name="about_title">About</string>
|
||||
<string name="briar_version">Briar version: %s</string>
|
||||
<string name="tor_version">Tor version: %s</string>
|
||||
<string name="links">Links</string>
|
||||
<string name="briar_website">\u2022 <a href="">Website</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Source code</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Changelog</a></string>
|
||||
<!-- Here translators can add their names or Transifex usernames(eg "Thanks to all the contributors at the Localization Lab, especially Tom, Matthew and Jerry") -->
|
||||
<string name="translator_thanks">Thanks to all the contributors at the Localization Lab</string>
|
||||
|
||||
<!-- Conversation Settings -->
|
||||
<string name="disappearing_messages_title">Disappearing messages</string>
|
||||
<string name="disappearing_messages_explanation_long">Turning on this setting will make new
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
android:title="@string/mailbox_settings_title"
|
||||
app:icon="@drawable/ic_mailbox" />
|
||||
|
||||
<Preference
|
||||
android:title="@string/about_title"
|
||||
app:fragment="org.briarproject.briar.android.settings.AboutFragment"
|
||||
app:icon="@drawable/ic_info_dark" />
|
||||
|
||||
<PreferenceCategory
|
||||
android:key="pref_key_actions"
|
||||
android:layout="@layout/preferences_category"
|
||||
|
||||
Reference in New Issue
Block a user