mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
79 Commits
2343-mailb
...
beta-1.4.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
559138c5b6 | ||
|
|
f90aef7767 | ||
|
|
ee417fc8d2 | ||
|
|
b424d6f98e | ||
|
|
32205ca6d3 | ||
|
|
dd3a9aa71b | ||
|
|
4e59836dd0 | ||
|
|
264b2ca2f3 | ||
|
|
23f5de66a8 | ||
|
|
79aa42c0f8 | ||
|
|
f00c3a47f5 | ||
|
|
04011e50bc | ||
|
|
7d20a844ff | ||
|
|
43581cc339 | ||
|
|
34815eb1a5 | ||
|
|
13d9e93758 | ||
|
|
98c1dca602 | ||
|
|
5ceba8f508 | ||
|
|
8e5ec347f2 | ||
|
|
f3afcb8469 | ||
|
|
3a317a9144 | ||
|
|
480a4b5901 | ||
|
|
6d9a241820 | ||
|
|
1c656d217c | ||
|
|
a503aa6ed2 | ||
|
|
85361b0099 | ||
|
|
4efdb7b75b | ||
|
|
787200d03f | ||
|
|
3ac05e4b88 | ||
|
|
7aafbdd715 | ||
|
|
617a6db84c | ||
|
|
2c295fb096 | ||
|
|
4af895d124 | ||
|
|
3cd388decd | ||
|
|
08551d16cd | ||
|
|
d905cb6cda | ||
|
|
bcc7a4b93b | ||
|
|
4fe9fa3315 | ||
|
|
079ef5b3c0 | ||
|
|
de76986ee4 | ||
|
|
96630e1b34 | ||
|
|
4eddf625d8 | ||
|
|
28ad66a03d | ||
|
|
0af371d026 | ||
|
|
a57c784b47 | ||
|
|
ab360e1e25 | ||
|
|
2aa39e43ef | ||
|
|
efb294de53 | ||
|
|
99755619c5 | ||
|
|
9990fb3b8f | ||
|
|
6d26db3d66 | ||
|
|
51301968a5 | ||
|
|
feb1c1b655 | ||
|
|
148f61a6b5 | ||
|
|
24d4debde0 | ||
|
|
a1f25c8101 | ||
|
|
62883b4bde | ||
|
|
42243f73f4 | ||
|
|
f4365330cb | ||
|
|
d3a06cf2c0 | ||
|
|
15d29f6189 | ||
|
|
339e4daded | ||
|
|
217a6dbf1c | ||
|
|
46352f664c | ||
|
|
dfcd626081 | ||
|
|
347895f6b2 | ||
|
|
7a6d075984 | ||
|
|
68ab3b0e97 | ||
|
|
16fc4f4527 | ||
|
|
8657216345 | ||
|
|
42e2926d61 | ||
|
|
a261b8e739 | ||
|
|
1699d6b5f8 | ||
|
|
848872a803 | ||
|
|
04ed3a652a | ||
|
|
d20457f338 | ||
|
|
ab29aacce0 | ||
|
|
46bb2b8ec2 | ||
|
|
7a888a6114 |
@@ -98,7 +98,7 @@ bridge test:
|
||||
allow_failure: true
|
||||
script:
|
||||
- OPTIONAL_TESTS=org.briarproject.bramble.plugin.tor.BridgeTest ./gradlew --info bramble-java:test --tests BridgeTest
|
||||
timeout: 3h
|
||||
timeout: 4h
|
||||
|
||||
mailbox integration test:
|
||||
extends: .optional_tests
|
||||
|
||||
@@ -15,8 +15,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
versionCode 10412
|
||||
versionName "1.4.12"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
@@ -42,8 +42,10 @@ configurations {
|
||||
|
||||
dependencies {
|
||||
implementation project(path: ':bramble-core', configuration: 'default')
|
||||
implementation 'androidx.annotation:annotation:1.5.0'
|
||||
tor "org.briarproject:tor-android:$tor_version"
|
||||
tor "org.briarproject:obfs4proxy-android:$obfs4proxy_version"
|
||||
tor "org.briarproject:snowflake-android:$snowflake_version"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
|
||||
@@ -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 * { *; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.battery.AndroidBatteryModule;
|
||||
import org.briarproject.bramble.io.DnsModule;
|
||||
import org.briarproject.bramble.network.AndroidNetworkModule;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.reporting.ReportingModule;
|
||||
@@ -18,6 +19,7 @@ import dagger.Module;
|
||||
AndroidTaskSchedulerModule.class,
|
||||
AndroidWakefulIoExecutorModule.class,
|
||||
CircumventionModule.class,
|
||||
DnsModule.class,
|
||||
ReportingModule.class,
|
||||
SocksModule.class
|
||||
})
|
||||
|
||||
@@ -31,6 +31,8 @@ import java.util.zip.ZipInputStream;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import androidx.annotation.ChecksSdkIntAtLeast;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -45,13 +47,14 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
|
||||
private static final String TOR_LIB_NAME = "libtor.so";
|
||||
private static final String OBFS4_LIB_NAME = "libobfs4proxy.so";
|
||||
private static final String SNOWFLAKE_LIB_NAME = "libsnowflake.so";
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(AndroidTorPlugin.class.getName());
|
||||
|
||||
private final Application app;
|
||||
private final AndroidWakeLock wakeLock;
|
||||
private final File torLib, obfs4Lib;
|
||||
private final File torLib, obfs4Lib, snowflakeLib;
|
||||
|
||||
AndroidTorPlugin(Executor ioExecutor,
|
||||
Executor wakefulIoExecutor,
|
||||
@@ -83,6 +86,7 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
String nativeLibDir = app.getApplicationInfo().nativeLibraryDir;
|
||||
torLib = new File(nativeLibDir, TOR_LIB_NAME);
|
||||
obfs4Lib = new File(nativeLibDir, OBFS4_LIB_NAME);
|
||||
snowflakeLib = new File(nativeLibDir, SNOWFLAKE_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -108,6 +112,12 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
if (!enable) wakeLock.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ChecksSdkIntAtLeast(api = 25)
|
||||
protected boolean canVerifyLetsEncryptCerts() {
|
||||
return SDK_INT >= 25;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
super.stop();
|
||||
@@ -124,39 +134,43 @@ class AndroidTorPlugin extends TorPlugin {
|
||||
return obfs4Lib.exists() ? obfs4Lib : super.getObfs4ExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected File getSnowflakeExecutableFile() {
|
||||
return snowflakeLib.exists()
|
||||
? snowflakeLib : super.getSnowflakeExecutableFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installTorExecutable() throws IOException {
|
||||
File extracted = super.getTorExecutableFile();
|
||||
if (torLib.exists()) {
|
||||
// If an older version left behind a Tor binary, delete it
|
||||
if (extracted.exists()) {
|
||||
if (extracted.delete()) LOG.info("Deleted Tor binary");
|
||||
else LOG.info("Failed to delete Tor binary");
|
||||
}
|
||||
} else if (SDK_INT < 29) {
|
||||
// The binary wasn't extracted at install time. Try to extract it
|
||||
extractLibraryFromApk(TOR_LIB_NAME, extracted);
|
||||
} else {
|
||||
// No point extracting the binary, we won't be allowed to execute it
|
||||
throw new FileNotFoundException(torLib.getAbsolutePath());
|
||||
}
|
||||
installExecutable(super.getTorExecutableFile(), torLib, TOR_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installObfs4Executable() throws IOException {
|
||||
File extracted = super.getObfs4ExecutableFile();
|
||||
if (obfs4Lib.exists()) {
|
||||
// If an older version left behind an obfs4 binary, delete it
|
||||
installExecutable(super.getObfs4ExecutableFile(), obfs4Lib,
|
||||
OBFS4_LIB_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void installSnowflakeExecutable() throws IOException {
|
||||
installExecutable(super.getSnowflakeExecutableFile(), snowflakeLib,
|
||||
SNOWFLAKE_LIB_NAME);
|
||||
}
|
||||
|
||||
private void installExecutable(File extracted, File lib, String libName)
|
||||
throws IOException {
|
||||
if (lib.exists()) {
|
||||
// If an older version left behind a binary, delete it
|
||||
if (extracted.exists()) {
|
||||
if (extracted.delete()) LOG.info("Deleted obfs4 binary");
|
||||
else LOG.info("Failed to delete obfs4 binary");
|
||||
if (extracted.delete()) LOG.info("Deleted old binary");
|
||||
else LOG.info("Failed to delete old binary");
|
||||
}
|
||||
} else if (SDK_INT < 29) {
|
||||
// The binary wasn't extracted at install time. Try to extract it
|
||||
extractLibraryFromApk(OBFS4_LIB_NAME, extracted);
|
||||
extractLibraryFromApk(libName, extracted);
|
||||
} else {
|
||||
// No point extracting the binary, we won't be allowed to execute it
|
||||
throw new FileNotFoundException(obfs4Lib.getAbsolutePath());
|
||||
throw new FileNotFoundException(lib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'androidx.annotation:annotation:1.5.0:annotation-1.5.0.jar:261fb7c0210858500bab66d34354972a75166ab4182add283780b05513d6ec4a',
|
||||
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db',
|
||||
'com.android.tools.analytics-library:protos:30.0.3:protos-30.0.3.jar:f62b89dcd9de719c6a7b7e15fb1dd20e45b57222e675cf633607bd0ed6bca7e7',
|
||||
'com.android.tools.analytics-library:shared:30.0.3:shared-30.0.3.jar:05aa9ba3cc890354108521fdf99802565aae5dd6ca44a6ac8bb8d594d1c1cd15',
|
||||
@@ -87,8 +88,9 @@ dependencyVerification {
|
||||
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
|
||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||
'org.briarproject:obfs4proxy-android:0.0.12:obfs4proxy-android-0.0.12.jar:84159d2a4668abc40e3fccaa1f6fa0c04892863f9eb80a866ac8928d9f9a7e89',
|
||||
'org.briarproject:tor-android:0.4.5.12-2:tor-android-0.4.5.12-2.jar:8545dbcef2bb6aa89c32bb6f8ac51f7a64bce3ae85845b3578ffdeb9b206feb9',
|
||||
'org.briarproject:obfs4proxy-android:0.0.14:obfs4proxy-android-0.0.14.jar:ad9b1ee4757b05867a19e993147bbb018bddd1f26ce3da746d5f037d5991a8c8',
|
||||
'org.briarproject:snowflake-android:2.3.1:snowflake-android-2.3.1.jar:1f83c9a070f87b7074af13627709a8b5aced5460104be7166af736b1bb73c293',
|
||||
'org.briarproject:tor-android:0.4.5.14:tor-android-0.4.5.14.jar:7cf1beaa6c1db51fc8fac263aba9624ef289c3db29772509efcbc59f7057330a',
|
||||
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
|
||||
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||
'org.checkerframework:checker-qual:3.5.0:checker-qual-3.5.0.jar:729990b3f18a95606fc2573836b6958bcdb44cb52bfbd1b7aa9c339cff35a5a4',
|
||||
@@ -129,10 +131,12 @@ dependencyVerification {
|
||||
'org.jetbrains.kotlin:kotlin-reflect:1.4.32:kotlin-reflect-1.4.32.jar:dbf19e9cdaa9c3c170f3f6f6ce3922f38dfc1d7fa1cab5b7c23a19da8b5eec5b',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.20:kotlin-stdlib-common-1.4.20.jar:a7112c9b3cefee418286c9c9372f7af992bd1e6e030691d52f60cb36dbec8320',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32:kotlin-stdlib-common-1.4.32.jar:e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.7.10:kotlin-stdlib-common-1.7.10.jar:19f102efe9629f8eabc63853ad15c533e47c47f91fca09285c5bde86e59f91d4',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.4.32:kotlin-stdlib-jdk7-1.4.32.jar:5f801e75ca27d8791c14b07943c608da27620d910a8093022af57f543d5d98b6',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.32:kotlin-stdlib-jdk8-1.4.32.jar:adc43e54757b106e0cd7b3b7aa257dff471b61efdabe067fc02b2f57e2396262',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.20:kotlin-stdlib-1.4.20.jar:b8ab1da5cdc89cb084d41e1f28f20a42bd431538642a5741c52bbfae3fa3e656',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.4.32:kotlin-stdlib-1.4.32.jar:13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba',
|
||||
'org.jetbrains.kotlin:kotlin-stdlib:1.7.10:kotlin-stdlib-1.7.10.jar:e771fe74250a943e8f6346713201ff1d8cb95c3a5d1a91a22b65a9e04f6a8901',
|
||||
'org.jetbrains.kotlinx:kotlinx-metadata-jvm:0.1.0:kotlinx-metadata-jvm-0.1.0.jar:9753bb39efef35957c5c15df9a3cb769aabf2cdfa74b47afcb7760e5146be3b5',
|
||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09',
|
||||
|
||||
@@ -283,6 +283,13 @@ public interface DatabaseComponent extends TransactionManager {
|
||||
*/
|
||||
Group getGroup(Transaction txn, GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the ID of the group containing the given message.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
GroupId getGroupId(Transaction txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for the given group.
|
||||
* <p/>
|
||||
@@ -349,13 +356,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 +499,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,19 +1,22 @@
|
||||
package org.briarproject.bramble.api.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_CLIENT_TOO_OLD;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.API_SERVER_TOO_OLD;
|
||||
|
||||
class MailboxHelper {
|
||||
@NotNullByDefault
|
||||
public class MailboxHelper {
|
||||
|
||||
/**
|
||||
* Returns the highest major version that both client and server support
|
||||
* or {@link MailboxConstants#API_SERVER_TOO_OLD} if the server is too old
|
||||
* or {@link MailboxConstants#API_CLIENT_TOO_OLD} if the client is too old.
|
||||
*/
|
||||
static int getHighestCommonMajorVersion(
|
||||
public static int getHighestCommonMajorVersion(
|
||||
List<MailboxVersion> client, List<MailboxVersion> server) {
|
||||
TreeSet<Integer> clientVersions = new TreeSet<>();
|
||||
for (MailboxVersion version : client) {
|
||||
@@ -32,4 +35,13 @@ class MailboxHelper {
|
||||
return API_SERVER_TOO_OLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a client and server with the given API versions can
|
||||
* communicate with each other (ie, have any major API versions in common).
|
||||
*/
|
||||
public static boolean isClientCompatibleWithServer(
|
||||
List<MailboxVersion> client, List<MailboxVersion> server) {
|
||||
int common = getHighestCommonMajorVersion(client, server);
|
||||
return common != API_CLIENT_TOO_OLD && common != API_SERVER_TOO_OLD;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,20 +46,28 @@ public interface MailboxSettingsManager {
|
||||
|
||||
interface MailboxHook {
|
||||
/**
|
||||
* Called when Briar is paired with a mailbox
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Called when the mailbox is unpaired
|
||||
* Called when the mailbox is unpaired.
|
||||
*
|
||||
* @param txn A read-write transaction
|
||||
*/
|
||||
void mailboxUnpaired(Transaction txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Called when we receive our mailbox's server-supported API versions.
|
||||
* This happens whenever we successfully check the connectivity of
|
||||
* our mailbox, so this hook may be called frequently.
|
||||
*
|
||||
* @param txn A read-write transaction
|
||||
*/
|
||||
void serverSupportedVersionsReceived(Transaction txn,
|
||||
List<MailboxVersion> serverSupports) 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.
|
||||
*/
|
||||
|
||||
@@ -79,6 +79,12 @@ public interface MailboxUpdateManager {
|
||||
*/
|
||||
String GROUP_KEY_SENT_CLIENT_SUPPORTS = "sentClientSupports";
|
||||
|
||||
/**
|
||||
* Key in the client's local group for storing the serverSupports list that
|
||||
* was last sent out, if any.
|
||||
*/
|
||||
String GROUP_KEY_SENT_SERVER_SUPPORTS = "sentServerSupports";
|
||||
|
||||
/**
|
||||
* Returns the latest {@link MailboxUpdate} sent to the given contact.
|
||||
* <p>
|
||||
|
||||
@@ -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,40 @@
|
||||
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.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
* An event that is broadcast when the first mailbox update is sent to a
|
||||
* newly added contact, which happens in the same transaction in which the
|
||||
* contact is added.
|
||||
* <p>
|
||||
* This event is not broadcast when the first mailbox update is sent to an
|
||||
* existing contact when setting up the
|
||||
* {@link MailboxUpdateManager mailbox update client}.
|
||||
*/
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class MailboxUpdateSentToNewContactEvent extends Event {
|
||||
|
||||
private final ContactId contactId;
|
||||
private final MailboxUpdate mailboxUpdate;
|
||||
|
||||
public MailboxUpdateSentToNewContactEvent(ContactId contactId,
|
||||
MailboxUpdate mailboxUpdate) {
|
||||
this.contactId = contactId;
|
||||
this.mailboxUpdate = mailboxUpdate;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public MailboxUpdate getMailboxUpdate() {
|
||||
return mailboxUpdate;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,14 @@
|
||||
package org.briarproject.bramble.api.sync.event;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.Group.Visibility;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
/**
|
||||
@@ -14,12 +19,32 @@ import javax.annotation.concurrent.Immutable;
|
||||
public class MessageSharedEvent extends Event {
|
||||
|
||||
private final MessageId messageId;
|
||||
private final GroupId groupId;
|
||||
private final Map<ContactId, Boolean> groupVisibility;
|
||||
|
||||
public MessageSharedEvent(MessageId message) {
|
||||
public MessageSharedEvent(MessageId message, GroupId groupId,
|
||||
Map<ContactId, Boolean> groupVisibility) {
|
||||
this.messageId = message;
|
||||
this.groupId = groupId;
|
||||
this.groupVisibility = groupVisibility;
|
||||
}
|
||||
|
||||
public MessageId getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IDs of all contacts for which the visibility of the
|
||||
* message's group is either {@link Visibility#SHARED shared} or
|
||||
* {@link Visibility#VISIBLE visible}. The value in the map is true if the
|
||||
* group is {@link Visibility#SHARED shared} or false if the group is
|
||||
* {@link Visibility#VISIBLE visible}.
|
||||
*/
|
||||
public Map<ContactId, Boolean> getGroupVisibility() {
|
||||
return groupVisibility;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -320,6 +320,13 @@ interface Database<T> {
|
||||
*/
|
||||
Group getGroup(T txn, GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the ID of the group containing the given message.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
GroupId getGroupId(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the metadata for the given group.
|
||||
* <p/>
|
||||
@@ -345,8 +352,11 @@ interface Database<T> {
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all contacts to which the given group's visibility is
|
||||
* either {@link Visibility VISIBLE} or {@link Visibility SHARED}.
|
||||
* Returns the IDs of all contacts for which the given group's visibility
|
||||
* is either {@link Visibility#SHARED shared} or
|
||||
* {@link Visibility#VISIBLE visible}. The value in the map is true if the
|
||||
* group is {@link Visibility#SHARED shared} or false if the group is
|
||||
* {@link Visibility#VISIBLE visible}.
|
||||
* <p/>
|
||||
* Read-only.
|
||||
*/
|
||||
|
||||
@@ -287,7 +287,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
transaction.attach(new MessageAddedEvent(m, null));
|
||||
transaction.attach(new MessageStateChangedEvent(m.getId(), true,
|
||||
DELIVERED));
|
||||
if (shared) transaction.attach(new MessageSharedEvent(m.getId()));
|
||||
if (shared) {
|
||||
Map<ContactId, Boolean> visibility =
|
||||
db.getGroupVisibility(txn, m.getGroupId());
|
||||
transaction.attach(new MessageSharedEvent(m.getId(),
|
||||
m.getGroupId(), visibility));
|
||||
}
|
||||
}
|
||||
db.mergeMessageMetadata(txn, m.getId(), meta);
|
||||
}
|
||||
@@ -550,6 +555,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
return db.getGroup(txn, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupId getGroupId(Transaction transaction, MessageId m)
|
||||
throws DbException {
|
||||
T txn = unbox(transaction);
|
||||
if (!db.containsMessage(txn, m))
|
||||
throw new NoSuchMessageException();
|
||||
return db.getGroupId(txn, m);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Metadata getGroupMetadata(Transaction transaction, GroupId g)
|
||||
throws DbException {
|
||||
@@ -620,11 +634,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 +760,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 +1113,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
|
||||
@@ -1184,7 +1196,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
if (db.getMessageState(txn, m) != DELIVERED)
|
||||
throw new IllegalArgumentException("Shared undelivered message");
|
||||
db.setMessageShared(txn, m, true);
|
||||
transaction.attach(new MessageSharedEvent(m));
|
||||
GroupId g = db.getGroupId(txn, m);
|
||||
Map<ContactId, Boolean> visibility = db.getGroupVisibility(txn, g);
|
||||
transaction.attach(new MessageSharedEvent(m, g, visibility));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1683,6 +1683,27 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupId getGroupId(Connection txn, MessageId m) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT groupId FROM messages WHERE messageId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, m.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
if (!rs.next()) throw new DbStateException();
|
||||
GroupId g = new GroupId(rs.getBytes(1));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return g;
|
||||
} catch (SQLException e) {
|
||||
tryToClose(rs, LOG, WARNING);
|
||||
tryToClose(ps, LOG, WARNING);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Group> getGroups(Connection txn, ClientId c,
|
||||
int majorVersion) throws DbException {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.feed;
|
||||
package org.briarproject.bramble.io;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -1,4 +1,6 @@
|
||||
package org.briarproject.briar.feed;
|
||||
package org.briarproject.bramble.io;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
@@ -9,6 +11,7 @@ import javax.inject.Inject;
|
||||
|
||||
import okhttp3.Dns;
|
||||
|
||||
@NotNullByDefault
|
||||
class NoDns implements Dns {
|
||||
|
||||
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||
@@ -55,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());
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -245,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();
|
||||
|
||||
@@ -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,52 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
@NotNullByDefault
|
||||
class MailboxClientFactoryImpl implements MailboxClientFactory {
|
||||
|
||||
private final MailboxWorkerFactory workerFactory;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final Executor ioExecutor;
|
||||
private final Provider<ContactMailboxConnectivityChecker>
|
||||
contactCheckerProvider;
|
||||
private final Provider<OwnMailboxConnectivityChecker> ownCheckerProvider;
|
||||
|
||||
@Inject
|
||||
MailboxClientFactoryImpl(MailboxWorkerFactory workerFactory,
|
||||
TaskScheduler taskScheduler,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
Provider<ContactMailboxConnectivityChecker> contactCheckerProvider,
|
||||
Provider<OwnMailboxConnectivityChecker> ownCheckerProvider) {
|
||||
this.workerFactory = workerFactory;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.ioExecutor = ioExecutor;
|
||||
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, taskScheduler, ioExecutor, properties);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,667 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.event.EventListener;
|
||||
import org.briarproject.bramble.api.lifecycle.Service;
|
||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
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.MailboxUpdateSentToNewContactEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.OwnMailboxConnectionStatusEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.Plugin;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxHelper.isClientCompatibleWithServer;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
|
||||
/**
|
||||
* This component manages a {@link MailboxClient} for each mailbox we know
|
||||
* about and are able to use (our own mailbox and/or contacts' mailboxes).
|
||||
* The clients are created when we come online (i.e. when the Tor plugin
|
||||
* becomes {@link Plugin.State#ACTIVE active}) and destroyed when we go
|
||||
* offline.
|
||||
* <p/>
|
||||
* The manager keeps track of the latest {@link MailboxUpdate} sent to and
|
||||
* received from each contact. These updates are used to decide which
|
||||
* mailboxes the manager needs clients for, and which mailbox (if any) should
|
||||
* be used for uploading data to and/or downloading data from each contact.
|
||||
* <p/>
|
||||
* The assignments of contacts to mailboxes for upload and/or download may
|
||||
* change when the following events happen:
|
||||
* <ul>
|
||||
* <li> A mailbox is paired or unpaired </li>
|
||||
* <li> A contact is added or removed </li>
|
||||
* <li> A {@link MailboxUpdate} is received from a contact </li>
|
||||
* <li> We discover that our own mailbox's supported API versions have
|
||||
* changed </li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* The manager keeps its mutable state consistent with the state in the DB by
|
||||
* using commit actions and events to update the manager's state on the event
|
||||
* thread.
|
||||
*/
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class MailboxClientManager implements Service, EventListener {
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(MailboxClientManager.class.getName());
|
||||
|
||||
private final Executor eventExecutor, dbExecutor;
|
||||
private final TransactionManager db;
|
||||
private final ContactManager contactManager;
|
||||
private final PluginManager pluginManager;
|
||||
private final MailboxSettingsManager mailboxSettingsManager;
|
||||
private final MailboxUpdateManager mailboxUpdateManager;
|
||||
private final MailboxClientFactory mailboxClientFactory;
|
||||
private final TorReachabilityMonitor reachabilityMonitor;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
// All of the following mutable state must only be accessed on the
|
||||
// event thread
|
||||
private final Map<ContactId, Updates> contactUpdates = new HashMap<>();
|
||||
private final Map<ContactId, MailboxClient> contactClients =
|
||||
new HashMap<>();
|
||||
@Nullable
|
||||
private MailboxProperties ownProperties = null;
|
||||
@Nullable
|
||||
private MailboxClient ownClient = null;
|
||||
private boolean online = false, handleEvents = false;
|
||||
|
||||
@Inject
|
||||
MailboxClientManager(@EventExecutor Executor eventExecutor,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
TransactionManager db,
|
||||
ContactManager contactManager,
|
||||
PluginManager pluginManager,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
MailboxClientFactory mailboxClientFactory,
|
||||
TorReachabilityMonitor reachabilityMonitor) {
|
||||
this.eventExecutor = eventExecutor;
|
||||
this.dbExecutor = dbExecutor;
|
||||
this.db = db;
|
||||
this.contactManager = contactManager;
|
||||
this.pluginManager = pluginManager;
|
||||
this.mailboxSettingsManager = mailboxSettingsManager;
|
||||
this.mailboxUpdateManager = mailboxUpdateManager;
|
||||
this.mailboxClientFactory = mailboxClientFactory;
|
||||
this.reachabilityMonitor = reachabilityMonitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startService() throws ServiceException {
|
||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||
reachabilityMonitor.start();
|
||||
dbExecutor.execute(this::loadMailboxProperties);
|
||||
}
|
||||
|
||||
@DatabaseExecutor
|
||||
private void loadMailboxProperties() {
|
||||
LOG.info("Loading mailbox properties");
|
||||
try {
|
||||
db.transaction(true, txn -> {
|
||||
Map<ContactId, Updates> updates = new HashMap<>();
|
||||
for (Contact c : contactManager.getContacts(txn)) {
|
||||
MailboxUpdate local = mailboxUpdateManager
|
||||
.getLocalUpdate(txn, c.getId());
|
||||
MailboxUpdate remote = mailboxUpdateManager
|
||||
.getRemoteUpdate(txn, c.getId());
|
||||
updates.put(c.getId(), new Updates(local, remote));
|
||||
}
|
||||
MailboxProperties ownProps =
|
||||
mailboxSettingsManager.getOwnMailboxProperties(txn);
|
||||
// Use a commit action so the state in memory remains
|
||||
// consistent with the state in the DB
|
||||
txn.attach(() -> initialiseState(updates, ownProps));
|
||||
});
|
||||
} catch (DbException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void initialiseState(Map<ContactId, Updates> updates,
|
||||
@Nullable MailboxProperties ownProps) {
|
||||
contactUpdates.putAll(updates);
|
||||
ownProperties = ownProps;
|
||||
Plugin tor = pluginManager.getPlugin(ID);
|
||||
if (tor != null && tor.getState() == ACTIVE) {
|
||||
LOG.info("Online");
|
||||
online = true;
|
||||
createClients();
|
||||
}
|
||||
// Now that the mutable state has been initialised we can start
|
||||
// handling events. This is done in a commit action so that we don't
|
||||
// miss any changes to the DB state or handle events for any changes
|
||||
// that were already reflected in the initial load
|
||||
handleEvents = true;
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void createClients() {
|
||||
LOG.info("Creating clients");
|
||||
for (Entry<ContactId, Updates> e : contactUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates u = e.getValue();
|
||||
if (isContactMailboxUsable(u.remote)) {
|
||||
// Create and start a client for the contact's mailbox
|
||||
MailboxClient contactClient = createAndStartClient(c);
|
||||
// Assign the contact to the contact's mailbox for upload
|
||||
assignContactToContactMailboxForUpload(c, contactClient, u);
|
||||
if (!isOwnMailboxUsable(ownProperties, u.remote)) {
|
||||
// We don't have a usable mailbox, so assign the contact to
|
||||
// the contact's mailbox for download too
|
||||
assignContactToContactMailboxForDownload(c,
|
||||
contactClient, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ownProperties == null) return;
|
||||
if (!isOwnMailboxUsable(ownProperties)) {
|
||||
LOG.warning("We have a mailbox but we can't use it");
|
||||
return;
|
||||
}
|
||||
// Create and start a client for our mailbox
|
||||
createAndStartClientForOwnMailbox();
|
||||
// Assign contacts to our mailbox for upload/download
|
||||
for (Entry<ContactId, Updates> e : contactUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates u = e.getValue();
|
||||
if (isOwnMailboxUsable(ownProperties, u.remote)) {
|
||||
// Assign the contact to our mailbox for download
|
||||
assignContactToOwnMailboxForDownload(c, u);
|
||||
if (!isContactMailboxUsable(u.remote)) {
|
||||
// The contact doesn't have a usable mailbox, so assign
|
||||
// the contact to our mailbox for upload too
|
||||
assignContactToOwnMailboxForUpload(c, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopService() throws ServiceException {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
eventExecutor.execute(() -> {
|
||||
handleEvents = false;
|
||||
if (online) destroyClients();
|
||||
latch.countDown();
|
||||
});
|
||||
reachabilityMonitor.destroy();
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new ServiceException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void destroyClients() {
|
||||
LOG.info("Destroying clients");
|
||||
for (MailboxClient client : contactClients.values()) {
|
||||
client.destroy();
|
||||
}
|
||||
contactClients.clear();
|
||||
destroyOwnClient();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void destroyOwnClient() {
|
||||
if (ownClient != null) {
|
||||
ownClient.destroy();
|
||||
ownClient = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (!handleEvents) return;
|
||||
if (e instanceof TransportActiveEvent) {
|
||||
TransportActiveEvent t = (TransportActiveEvent) e;
|
||||
if (t.getTransportId().equals(ID)) onTorActive();
|
||||
} else if (e instanceof TransportInactiveEvent) {
|
||||
TransportInactiveEvent t = (TransportInactiveEvent) e;
|
||||
if (t.getTransportId().equals(ID)) onTorInactive();
|
||||
} else if (e instanceof MailboxPairedEvent) {
|
||||
LOG.info("Mailbox paired");
|
||||
MailboxPairedEvent m = (MailboxPairedEvent) e;
|
||||
onMailboxPaired(m.getProperties(), m.getLocalUpdates());
|
||||
} else if (e instanceof MailboxUnpairedEvent) {
|
||||
LOG.info("Mailbox unpaired");
|
||||
MailboxUnpairedEvent m = (MailboxUnpairedEvent) e;
|
||||
onMailboxUnpaired(m.getLocalUpdates());
|
||||
} else if (e instanceof MailboxUpdateSentToNewContactEvent) {
|
||||
LOG.info("Contact added");
|
||||
MailboxUpdateSentToNewContactEvent
|
||||
m = (MailboxUpdateSentToNewContactEvent) e;
|
||||
onContactAdded(m.getContactId(), m.getMailboxUpdate());
|
||||
} else if (e instanceof ContactRemovedEvent) {
|
||||
LOG.info("Contact removed");
|
||||
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||
onContactRemoved(c.getContactId());
|
||||
} else if (e instanceof RemoteMailboxUpdateEvent) {
|
||||
LOG.info("Remote mailbox update");
|
||||
RemoteMailboxUpdateEvent r = (RemoteMailboxUpdateEvent) e;
|
||||
onRemoteMailboxUpdate(r.getContact(), r.getMailboxUpdate());
|
||||
} else if (e instanceof OwnMailboxConnectionStatusEvent) {
|
||||
OwnMailboxConnectionStatusEvent o =
|
||||
(OwnMailboxConnectionStatusEvent) e;
|
||||
onOwnMailboxConnectionStatusChanged(o.getStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onTorActive() {
|
||||
// If we checked the plugin at startup concurrently with the plugin
|
||||
// becoming active then `online` may already be true when we receive
|
||||
// the first TransportActiveEvent, in which case ignore it
|
||||
if (online) return;
|
||||
LOG.info("Online");
|
||||
online = true;
|
||||
createClients();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onTorInactive() {
|
||||
// If we checked the plugin at startup concurrently with the plugin
|
||||
// becoming inactive then `online` may already be false when we
|
||||
// receive the first TransportInactiveEvent, in which case ignore it
|
||||
if (!online) return;
|
||||
LOG.info("Offline");
|
||||
online = false;
|
||||
destroyClients();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onMailboxPaired(MailboxProperties ownProps,
|
||||
Map<ContactId, MailboxUpdateWithMailbox> localUpdates) {
|
||||
for (Entry<ContactId, MailboxUpdateWithMailbox> e :
|
||||
localUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates u = contactUpdates.get(c);
|
||||
contactUpdates.put(c, new Updates(e.getValue(), u.remote));
|
||||
}
|
||||
ownProperties = ownProps;
|
||||
if (!online) return;
|
||||
if (!isOwnMailboxUsable(ownProperties)) {
|
||||
LOG.warning("We have a mailbox but we can't use it");
|
||||
return;
|
||||
}
|
||||
// Create and start a client for our mailbox
|
||||
createAndStartClientForOwnMailbox();
|
||||
// Assign contacts to our mailbox for upload/download
|
||||
for (Entry<ContactId, Updates> e : contactUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates u = e.getValue();
|
||||
if (!isOwnMailboxUsable(ownProperties, u.remote)) {
|
||||
// Our mailbox isn't usable for communicating with this
|
||||
// contact, so don't assign/reassign this contact
|
||||
continue;
|
||||
}
|
||||
if (isContactMailboxUsable(u.remote)) {
|
||||
// The contact has a usable mailbox, so the contact should
|
||||
// currently be assigned to the contact's mailbox for upload
|
||||
// and download. Reassign the contact to our mailbox for
|
||||
// download
|
||||
MailboxClient contactClient =
|
||||
requireNonNull(contactClients.get(c));
|
||||
contactClient.deassignContactForDownload(c);
|
||||
} else {
|
||||
// The contact doesn't have a usable mailbox, so assign the
|
||||
// contact to our mailbox for upload
|
||||
assignContactToOwnMailboxForUpload(c, u);
|
||||
}
|
||||
assignContactToOwnMailboxForDownload(c, u);
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onMailboxUnpaired(Map<ContactId, MailboxUpdate> localUpdates) {
|
||||
for (Entry<ContactId, MailboxUpdate> e : localUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates updates = contactUpdates.get(c);
|
||||
contactUpdates.put(c, new Updates(e.getValue(), updates.remote));
|
||||
}
|
||||
MailboxProperties oldOwnProperties = ownProperties;
|
||||
ownProperties = null;
|
||||
if (!online) return;
|
||||
// Destroy the client for our own mailbox, if any
|
||||
destroyOwnClient();
|
||||
// Reassign contacts to their own mailboxes for download where possible
|
||||
for (Entry<ContactId, Updates> e : contactUpdates.entrySet()) {
|
||||
ContactId c = e.getKey();
|
||||
Updates u = e.getValue();
|
||||
if (isContactMailboxUsable(u.remote) &&
|
||||
isOwnMailboxUsable(oldOwnProperties, u.remote)) {
|
||||
// The contact should currently be assigned to our mailbox
|
||||
// for download. Reassign the contact to the contact's
|
||||
// mailbox for download
|
||||
MailboxClient contactClient =
|
||||
requireNonNull(contactClients.get(c));
|
||||
assignContactToContactMailboxForDownload(c, contactClient, u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onContactAdded(ContactId c, MailboxUpdate u) {
|
||||
Updates old = contactUpdates.put(c, new Updates(u, null));
|
||||
if (old != null) throw new IllegalStateException();
|
||||
// We haven't yet received an update from the newly added contact,
|
||||
// so at this stage we don't need to assign the contact to any
|
||||
// mailboxes for upload or download
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onContactRemoved(ContactId c) {
|
||||
Updates updates = requireNonNull(contactUpdates.remove(c));
|
||||
if (!online) return;
|
||||
// Destroy the client for the contact's mailbox, if any
|
||||
MailboxClient client = contactClients.remove(c);
|
||||
if (client != null) client.destroy();
|
||||
// If we have a mailbox and the contact is assigned to it for upload
|
||||
// and/or download, deassign the contact
|
||||
if (ownProperties == null) return;
|
||||
if (isOwnMailboxUsable(ownProperties, updates.remote)) {
|
||||
// We have a usable mailbox, so the contact should currently be
|
||||
// assigned to our mailbox for download. Deassign the contact from
|
||||
// our mailbox for download
|
||||
requireNonNull(ownClient).deassignContactForDownload(c);
|
||||
if (!isContactMailboxUsable(updates.remote)) {
|
||||
// The contact doesn't have a usable mailbox, so the contact
|
||||
// should currently be assigned to our mailbox for upload.
|
||||
// Deassign the contact from our mailbox for upload
|
||||
requireNonNull(ownClient).deassignContactForUpload(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onRemoteMailboxUpdate(ContactId c, MailboxUpdate remote) {
|
||||
Updates old = contactUpdates.get(c);
|
||||
MailboxUpdate oldRemote = old.remote;
|
||||
Updates u = new Updates(old.local, remote);
|
||||
contactUpdates.put(c, u);
|
||||
if (!online) return;
|
||||
// What may have changed?
|
||||
// * Contact's mailbox may be usable now, was unusable before
|
||||
// * Contact's mailbox may be unusable now, was usable before
|
||||
// * Contact's mailbox may have been replaced
|
||||
// * Contact's mailbox may have changed its API versions
|
||||
// * Contact may be able to use our mailbox now, was unable before
|
||||
// * Contact may be unable to use our mailbox now, was able before
|
||||
boolean wasContactMailboxUsable = isContactMailboxUsable(oldRemote);
|
||||
boolean isContactMailboxUsable = isContactMailboxUsable(remote);
|
||||
boolean wasOwnMailboxUsable =
|
||||
isOwnMailboxUsable(ownProperties, oldRemote);
|
||||
boolean isOwnMailboxUsable = isOwnMailboxUsable(ownProperties, remote);
|
||||
|
||||
// Create/destroy/replace the client for the contact's mailbox if needed
|
||||
MailboxClient contactClient = null;
|
||||
boolean clientReplaced = false;
|
||||
if (isContactMailboxUsable) {
|
||||
if (wasContactMailboxUsable) {
|
||||
MailboxProperties oldProps = getMailboxProperties(oldRemote);
|
||||
MailboxProperties newProps = getMailboxProperties(remote);
|
||||
if (oldProps.equals(newProps)) {
|
||||
// The contact previously had a usable mailbox, now has
|
||||
// a usable mailbox, it's the same mailbox, and its API
|
||||
// versions haven't changed. Keep using the existing client
|
||||
contactClient = requireNonNull(contactClients.get(c));
|
||||
} else {
|
||||
// The contact previously had a usable mailbox and now has
|
||||
// a usable mailbox, but either it's a new mailbox or its
|
||||
// API versions have changed. Replace the client
|
||||
requireNonNull(contactClients.remove(c)).destroy();
|
||||
contactClient = createAndStartClient(c);
|
||||
clientReplaced = true;
|
||||
}
|
||||
} else {
|
||||
// The client didn't previously have a usable mailbox but now
|
||||
// has one. Create and start a client
|
||||
contactClient = createAndStartClient(c);
|
||||
}
|
||||
} else if (wasContactMailboxUsable) {
|
||||
// The client previously had a usable mailbox but no longer does.
|
||||
// Destroy the existing client
|
||||
requireNonNull(contactClients.remove(c)).destroy();
|
||||
}
|
||||
|
||||
boolean wasAssignedToOwnMailboxForUpload =
|
||||
wasOwnMailboxUsable && !wasContactMailboxUsable;
|
||||
boolean willBeAssignedToOwnMailboxForUpload =
|
||||
isOwnMailboxUsable && !isContactMailboxUsable;
|
||||
|
||||
boolean wasAssignedToContactMailboxForDownload =
|
||||
!wasOwnMailboxUsable && wasContactMailboxUsable;
|
||||
boolean willBeAssignedToContactMailboxForDownload =
|
||||
!isOwnMailboxUsable && isContactMailboxUsable;
|
||||
|
||||
// Deassign the contact for upload/download if needed
|
||||
if (wasAssignedToOwnMailboxForUpload &&
|
||||
!willBeAssignedToOwnMailboxForUpload) {
|
||||
requireNonNull(ownClient).deassignContactForUpload(c);
|
||||
}
|
||||
if (wasOwnMailboxUsable && !isOwnMailboxUsable) {
|
||||
requireNonNull(ownClient).deassignContactForDownload(c);
|
||||
}
|
||||
// If the client for the contact's mailbox was replaced or destroyed
|
||||
// above then we don't need to deassign the contact for download
|
||||
if (wasAssignedToContactMailboxForDownload &&
|
||||
!willBeAssignedToContactMailboxForDownload &&
|
||||
!clientReplaced && isContactMailboxUsable) {
|
||||
requireNonNull(contactClient).deassignContactForDownload(c);
|
||||
}
|
||||
// We never need to deassign the contact from the contact's mailbox for
|
||||
// upload: this would only be needed if the contact's mailbox were no
|
||||
// longer usable, in which case the client would already have been
|
||||
// destroyed above. Thanks to the linter for spotting this
|
||||
|
||||
// Assign the contact for upload/download if needed
|
||||
if (!wasAssignedToOwnMailboxForUpload &&
|
||||
willBeAssignedToOwnMailboxForUpload) {
|
||||
assignContactToOwnMailboxForUpload(c, u);
|
||||
}
|
||||
if (!wasOwnMailboxUsable && isOwnMailboxUsable) {
|
||||
assignContactToOwnMailboxForDownload(c, u);
|
||||
}
|
||||
if ((!wasContactMailboxUsable || clientReplaced) &&
|
||||
isContactMailboxUsable) {
|
||||
assignContactToContactMailboxForUpload(c, contactClient, u);
|
||||
}
|
||||
if ((!wasAssignedToContactMailboxForDownload || clientReplaced) &&
|
||||
willBeAssignedToContactMailboxForDownload) {
|
||||
assignContactToContactMailboxForDownload(c, contactClient, u);
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onOwnMailboxConnectionStatusChanged(MailboxStatus status) {
|
||||
if (!online || ownProperties == null) return;
|
||||
List<MailboxVersion> oldServerSupports =
|
||||
ownProperties.getServerSupports();
|
||||
List<MailboxVersion> newServerSupports = status.getServerSupports();
|
||||
if (!oldServerSupports.equals(newServerSupports)) {
|
||||
LOG.info("Our mailbox's supported API versions have changed");
|
||||
// This potentially affects every assignment of contacts to
|
||||
// mailboxes for upload and download, so just rebuild the clients
|
||||
// and assignments from scratch
|
||||
destroyClients();
|
||||
createClients();
|
||||
}
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void createAndStartClientForOwnMailbox() {
|
||||
if (ownClient != null) throw new IllegalStateException();
|
||||
ownClient = mailboxClientFactory.createOwnMailboxClient(
|
||||
reachabilityMonitor, requireNonNull(ownProperties));
|
||||
ownClient.start();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private MailboxClient createAndStartClient(ContactId c) {
|
||||
MailboxClient client = mailboxClientFactory
|
||||
.createContactMailboxClient(reachabilityMonitor);
|
||||
MailboxClient old = contactClients.put(c, client);
|
||||
if (old != null) throw new IllegalStateException();
|
||||
client.start();
|
||||
return client;
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void assignContactToOwnMailboxForDownload(ContactId c, Updates u) {
|
||||
MailboxProperties localProps = getMailboxProperties(u.local);
|
||||
requireNonNull(ownClient).assignContactForDownload(c,
|
||||
requireNonNull(ownProperties),
|
||||
requireNonNull(localProps.getOutboxId()));
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void assignContactToOwnMailboxForUpload(ContactId c, Updates u) {
|
||||
MailboxProperties localProps = getMailboxProperties(u.local);
|
||||
requireNonNull(ownClient).assignContactForUpload(c,
|
||||
requireNonNull(ownProperties),
|
||||
requireNonNull(localProps.getInboxId()));
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void assignContactToContactMailboxForDownload(ContactId c,
|
||||
MailboxClient contactClient, Updates u) {
|
||||
MailboxProperties remoteProps =
|
||||
getMailboxProperties(requireNonNull(u.remote));
|
||||
contactClient.assignContactForDownload(c, remoteProps,
|
||||
requireNonNull(remoteProps.getInboxId()));
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void assignContactToContactMailboxForUpload(ContactId c,
|
||||
MailboxClient contactClient, Updates u) {
|
||||
MailboxProperties remoteProps =
|
||||
getMailboxProperties(requireNonNull(u.remote));
|
||||
contactClient.assignContactForUpload(c, remoteProps,
|
||||
requireNonNull(remoteProps.getOutboxId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link MailboxProperties} included in the given update,
|
||||
* which must be a {@link MailboxUpdateWithMailbox}.
|
||||
*/
|
||||
private MailboxProperties getMailboxProperties(MailboxUpdate update) {
|
||||
if (!(update instanceof MailboxUpdateWithMailbox)) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
MailboxUpdateWithMailbox mailbox = (MailboxUpdateWithMailbox) update;
|
||||
return mailbox.getMailboxProperties();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we can use our own mailbox to communicate with the
|
||||
* contact that sent the given update.
|
||||
*/
|
||||
private boolean isOwnMailboxUsable(
|
||||
@Nullable MailboxProperties ownProperties,
|
||||
@Nullable MailboxUpdate remote) {
|
||||
if (ownProperties == null || remote == null) return false;
|
||||
return isMailboxUsable(remote.getClientSupports(),
|
||||
ownProperties.getServerSupports());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we can use the contact's mailbox to communicate with
|
||||
* the contact that sent the given update.
|
||||
*/
|
||||
private boolean isContactMailboxUsable(@Nullable MailboxUpdate remote) {
|
||||
if (remote instanceof MailboxUpdateWithMailbox) {
|
||||
MailboxUpdateWithMailbox remoteMailbox =
|
||||
(MailboxUpdateWithMailbox) remote;
|
||||
return isMailboxUsable(remoteMailbox.getClientSupports(),
|
||||
remoteMailbox.getMailboxProperties().getServerSupports());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we can communicate with a contact that has the given
|
||||
* client-supported API versions via a mailbox with the given
|
||||
* server-supported API versions.
|
||||
*/
|
||||
private boolean isMailboxUsable(List<MailboxVersion> contactClient,
|
||||
List<MailboxVersion> server) {
|
||||
return isClientCompatibleWithServer(contactClient, server)
|
||||
&& isClientCompatibleWithServer(CLIENT_SUPPORTS, server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if our client-supported API versions are compatible with
|
||||
* our own mailbox's server-supported API versions.
|
||||
*/
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
private boolean isOwnMailboxUsable(MailboxProperties ownProperties) {
|
||||
return isClientCompatibleWithServer(CLIENT_SUPPORTS,
|
||||
ownProperties.getServerSupports());
|
||||
}
|
||||
|
||||
/**
|
||||
* A container for the latest {@link MailboxUpdate updates} sent to and
|
||||
* received from a given contact.
|
||||
*/
|
||||
private static class Updates {
|
||||
|
||||
/**
|
||||
* The latest update sent to the contact.
|
||||
*/
|
||||
private final MailboxUpdate local;
|
||||
|
||||
/**
|
||||
* The latest update received from the contact, or null if no update
|
||||
* has been received.
|
||||
*/
|
||||
@Nullable
|
||||
private final MailboxUpdate remote;
|
||||
|
||||
private Updates(MailboxUpdate local, @Nullable MailboxUpdate remote) {
|
||||
this.local = local;
|
||||
this.remote = remote;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,22 @@ import org.briarproject.bramble.api.FeatureFlags;
|
||||
import org.briarproject.bramble.api.client.ClientHelper;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.data.MetadataEncoder;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.event.EventExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxUpdateManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxVersion;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.sync.validation.ValidationManager;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -37,6 +42,8 @@ public class MailboxModule {
|
||||
MailboxUpdateManager mailboxUpdateManager;
|
||||
@Inject
|
||||
MailboxFileManager mailboxFileManager;
|
||||
@Inject
|
||||
MailboxClientManager mailboxClientManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -126,4 +133,49 @@ public class MailboxModule {
|
||||
MailboxWorkerFactoryImpl mailboxWorkerFactory) {
|
||||
return mailboxWorkerFactory;
|
||||
}
|
||||
|
||||
@Provides
|
||||
MailboxClientFactory provideMailboxClientFactory(
|
||||
MailboxClientFactoryImpl mailboxClientFactory) {
|
||||
return mailboxClientFactory;
|
||||
}
|
||||
|
||||
@Provides
|
||||
MailboxApiCaller provideMailboxApiCaller(
|
||||
MailboxApiCallerImpl mailboxApiCaller) {
|
||||
return mailboxApiCaller;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
TorReachabilityMonitor provideTorReachabilityMonitor(
|
||||
TorReachabilityMonitorImpl reachabilityMonitor) {
|
||||
return reachabilityMonitor;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
MailboxClientManager provideMailboxClientManager(
|
||||
@EventExecutor Executor eventExecutor,
|
||||
@DatabaseExecutor Executor dbExecutor,
|
||||
TransactionManager db,
|
||||
ContactManager contactManager,
|
||||
PluginManager pluginManager,
|
||||
MailboxSettingsManager mailboxSettingsManager,
|
||||
MailboxUpdateManager mailboxUpdateManager,
|
||||
MailboxClientFactory mailboxClientFactory,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
FeatureFlags featureFlags,
|
||||
LifecycleManager lifecycleManager,
|
||||
EventBus eventBus) {
|
||||
MailboxClientManager manager = new MailboxClientManager(eventExecutor,
|
||||
dbExecutor, db, contactManager, pluginManager,
|
||||
mailboxSettingsManager, mailboxUpdateManager,
|
||||
mailboxClientFactory, reachabilityMonitor);
|
||||
if (featureFlags.shouldEnableMailbox()) {
|
||||
lifecycleManager.registerService(manager);
|
||||
eventBus.addListener(manager);
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,42 +116,39 @@ 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 {
|
||||
// if we no longer have a paired mailbox, return
|
||||
Settings oldSettings =
|
||||
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||
String onion = oldSettings.get(SETTINGS_KEY_ONION);
|
||||
String token = oldSettings.get(SETTINGS_KEY_TOKEN);
|
||||
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return;
|
||||
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);
|
||||
for (MailboxHook hook : hooks) {
|
||||
hook.serverSupportedVersionsReceived(txn, versions);
|
||||
}
|
||||
// broadcast status event
|
||||
MailboxStatus status = new MailboxStatus(now, now, 0, serverSupports);
|
||||
MailboxStatus status = new MailboxStatus(now, now, 0, versions);
|
||||
txn.attach(new OwnMailboxConnectionStatusEvent(status));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void recordFailedConnectionAttempt(Transaction txn, long now)
|
||||
throws DbException {
|
||||
// if we no longer have a paired mailbox, return
|
||||
Settings oldSettings =
|
||||
settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
|
||||
String onion = oldSettings.get(SETTINGS_KEY_ONION);
|
||||
String token = oldSettings.get(SETTINGS_KEY_TOKEN);
|
||||
if (isNullOrEmpty(onion) || isNullOrEmpty(token)) return;
|
||||
int newAttempts = 1 + oldSettings.getInt(SETTINGS_KEY_ATTEMPTS, 0);
|
||||
long lastSuccess = oldSettings.getLong(SETTINGS_KEY_LAST_SUCCESS, 0);
|
||||
Settings newSettings = new Settings();
|
||||
|
||||
@@ -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.MailboxUpdateSentToNewContactEvent;
|
||||
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;
|
||||
@@ -45,6 +49,9 @@ import java.util.Map.Entry;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.api.sync.validation.IncomingMessageHook.DeliveryAction.ACCEPT_DO_NOT_SHARE;
|
||||
|
||||
@NotNullByDefault
|
||||
@@ -116,7 +123,7 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
db.addGroup(txn, localGroup);
|
||||
// Set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
addingContact(txn, c);
|
||||
addingContact(txn, c, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,6 +139,17 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
|
||||
@Override
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
addingContact(txn, c, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param attachEvent True if a {@link MailboxUpdateSentToNewContactEvent}
|
||||
* should be attached to the transaction. We should only do this when
|
||||
* adding a new contact, not when setting up this client for an existing
|
||||
* contact.
|
||||
*/
|
||||
private void addingContact(Transaction txn, Contact c, boolean attachEvent)
|
||||
throws DbException {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
db.addGroup(txn, g);
|
||||
@@ -143,13 +161,17 @@ 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);
|
||||
}
|
||||
if (attachEvent) {
|
||||
txn.attach(new MailboxUpdateSentToNewContactEvent(c.getId(), u));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,18 +181,103 @@ 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));
|
||||
// Store the list of server-supported versions
|
||||
try {
|
||||
storeSentServerSupports(txn, p.getServerSupports());
|
||||
} catch (FormatException e) {
|
||||
throw new DbException();
|
||||
}
|
||||
}
|
||||
|
||||
@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));
|
||||
// Remove the list of server-supported versions
|
||||
try {
|
||||
BdfDictionary meta = BdfDictionary.of(new BdfEntry(
|
||||
GROUP_KEY_SENT_SERVER_SUPPORTS, NULL_VALUE));
|
||||
clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serverSupportedVersionsReceived(Transaction txn,
|
||||
List<MailboxVersion> serverSupports) throws DbException {
|
||||
try {
|
||||
List<MailboxVersion> oldServerSupports =
|
||||
loadSentServerSupports(txn);
|
||||
if (serverSupports.equals(oldServerSupports)) return;
|
||||
storeSentServerSupports(txn, serverSupports);
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group contactGroup = getContactGroup(c);
|
||||
LatestUpdate latest =
|
||||
findLatest(txn, contactGroup.getId(), true);
|
||||
// This method should only be called when we have a mailbox,
|
||||
// in which case we should have sent a local update to every
|
||||
// contact
|
||||
if (latest == null) throw new DbException();
|
||||
BdfList body =
|
||||
clientHelper.getMessageAsList(txn, latest.messageId);
|
||||
MailboxUpdate oldUpdate = parseUpdate(body);
|
||||
if (!oldUpdate.hasMailbox()) throw new DbException();
|
||||
MailboxUpdateWithMailbox newUpdate = updateServerSupports(
|
||||
(MailboxUpdateWithMailbox) oldUpdate, serverSupports);
|
||||
storeMessageReplaceLatest(txn, contactGroup.getId(), newUpdate,
|
||||
latest);
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
throw new DbException();
|
||||
}
|
||||
}
|
||||
|
||||
private void storeSentServerSupports(Transaction txn,
|
||||
List<MailboxVersion> serverSupports)
|
||||
throws DbException, FormatException {
|
||||
BdfDictionary meta = BdfDictionary.of(new BdfEntry(
|
||||
GROUP_KEY_SENT_SERVER_SUPPORTS,
|
||||
encodeSupportsList(serverSupports)));
|
||||
clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta);
|
||||
}
|
||||
|
||||
private List<MailboxVersion> loadSentServerSupports(Transaction txn)
|
||||
throws DbException, FormatException {
|
||||
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
BdfList serverSupports =
|
||||
meta.getOptionalList(GROUP_KEY_SENT_SERVER_SUPPORTS);
|
||||
if (serverSupports == null) return emptyList();
|
||||
return clientHelper.parseMailboxVersionList(serverSupports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link MailboxUpdateWithMailbox} that updates the list
|
||||
* of server-supported API versions in the given
|
||||
* {@link MailboxUpdateWithMailbox}.
|
||||
*/
|
||||
private MailboxUpdateWithMailbox updateServerSupports(
|
||||
MailboxUpdateWithMailbox old, List<MailboxVersion> serverSupports) {
|
||||
MailboxProperties oldProps = old.getMailboxProperties();
|
||||
MailboxProperties newProps = new MailboxProperties(oldProps.getOnion(),
|
||||
oldProps.getAuthToken(), serverSupports,
|
||||
requireNonNull(oldProps.getInboxId()),
|
||||
requireNonNull(oldProps.getOutboxId()));
|
||||
return new MailboxUpdateWithMailbox(old.getClientSupports(), newProps);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -239,18 +346,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 +367,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
|
||||
@@ -288,21 +397,27 @@ class MailboxUpdateManagerImpl implements MailboxUpdateManager,
|
||||
MailboxUpdate u) throws DbException {
|
||||
try {
|
||||
LatestUpdate latest = findLatest(txn, g, true);
|
||||
long version = latest == null ? 1 : latest.version + 1;
|
||||
Message m = clientHelper.createMessage(g, clock.currentTimeMillis(),
|
||||
encodeProperties(version, u));
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(MSG_KEY_VERSION, version);
|
||||
meta.put(MSG_KEY_LOCAL, true);
|
||||
clientHelper.addLocalMessage(txn, m, meta, true, false);
|
||||
if (latest != null) {
|
||||
db.removeMessage(txn, latest.messageId);
|
||||
}
|
||||
storeMessageReplaceLatest(txn, g, u, latest);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void storeMessageReplaceLatest(Transaction txn, GroupId g,
|
||||
MailboxUpdate u, @Nullable LatestUpdate latest)
|
||||
throws DbException, FormatException {
|
||||
long version = latest == null ? 1 : latest.version + 1;
|
||||
Message m = clientHelper.createMessage(g, clock.currentTimeMillis(),
|
||||
encodeProperties(version, u));
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(MSG_KEY_VERSION, version);
|
||||
meta.put(MSG_KEY_LOCAL, true);
|
||||
clientHelper.addLocalMessage(txn, m, meta, true, false);
|
||||
if (latest != null) {
|
||||
db.removeMessage(txn, latest.messageId);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private LatestUpdate findLatest(Transaction txn, GroupId g, boolean local)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
@@ -12,6 +13,8 @@ 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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
|
||||
import org.briarproject.bramble.api.sync.event.GroupVisibilityUpdatedEvent;
|
||||
@@ -32,6 +35,7 @@ import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.logging.Level.INFO;
|
||||
@@ -58,9 +62,16 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
* <p>
|
||||
* If there's no data to send, the worker listens for events indicating
|
||||
* that new data may be ready to send.
|
||||
* <p>
|
||||
* Whenever we're directly connected to the contact, the worker doesn't
|
||||
* check for data to send or start connectivity checks until the contact
|
||||
* disconnects. However, if the worker has already started writing and
|
||||
* uploading a file when the contact connects, the worker will finish the
|
||||
* upload.
|
||||
*/
|
||||
private enum State {
|
||||
CREATED,
|
||||
CONNECTED_TO_CONTACT,
|
||||
CHECKING_FOR_DATA,
|
||||
WAITING_FOR_DATA,
|
||||
CONNECTIVITY_CHECK,
|
||||
@@ -95,6 +106,7 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
private final Clock clock;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final EventBus eventBus;
|
||||
private final ConnectionRegistry connectionRegistry;
|
||||
private final ConnectivityChecker connectivityChecker;
|
||||
private final MailboxApiCaller mailboxApiCaller;
|
||||
private final MailboxApi mailboxApi;
|
||||
@@ -121,6 +133,7 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
Clock clock,
|
||||
TaskScheduler taskScheduler,
|
||||
EventBus eventBus,
|
||||
ConnectionRegistry connectionRegistry,
|
||||
ConnectivityChecker connectivityChecker,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
@@ -133,6 +146,7 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
this.clock = clock;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.eventBus = eventBus;
|
||||
this.connectionRegistry = connectionRegistry;
|
||||
this.connectivityChecker = connectivityChecker;
|
||||
this.mailboxApiCaller = mailboxApiCaller;
|
||||
this.mailboxApi = mailboxApi;
|
||||
@@ -182,6 +196,12 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
synchronized (lock) {
|
||||
checkTask = null;
|
||||
if (state != State.CHECKING_FOR_DATA) return;
|
||||
// Check whether we're directly connected to the contact. Calling
|
||||
// this while holding the lock isn't ideal, but it avoids races
|
||||
if (connectionRegistry.isConnected(contactId)) {
|
||||
state = State.CONNECTED_TO_CONTACT;
|
||||
return;
|
||||
}
|
||||
}
|
||||
LOG.info("Checking for data to send");
|
||||
try {
|
||||
@@ -328,13 +348,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();
|
||||
}
|
||||
|
||||
@@ -364,8 +384,14 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
onDataToSend();
|
||||
}
|
||||
} else if (e instanceof MessageSharedEvent) {
|
||||
LOG.info("Message shared");
|
||||
onDataToSend();
|
||||
MessageSharedEvent m = (MessageSharedEvent) e;
|
||||
// If the contact is present in the map (ie the value is not null)
|
||||
// and the value is true, the message's group is shared with the
|
||||
// contact and therefore the message may now be sendable
|
||||
if (m.getGroupVisibility().get(contactId) == TRUE) {
|
||||
LOG.info("Message shared");
|
||||
onDataToSend();
|
||||
}
|
||||
} else if (e instanceof GroupVisibilityUpdatedEvent) {
|
||||
GroupVisibilityUpdatedEvent g = (GroupVisibilityUpdatedEvent) e;
|
||||
if (g.getVisibility() == SHARED &&
|
||||
@@ -373,6 +399,18 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
LOG.info("Group shared");
|
||||
onDataToSend();
|
||||
}
|
||||
} else if (e instanceof ContactConnectedEvent) {
|
||||
ContactConnectedEvent c = (ContactConnectedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) {
|
||||
LOG.info("Contact connected");
|
||||
onContactConnected();
|
||||
}
|
||||
} else if (e instanceof ContactDisconnectedEvent) {
|
||||
ContactDisconnectedEvent c = (ContactDisconnectedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) {
|
||||
LOG.info("Contact disconnected");
|
||||
onContactDisconnected();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,4 +429,36 @@ class MailboxUploadWorker implements MailboxWorker, ConnectivityObserver,
|
||||
// If we had scheduled a wakeup when data was due to be sent, cancel it
|
||||
if (wakeupTask != null) wakeupTask.cancel();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onContactConnected() {
|
||||
Cancellable wakeupTask = null, checkTask = null;
|
||||
synchronized (lock) {
|
||||
if (state == State.DESTROYED) return;
|
||||
// If we're checking for data to send, waiting for data to send,
|
||||
// or checking connectivity then wait until we disconnect from
|
||||
// the contact before proceeding. If we're writing or uploading
|
||||
// a file then continue
|
||||
if (state == State.CHECKING_FOR_DATA ||
|
||||
state == State.WAITING_FOR_DATA ||
|
||||
state == State.CONNECTIVITY_CHECK) {
|
||||
state = State.CONNECTED_TO_CONTACT;
|
||||
wakeupTask = this.wakeupTask;
|
||||
this.wakeupTask = null;
|
||||
checkTask = this.checkTask;
|
||||
this.checkTask = null;
|
||||
}
|
||||
}
|
||||
if (wakeupTask != null) wakeupTask.cancel();
|
||||
if (checkTask != null) checkTask.cancel();
|
||||
}
|
||||
|
||||
@EventExecutor
|
||||
private void onContactDisconnected() {
|
||||
synchronized (lock) {
|
||||
if (state != State.CONNECTED_TO_CONTACT) return;
|
||||
state = State.CHECKING_FOR_DATA;
|
||||
}
|
||||
ioExecutor.execute(this::checkForDataToSend);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -25,6 +26,7 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
private final Clock clock;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final EventBus eventBus;
|
||||
private final ConnectionRegistry connectionRegistry;
|
||||
private final MailboxApiCaller mailboxApiCaller;
|
||||
private final MailboxApi mailboxApi;
|
||||
private final MailboxFileManager mailboxFileManager;
|
||||
@@ -36,6 +38,7 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
Clock clock,
|
||||
TaskScheduler taskScheduler,
|
||||
EventBus eventBus,
|
||||
ConnectionRegistry connectionRegistry,
|
||||
MailboxApiCaller mailboxApiCaller,
|
||||
MailboxApi mailboxApi,
|
||||
MailboxFileManager mailboxFileManager,
|
||||
@@ -45,6 +48,7 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
this.clock = clock;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.eventBus = eventBus;
|
||||
this.connectionRegistry = connectionRegistry;
|
||||
this.mailboxApiCaller = mailboxApiCaller;
|
||||
this.mailboxApi = mailboxApi;
|
||||
this.mailboxFileManager = mailboxFileManager;
|
||||
@@ -57,9 +61,9 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
MailboxProperties properties, MailboxFolderId folderId,
|
||||
ContactId contactId) {
|
||||
MailboxUploadWorker worker = new MailboxUploadWorker(ioExecutor, db,
|
||||
clock, taskScheduler, eventBus, connectivityChecker,
|
||||
mailboxApiCaller, mailboxApi, mailboxFileManager,
|
||||
properties, folderId, contactId);
|
||||
clock, taskScheduler, eventBus, connectionRegistry,
|
||||
connectivityChecker, mailboxApiCaller, mailboxApi,
|
||||
mailboxFileManager, properties, folderId, contactId);
|
||||
eventBus.addListener(worker);
|
||||
return worker;
|
||||
}
|
||||
@@ -88,8 +92,10 @@ class MailboxWorkerFactoryImpl implements MailboxWorkerFactory {
|
||||
public MailboxWorker createContactListWorkerForOwnMailbox(
|
||||
ConnectivityChecker connectivityChecker,
|
||||
MailboxProperties properties) {
|
||||
return new OwnMailboxContactListWorker(ioExecutor, db, eventBus,
|
||||
connectivityChecker, mailboxApiCaller, mailboxApi,
|
||||
mailboxUpdateManager, properties);
|
||||
OwnMailboxContactListWorker worker = new OwnMailboxContactListWorker(
|
||||
ioExecutor, db, eventBus, connectivityChecker, mailboxApiCaller,
|
||||
mailboxApi, mailboxUpdateManager, properties);
|
||||
eventBus.addListener(worker);
|
||||
return worker;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
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.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.TaskScheduler;
|
||||
import org.briarproject.bramble.mailbox.ConnectivityChecker.ConnectivityObserver;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -11,17 +15,27 @@ import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.GuardedBy;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
|
||||
@ThreadSafe
|
||||
@NotNullByDefault
|
||||
class OwnMailboxClient implements MailboxClient {
|
||||
class OwnMailboxClient implements MailboxClient, ConnectivityObserver {
|
||||
|
||||
/**
|
||||
* How often to check our own mailbox's connectivity.
|
||||
* <p>
|
||||
* Package access for testing.
|
||||
*/
|
||||
static final long CONNECTIVITY_CHECK_INTERVAL_MS = HOURS.toMillis(1);
|
||||
|
||||
private static final Logger LOG =
|
||||
getLogger(OwnMailboxClient.class.getName());
|
||||
@@ -29,6 +43,9 @@ class OwnMailboxClient implements MailboxClient {
|
||||
private final MailboxWorkerFactory workerFactory;
|
||||
private final ConnectivityChecker connectivityChecker;
|
||||
private final TorReachabilityMonitor reachabilityMonitor;
|
||||
private final TaskScheduler taskScheduler;
|
||||
private final Executor ioExecutor;
|
||||
private final MailboxProperties properties;
|
||||
private final MailboxWorker contactListWorker;
|
||||
private final Object lock = new Object();
|
||||
|
||||
@@ -53,14 +70,30 @@ class OwnMailboxClient implements MailboxClient {
|
||||
@GuardedBy("lock")
|
||||
private final Set<ContactId> assignedForDownload = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Scheduled task for periodically checking whether the mailbox is
|
||||
* reachable.
|
||||
*/
|
||||
@GuardedBy("lock")
|
||||
@Nullable
|
||||
private Cancellable connectivityTask = null;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private boolean destroyed = false;
|
||||
|
||||
OwnMailboxClient(MailboxWorkerFactory workerFactory,
|
||||
ConnectivityChecker connectivityChecker,
|
||||
TorReachabilityMonitor reachabilityMonitor,
|
||||
TaskScheduler taskScheduler,
|
||||
@IoExecutor Executor ioExecutor,
|
||||
MailboxProperties properties) {
|
||||
if (!properties.isOwner()) throw new IllegalArgumentException();
|
||||
this.workerFactory = workerFactory;
|
||||
this.connectivityChecker = connectivityChecker;
|
||||
this.reachabilityMonitor = reachabilityMonitor;
|
||||
this.taskScheduler = taskScheduler;
|
||||
this.ioExecutor = ioExecutor;
|
||||
this.properties = properties;
|
||||
contactListWorker = workerFactory.createContactListWorkerForOwnMailbox(
|
||||
connectivityChecker, properties);
|
||||
}
|
||||
@@ -68,6 +101,7 @@ class OwnMailboxClient implements MailboxClient {
|
||||
@Override
|
||||
public void start() {
|
||||
LOG.info("Started");
|
||||
checkConnectivity();
|
||||
contactListWorker.start();
|
||||
}
|
||||
|
||||
@@ -76,16 +110,26 @@ class OwnMailboxClient implements MailboxClient {
|
||||
LOG.info("Destroyed");
|
||||
List<MailboxWorker> uploadWorkers;
|
||||
MailboxWorker downloadWorker;
|
||||
Cancellable connectivityTask;
|
||||
synchronized (lock) {
|
||||
uploadWorkers = new ArrayList<>(this.uploadWorkers.values());
|
||||
this.uploadWorkers.clear();
|
||||
downloadWorker = this.downloadWorker;
|
||||
this.downloadWorker = null;
|
||||
connectivityTask = this.connectivityTask;
|
||||
this.connectivityTask = null;
|
||||
destroyed = true;
|
||||
}
|
||||
// Destroy the workers (with apologies to Mr Marx and Mr Engels)
|
||||
for (MailboxWorker worker : uploadWorkers) worker.destroy();
|
||||
if (downloadWorker != null) downloadWorker.destroy();
|
||||
// If a connectivity check is scheduled, cancel it
|
||||
if (connectivityTask != null) connectivityTask.cancel();
|
||||
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
|
||||
@@ -151,4 +195,28 @@ class OwnMailboxClient implements MailboxClient {
|
||||
}
|
||||
if (toDestroy != null) toDestroy.destroy();
|
||||
}
|
||||
|
||||
private void checkConnectivity() {
|
||||
synchronized (lock) {
|
||||
if (destroyed) return;
|
||||
connectivityTask = null;
|
||||
}
|
||||
connectivityChecker.checkConnectivity(properties, this);
|
||||
// Avoid leaking observer in case destroy() is called concurrently
|
||||
// before observer is added
|
||||
boolean removeObserver;
|
||||
synchronized (lock) {
|
||||
removeObserver = destroyed;
|
||||
}
|
||||
if (removeObserver) connectivityChecker.removeObserver(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectivityCheckSucceeded() {
|
||||
synchronized (lock) {
|
||||
if (destroyed) return;
|
||||
connectivityTask = taskScheduler.schedule(this::checkConnectivity,
|
||||
ioExecutor, CONNECTIVITY_CHECK_INTERVAL_MS, MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -12,7 +12,8 @@ public interface CircumventionProvider {
|
||||
DEFAULT_OBFS4,
|
||||
NON_DEFAULT_OBFS4,
|
||||
VANILLA,
|
||||
MEEK
|
||||
MEEK,
|
||||
SNOWFLAKE
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,13 +42,14 @@ public interface CircumventionProvider {
|
||||
* Countries where non-default obfs4 or vanilla bridges are likely to work.
|
||||
* Should be a subset of {@link #BRIDGES}.
|
||||
*/
|
||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU", "TM"};
|
||||
String[] NON_DEFAULT_BRIDGES = {"BY", "RU"};
|
||||
|
||||
/**
|
||||
* Countries where vanilla bridges are blocked via DPI but non-default
|
||||
* obfs4 bridges and meek may work. Should be a subset of {@link #BRIDGES}.
|
||||
* obfs4 bridges, meek and snowflake may work. Should be a subset of
|
||||
* {@link #BRIDGES}.
|
||||
*/
|
||||
String[] DPI_BRIDGES = {"CN", "IR"};
|
||||
String[] DPI_BRIDGES = {"CN", "IR", "TM"};
|
||||
|
||||
/**
|
||||
* Returns true if vanilla Tor connections are blocked in the given country.
|
||||
@@ -68,6 +70,6 @@ public interface CircumventionProvider {
|
||||
List<BridgeType> getSuitableBridgeTypes(String countryCode);
|
||||
|
||||
@IoExecutor
|
||||
List<String> getBridges(BridgeType type);
|
||||
|
||||
List<String> getBridges(BridgeType type, String countryCode,
|
||||
boolean letsEncrypt);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Scanner;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
@@ -18,6 +20,7 @@ import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||
|
||||
@Immutable
|
||||
@@ -25,6 +28,8 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeTy
|
||||
class CircumventionProviderImpl implements CircumventionProvider {
|
||||
|
||||
private final static String BRIDGE_FILE_NAME = "bridges";
|
||||
private final static String SNOWFLAKE_PARAMS_FILE_NAME = "snowflake-params";
|
||||
private final static String DEFAULT_COUNTRY_CODE = "ZZ";
|
||||
|
||||
private static final Set<String> BLOCKED_IN_COUNTRIES =
|
||||
new HashSet<>(asList(BLOCKED));
|
||||
@@ -58,7 +63,7 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
||||
} else if (NON_DEFAULT_BRIDGE_COUNTRIES.contains(countryCode)) {
|
||||
return asList(NON_DEFAULT_OBFS4, VANILLA);
|
||||
} else if (DPI_COUNTRIES.contains(countryCode)) {
|
||||
return asList(NON_DEFAULT_OBFS4, MEEK);
|
||||
return asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE);
|
||||
} else {
|
||||
return asList(DEFAULT_OBFS4, VANILLA);
|
||||
}
|
||||
@@ -66,7 +71,8 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
||||
|
||||
@Override
|
||||
@IoExecutor
|
||||
public List<String> getBridges(BridgeType type) {
|
||||
public List<String> getBridges(BridgeType type, String countryCode,
|
||||
boolean letsEncrypt) {
|
||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
||||
.getResourceAsStream(BRIDGE_FILE_NAME));
|
||||
Scanner scanner = new Scanner(is);
|
||||
@@ -79,10 +85,45 @@ class CircumventionProviderImpl implements CircumventionProvider {
|
||||
(type == VANILLA && line.startsWith("v ")) ||
|
||||
(type == MEEK && line.startsWith("m "))) {
|
||||
bridges.add(line.substring(2));
|
||||
} else if (type == SNOWFLAKE && line.startsWith("s ")) {
|
||||
String params = getSnowflakeParams(countryCode, letsEncrypt);
|
||||
bridges.add(line.substring(2) + " " + params);
|
||||
}
|
||||
}
|
||||
scanner.close();
|
||||
return bridges;
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
String getSnowflakeParams(String countryCode, boolean letsEncrypt) {
|
||||
Map<String, String> params = loadSnowflakeParams();
|
||||
if (countryCode.isEmpty()) countryCode = DEFAULT_COUNTRY_CODE;
|
||||
// If we have parameters for this country code, return them
|
||||
String value = params.get(makeKey(countryCode, letsEncrypt));
|
||||
if (value != null) return value;
|
||||
// Return the default parameters
|
||||
value = params.get(makeKey(DEFAULT_COUNTRY_CODE, letsEncrypt));
|
||||
return requireNonNull(value);
|
||||
}
|
||||
|
||||
private Map<String, String> loadSnowflakeParams() {
|
||||
InputStream is = requireNonNull(getClass().getClassLoader()
|
||||
.getResourceAsStream(SNOWFLAKE_PARAMS_FILE_NAME));
|
||||
Scanner scanner = new Scanner(is);
|
||||
Map<String, String> params = new TreeMap<>();
|
||||
while (scanner.hasNextLine()) {
|
||||
String line = scanner.nextLine();
|
||||
if (line.length() < 5) continue;
|
||||
String key = line.substring(0, 4); // Country code, space, digit
|
||||
String value = line.substring(5);
|
||||
params.put(key, value);
|
||||
}
|
||||
scanner.close();
|
||||
return params;
|
||||
}
|
||||
|
||||
private String makeKey(String countryCode, boolean letsEncrypt) {
|
||||
return countryCode + " " + (letsEncrypt ? "1" : "0");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
|
||||
import static org.briarproject.bramble.util.IoUtils.copyAndClose;
|
||||
import static org.briarproject.bramble.util.IoUtils.tryToClose;
|
||||
@@ -212,6 +213,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
return new File(torDirectory, "obfs4proxy");
|
||||
}
|
||||
|
||||
protected File getSnowflakeExecutableFile() {
|
||||
return new File(torDirectory, "snowflake");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportId getId() {
|
||||
return TorConstants.ID;
|
||||
@@ -338,6 +343,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
geoIpFile.delete();
|
||||
installTorExecutable();
|
||||
installObfs4Executable();
|
||||
installSnowflakeExecutable();
|
||||
if (!doneFile.createNewFile())
|
||||
LOG.warning("Failed to create done file");
|
||||
}
|
||||
@@ -363,17 +369,29 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (!obfs4File.setExecutable(true, true)) throw new IOException();
|
||||
}
|
||||
|
||||
protected void installSnowflakeExecutable() throws IOException {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Installing snowflake binary for " + architecture);
|
||||
File snowflakeFile = getSnowflakeExecutableFile();
|
||||
extract(getSnowflakeInputStream(), snowflakeFile);
|
||||
if (!snowflakeFile.setExecutable(true, true)) throw new IOException();
|
||||
}
|
||||
|
||||
private InputStream getTorInputStream() throws IOException {
|
||||
InputStream in = resourceProvider
|
||||
.getResourceInputStream("tor_" + architecture, ".zip");
|
||||
ZipInputStream zin = new ZipInputStream(in);
|
||||
if (zin.getNextEntry() == null) throw new IOException();
|
||||
return zin;
|
||||
return getZipInputStream("tor");
|
||||
}
|
||||
|
||||
private InputStream getObfs4InputStream() throws IOException {
|
||||
return getZipInputStream("obfs4proxy");
|
||||
}
|
||||
|
||||
private InputStream getSnowflakeInputStream() throws IOException {
|
||||
return getZipInputStream("snowflake");
|
||||
}
|
||||
|
||||
private InputStream getZipInputStream(String basename) throws IOException {
|
||||
InputStream in = resourceProvider
|
||||
.getResourceInputStream("obfs4proxy_" + architecture, ".zip");
|
||||
.getResourceInputStream(basename + "_" + architecture, ".zip");
|
||||
ZipInputStream zin = new ZipInputStream(in);
|
||||
if (zin.getNextEntry() == null) throw new IOException();
|
||||
return zin;
|
||||
@@ -402,6 +420,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
String obfs4Path = getObfs4ExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin obfs4 exec", obfs4Path);
|
||||
append(strb, "ClientTransportPlugin meek_lite exec", obfs4Path);
|
||||
String snowflakePath = getSnowflakeExecutableFile().getAbsolutePath();
|
||||
append(strb, "ClientTransportPlugin snowflake exec", snowflakePath);
|
||||
//noinspection CharsetObjectCanBeUsed
|
||||
return new ByteArrayInputStream(
|
||||
strb.toString().getBytes(Charset.forName("UTF-8")));
|
||||
@@ -559,7 +579,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
private void enableBridges(List<BridgeType> bridgeTypes)
|
||||
private void enableBridges(List<BridgeType> bridgeTypes, String countryCode)
|
||||
throws IOException {
|
||||
if (!state.setBridgeTypes(bridgeTypes)) return; // Unchanged
|
||||
try {
|
||||
@@ -569,8 +589,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
} else {
|
||||
Collection<String> conf = new ArrayList<>();
|
||||
conf.add("UseBridges 1");
|
||||
boolean letsEncrypt = canVerifyLetsEncryptCerts();
|
||||
for (BridgeType bridgeType : bridgeTypes) {
|
||||
conf.addAll(circumventionProvider.getBridges(bridgeType));
|
||||
conf.addAll(circumventionProvider
|
||||
.getBridges(bridgeType, countryCode, letsEncrypt));
|
||||
}
|
||||
controlConnection.setConf(conf);
|
||||
}
|
||||
@@ -579,6 +601,15 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this device can verify Let's Encrypt certificates signed
|
||||
* with the IdentTrust DST Root X3 certificate, which expired at the end of
|
||||
* September 2021.
|
||||
*/
|
||||
protected boolean canVerifyLetsEncryptCerts() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
ServerSocket ss = state.setStopped();
|
||||
@@ -944,7 +975,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
|
||||
(automatic && bridgesWork)) {
|
||||
if (ipv6Only) {
|
||||
bridgeTypes = singletonList(MEEK);
|
||||
bridgeTypes = asList(MEEK, SNOWFLAKE);
|
||||
} else {
|
||||
bridgeTypes = circumventionProvider
|
||||
.getSuitableBridgeTypes(country);
|
||||
@@ -968,7 +999,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
||||
|
||||
try {
|
||||
if (enableNetwork) {
|
||||
enableBridges(bridgeTypes);
|
||||
enableBridges(bridgeTypes, country);
|
||||
enableConnectionPadding(enableConnectionPadding);
|
||||
enableIpv6(ipv6Only);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.util.logging.Logger;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -233,7 +234,13 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
|
||||
ContactRemovedEvent c = (ContactRemovedEvent) e;
|
||||
if (c.getContactId().equals(contactId)) interrupt();
|
||||
} else if (e instanceof MessageSharedEvent) {
|
||||
generateOffer();
|
||||
MessageSharedEvent m = (MessageSharedEvent) e;
|
||||
// If the contact is present in the map (ie the value is not null)
|
||||
// and the value is true, the message's group is shared with the
|
||||
// contact and therefore the message may now be sendable
|
||||
if (m.getGroupVisibility().get(contactId) == TRUE) {
|
||||
generateOffer();
|
||||
}
|
||||
} else if (e instanceof GroupVisibilityUpdatedEvent) {
|
||||
GroupVisibilityUpdatedEvent g = (GroupVisibilityUpdatedEvent) e;
|
||||
if (g.getVisibility() == SHARED &&
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -11,23 +11,26 @@ d Bridge obfs4 209.148.46.65:443 74FAD13168806246602538555B5521A0383A1875 cert=s
|
||||
d Bridge obfs4 146.57.248.225:22 10A6CD36A537FCE513A322361547444B393989F0 cert=K1gDtDAIcUfeLqbstggjIw2rtgIKqdIhUlHp82XRqNSq/mtAjp1BIC9vHKJ2FAEpGssTPw iat-mode=0
|
||||
d Bridge obfs4 45.145.95.6:27015 C5B7CD6946FF10C5B3E89691A7D3F2C122D2117C cert=TD7PbUO0/0k6xYHMPW3vJxICfkMZNdkRrb63Zhl5j9dW3iRGiCx0A7mPhe5T2EDzQ35+Zw iat-mode=0
|
||||
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 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
|
||||
n Bridge obfs4 82.39.132.97:6969 F505EF4C41C77FFDC0C440C122A02129FBE25823 cert=bwNWuL7UYB9aiKajE1gkffylYx/EM4FjSZxIJ0pVT/xaR21xXlIdaXw7l+EYmC4nVIh2HQ iat-mode=0
|
||||
n Bridge obfs4 185.103.252.72:443 75F15E9339FF572F88F5588D429FEA379744BC53 cert=nOZ/SaRE3L1dChvjfe0Ks/wM/F8iFhwd3g2G5zgtcLB8x+wiZRWCwjRrbbiQyb3Gh2mxRQ iat-mode=0
|
||||
n Bridge obfs4 185.177.207.13:22662 928C1E4289A01F34C8FB423FC32C0E77EE0F8736 cert=p9L6+25s8bnfkye1ZxFeAE4mAGY7DH4Gaj7dxngIIzP9BtqrHHwZXdjMK0RVIQ34C7aqZw iat-mode=2
|
||||
n Bridge obfs4 207.181.229.55:40132 37FE8D782F5DD2BAEEDAAB8257B701344676B6DD cert=f5Hbfn3ToMzH170cV8DfLly3vRynveidfOfDcbradIDtbLDX15V2yQ8eEH2CPKQJmQR2Hg iat-mode=0
|
||||
n Bridge obfs4 76.255.201.112:8888 96CF36C2ECCFB7376AB6BE905BECD2C2AE8AEFCD cert=+q0pjaiM0JMqHL/BKqCRD+pjflaw/S406eUDF7CnFgamvQW3l2HVLJhQ6uX9P8zff0PLGg 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
|
||||
m Bridge meek_lite 192.0.2.2:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||
v Bridge 185.189.195.124:8199 A1F3EE78F9C2343668E68FEB84358A4C742831A5
|
||||
v Bridge 213.196.191.96:9060 05E222E2A8C234234FE0CEB58B08A93B8FC360DB
|
||||
v Bridge 75.100.92.154:22815 465E990FA8A752DDE861EDF31E42454ACC037AB4
|
||||
m Bridge meek_lite 192.0.2.2:80 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
|
||||
s Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72
|
||||
|
||||
4
bramble-core/src/main/resources/snowflake-params
Normal file
4
bramble-core/src/main/resources/snowflake-params
Normal file
@@ -0,0 +1,4 @@
|
||||
ZZ 1 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ front=cdn.sstatic.net ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
|
||||
ZZ 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:stun.l.google.com:19302,stun:stun.voip.blackberry.com:3478,stun:stun.altar.com.pl:3478,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.sonetel.net:3478,stun:stun.stunprotocol.org:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478
|
||||
TM 1 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479
|
||||
TM 0 url=https://snowflake-broker.azureedge.net/ front=ajax.aspnetcdn.com ice=stun:206.53.159.130:3479,stun:176.119.42.11:3479,stun:94.23.17.185:3479,stun:217.74.179.29:3479,stun:83.125.8.47:3479,stun:23.253.102.137:3479,stun:52.26.251.34:3479,stun:52.26.251.34:3479,stun:18.191.223.12:3479,stun:154.73.34.8:3479,stun:185.125.180.70:3479,stun:195.35.115.37:3479
|
||||
@@ -10,6 +10,8 @@ import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestSocksModule;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@@ -20,7 +22,9 @@ import dagger.Component;
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class
|
||||
BrambleCoreModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class
|
||||
})
|
||||
interface ContactExchangeIntegrationTestComponent
|
||||
extends BrambleCoreIntegrationTestEagerSingletons {
|
||||
|
||||
@@ -70,6 +70,7 @@ import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.concurrent.TimeUnit.HOURS;
|
||||
import static org.briarproject.bramble.api.db.DatabaseComponent.TIMER_NOT_STARTED;
|
||||
import static org.briarproject.bramble.api.record.Record.RECORD_HEADER_BYTES;
|
||||
@@ -283,12 +284,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
temporary, null);
|
||||
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// The message was added, so the listeners should be called
|
||||
// Broadcast events for message being added and changing state
|
||||
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
|
||||
oneOf(eventBus).broadcast(with(any(
|
||||
MessageStateChangedEvent.class)));
|
||||
if (shared)
|
||||
// If message is shared, get group visibility and broadcast event
|
||||
if (shared) {
|
||||
oneOf(database).getGroupVisibility(txn, groupId);
|
||||
will(returnValue(singletonMap(contactId, true)));
|
||||
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
|
||||
}
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
eventExecutor, shutdownManager);
|
||||
@@ -389,7 +394,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
|
||||
@@ -694,11 +699,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the message is in the DB (which it's not)
|
||||
exactly(15).of(database).startTransaction();
|
||||
exactly(16).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(15).of(database).containsMessage(txn, messageId);
|
||||
exactly(16).of(database).containsMessage(txn, messageId);
|
||||
will(returnValue(false));
|
||||
exactly(15).of(database).abortTransaction(txn);
|
||||
exactly(16).of(database).abortTransaction(txn);
|
||||
// Allow other checks to pass
|
||||
allowing(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
@@ -722,6 +727,14 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.getGroupId(transaction, messageId));
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.transaction(true, transaction ->
|
||||
db.getMessage(transaction, messageId));
|
||||
@@ -1396,14 +1409,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,
|
||||
@@ -1880,12 +1886,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
||||
oneOf(database).containsMessage(txn, messageId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getMessageDependents(txn, messageId);
|
||||
// broadcast for message added event
|
||||
// Broadcast events for message being added and changing state
|
||||
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
|
||||
oneOf(eventBus).broadcast(with(any(
|
||||
MessageStateChangedEvent.class)));
|
||||
if (shared)
|
||||
// If message is shared, get group visibility and broadcast event
|
||||
if (shared) {
|
||||
oneOf(database).getGroupVisibility(txn, groupId);
|
||||
will(returnValue(singletonMap(contactId, true)));
|
||||
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
|
||||
}
|
||||
// endTransaction()
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// close()
|
||||
|
||||
@@ -168,6 +168,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||
assertTrue(db.containsContact(txn, contactId));
|
||||
assertTrue(db.containsGroup(txn, groupId));
|
||||
assertTrue(db.containsMessage(txn, messageId));
|
||||
assertEquals(groupId, db.getGroupId(txn, messageId));
|
||||
assertArrayEquals(message.getBody(),
|
||||
db.getMessage(txn, messageId).getBody());
|
||||
|
||||
|
||||
@@ -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,6 +74,7 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
client.deassignContactForUpload(contactId);
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@@ -85,6 +89,7 @@ public class ContactMailboxClientTest extends BrambleMockTestCase {
|
||||
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(downloadWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,929 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.contact.ContactManager;
|
||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.db.TransactionManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxSettingsManager;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxStatus;
|
||||
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.OwnMailboxConnectionStatusEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
|
||||
import org.briarproject.bramble.api.plugin.Plugin.State;
|
||||
import org.briarproject.bramble.api.plugin.PluginManager;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportActiveEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.TransportInactiveEvent;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.DbExpectations;
|
||||
import org.briarproject.bramble.test.RunAction;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ENABLING;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.ID;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContact;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
|
||||
public class MailboxClientManagerTest extends BrambleMockTestCase {
|
||||
|
||||
private final Executor eventExecutor =
|
||||
context.mock(Executor.class, "eventExecutor");
|
||||
private final Executor dbExecutor =
|
||||
context.mock(Executor.class, "dbExecutor");
|
||||
private final TransactionManager db =
|
||||
context.mock(TransactionManager.class);
|
||||
private final ContactManager contactManager =
|
||||
context.mock(ContactManager.class);
|
||||
private final PluginManager pluginManager =
|
||||
context.mock(PluginManager.class);
|
||||
private final MailboxSettingsManager mailboxSettingsManager =
|
||||
context.mock(MailboxSettingsManager.class);
|
||||
private final MailboxUpdateManager mailboxUpdateManager =
|
||||
context.mock(MailboxUpdateManager.class);
|
||||
private final MailboxClientFactory mailboxClientFactory =
|
||||
context.mock(MailboxClientFactory.class);
|
||||
private final TorReachabilityMonitor reachabilityMonitor =
|
||||
context.mock(TorReachabilityMonitor.class);
|
||||
private final DuplexPlugin plugin = context.mock(DuplexPlugin.class);
|
||||
private final MailboxClient ownClient =
|
||||
context.mock(MailboxClient.class, "ownClient");
|
||||
private final MailboxClient contactClient =
|
||||
context.mock(MailboxClient.class, "contactClient");
|
||||
|
||||
private final MailboxClientManager manager =
|
||||
new MailboxClientManager(eventExecutor, dbExecutor, db,
|
||||
contactManager, pluginManager, mailboxSettingsManager,
|
||||
mailboxUpdateManager, mailboxClientFactory,
|
||||
reachabilityMonitor);
|
||||
|
||||
private final Contact contact = getContact();
|
||||
private final List<MailboxVersion> incompatibleVersions =
|
||||
singletonList(new MailboxVersion(999, 0));
|
||||
private final MailboxProperties ownProperties =
|
||||
getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
private final MailboxProperties localProperties =
|
||||
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
private final MailboxProperties remoteProperties =
|
||||
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
private final MailboxProperties remotePropertiesForNewMailbox =
|
||||
getMailboxProperties(false, CLIENT_SUPPORTS);
|
||||
private final MailboxUpdate localUpdateWithoutMailbox =
|
||||
new MailboxUpdate(CLIENT_SUPPORTS);
|
||||
private final MailboxUpdate remoteUpdateWithoutMailbox =
|
||||
new MailboxUpdate(CLIENT_SUPPORTS);
|
||||
private final MailboxUpdate remoteUpdateWithIncompatibleClientVersions =
|
||||
new MailboxUpdate(incompatibleVersions);
|
||||
private final MailboxUpdateWithMailbox localUpdateWithMailbox =
|
||||
new MailboxUpdateWithMailbox(CLIENT_SUPPORTS, localProperties);
|
||||
private final MailboxUpdateWithMailbox remoteUpdateWithMailbox =
|
||||
new MailboxUpdateWithMailbox(CLIENT_SUPPORTS, remoteProperties);
|
||||
private final MailboxUpdateWithMailbox remoteUpdateWithNewMailbox =
|
||||
new MailboxUpdateWithMailbox(CLIENT_SUPPORTS,
|
||||
remotePropertiesForNewMailbox);
|
||||
|
||||
@Test
|
||||
public void testLoadsMailboxUpdatesAtStartupWhenOffline() throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We're offline so there's nothing
|
||||
// else to do.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithoutMailbox, null);
|
||||
expectCheckPluginState(ENABLING);
|
||||
manager.startService();
|
||||
|
||||
// At shutdown there should be no clients to destroy. The manager
|
||||
// should destroy the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadsMailboxUpdatesAtStartupWhenOnline() throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We're online but we don't have a
|
||||
// mailbox and neither does the contact, so there's nothing else to do.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithoutMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
manager.startService();
|
||||
|
||||
// At shutdown there should be no clients to destroy. The manager
|
||||
// should destroy the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToOurMailboxIfContactHasNoMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't.
|
||||
//
|
||||
// We're online, so the manager should create a client for our own
|
||||
// mailbox and assign the contact to it for upload and download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithoutMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
manager.startService();
|
||||
|
||||
// At shutdown the manager should destroy the client for our own
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotAssignContactToOurMailboxIfContactHasNotSentUpdate()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// has never sent us an update, so the remote update is null.
|
||||
//
|
||||
// We're online, so the manager should create a client for our own
|
||||
// mailbox. We don't know what API versions the contact supports,
|
||||
// if any, so the contact should not be assigned to our mailbox.
|
||||
expectLoadUpdates(localUpdateWithMailbox, null, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
manager.startService();
|
||||
|
||||
// At shutdown the manager should destroy the client for our own
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotAssignContactToOurMailboxIfContactHasIncompatibleClientVersions()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't. The contact's client API versions are incompatible with
|
||||
// our mailbox.
|
||||
//
|
||||
// We're online, so the manager should create a client for our own
|
||||
// mailbox. The contact's client API versions are incompatible with
|
||||
// our mailbox, so the contact should not be assigned to our mailbox.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithIncompatibleClientVersions, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
manager.startService();
|
||||
|
||||
// At shutdown the manager should destroy the client for our own
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToContactMailboxIfWeHaveNoMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We don't have a mailbox but the
|
||||
// contact does.
|
||||
//
|
||||
// We're online, so the manager should create a client for the
|
||||
// contact's mailbox and assign the contact to it for upload and
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
manager.startService();
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToBothMailboxesIfWeBothHaveMailboxes()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox and so does the
|
||||
// contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox, the client for our own mailbox, and the reachability
|
||||
// monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatesClientsWhenTorBecomesActive() throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't. We're offline so there's nothing else to do.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithoutMailbox, ownProperties);
|
||||
expectCheckPluginState(ENABLING);
|
||||
manager.startService();
|
||||
|
||||
// When we come online, the manager should create a client for our own
|
||||
// mailbox and assign the contact to it for upload and download.
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
manager.eventOccurred(new TransportActiveEvent(ID));
|
||||
|
||||
// When we go offline, the manager should destroy the client for our
|
||||
// own mailbox.
|
||||
expectDestroyClientForOwnMailbox();
|
||||
manager.eventOccurred(new TransportInactiveEvent(ID));
|
||||
|
||||
// At shutdown the manager should destroy the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToOurMailboxWhenPaired() throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We're online but we don't have a
|
||||
// mailbox and neither does the contact, so there's nothing else to do.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithoutMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
manager.startService();
|
||||
|
||||
// When we pair a mailbox, the manager should create a client for our
|
||||
// mailbox and assign the contact to our mailbox for upload and
|
||||
// download.
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.eventOccurred(new MailboxPairedEvent(ownProperties,
|
||||
singletonMap(contact.getId(), localUpdateWithMailbox)));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactToOurMailboxWhenPaired() throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. The contact has a mailbox but we
|
||||
// don't.
|
||||
//
|
||||
// We're online, so the manager should create a client for the
|
||||
// contact's mailbox and assign the contact to it for upload and
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
manager.startService();
|
||||
|
||||
// When we pair a mailbox, the manager should create a client for our
|
||||
// mailbox and reassign the contact to our mailbox for download.
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectDeassignContactFromContactMailboxForDownload();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.eventOccurred(new MailboxPairedEvent(ownProperties,
|
||||
singletonMap(contact.getId(), localUpdateWithMailbox)));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox, the client for our own mailbox, and the reachability
|
||||
// monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotAssignContactWhenPairedIfContactHasNotSentUpdate()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We don't have a mailbox and the
|
||||
// contact has never sent us an update, so the remote update is null.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox, null, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
manager.startService();
|
||||
|
||||
// When we pair a mailbox, the manager should create a client for our
|
||||
// mailbox. We don't know whether the contact can use our mailbox, so
|
||||
// the contact should not be assigned to our mailbox.
|
||||
expectCreateClientForOwnMailbox();
|
||||
manager.eventOccurred(new MailboxPairedEvent(ownProperties,
|
||||
singletonMap(contact.getId(), localUpdateWithMailbox)));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotAssignContactWhenPairedIfContactHasIncompatibleClientVersions()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We don't have a mailbox and neither
|
||||
// does the contact. The contact's client API versions are
|
||||
// incompatible with our mailbox.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithIncompatibleClientVersions, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
manager.startService();
|
||||
|
||||
// When we pair a mailbox, the manager should create a client for our
|
||||
// mailbox. The contact's client API versions are incompatible with
|
||||
// our mailbox, so the contact should not be assigned to our mailbox.
|
||||
expectCreateClientForOwnMailbox();
|
||||
manager.eventOccurred(new MailboxPairedEvent(ownProperties,
|
||||
singletonMap(contact.getId(), localUpdateWithMailbox)));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactToContactMailboxWhenUnpaired()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox and so does the
|
||||
// contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When we unpair our mailbox, the manager should destroy the client
|
||||
// for our mailbox and reassign the contact to the contact's mailbox
|
||||
// for download.
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
manager.eventOccurred(new MailboxUnpairedEvent(
|
||||
singletonMap(contact.getId(), localUpdateWithoutMailbox)));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeassignsContactForUploadAndDownloadWhenContactRemoved()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't.
|
||||
//
|
||||
// We're online, so the manager should create a client for our mailbox.
|
||||
// The manager should assign the contact to our mailbox for upload and
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithoutMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When the contact is removed, the manager should deassign the contact
|
||||
// from our mailbox for upload and download.
|
||||
expectDeassignContactFromOwnMailboxForUpload();
|
||||
expectDeassignContactFromOwnMailboxForDownload();
|
||||
manager.eventOccurred(new ContactRemovedEvent(contact.getId()));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeassignsContactForDownloadWhenContactRemoved()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox and so does the
|
||||
// contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When the contact is removed, the manager should destroy the client
|
||||
// for the contact's mailbox and deassign the contact from our mailbox
|
||||
// for download.
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDeassignContactFromOwnMailboxForDownload();
|
||||
manager.eventOccurred(new ContactRemovedEvent(contact.getId()));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToContactMailboxWhenContactPairsMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We're online but we don't have a
|
||||
// mailbox and neither does the contact, so there's nothing else to do.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithoutMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
manager.startService();
|
||||
|
||||
// When the contact pairs a mailbox, the manager should create a client
|
||||
// for the contact's mailbox and assign the contact to the contact's
|
||||
// mailbox for upload and download.
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactForUploadWhenContactPairsMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't.
|
||||
//
|
||||
// We're online, so the manager should create a client for our mailbox.
|
||||
// The manager should assign the contact to our mailbox for upload and
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithoutMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When the contact pairs a mailbox, the manager should create a client
|
||||
// for the contact's mailbox and reassign the contact to the contact's
|
||||
// mailbox for upload.
|
||||
expectCreateClientForContactMailbox();
|
||||
expectDeassignContactFromOwnMailboxForUpload();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox, the client for our own mailbox, and the reachability
|
||||
// monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactForUploadWhenContactUnpairsMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox and so does the
|
||||
// contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When the contact unpairs their mailbox, the manager should destroy
|
||||
// the client for the contact's mailbox and reassign the contact to
|
||||
// our mailbox for upload.
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithoutMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for our mailbox
|
||||
// and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactForUploadWhenContactReplacesMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox and so does the
|
||||
// contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When the contact replaces their mailbox, the manager should replace
|
||||
// the client for the contact's mailbox and assign the contact to
|
||||
// the contact's new mailbox for upload.
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(
|
||||
remotePropertiesForNewMailbox);
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithNewMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox, the client for our own mailbox, and the reachability
|
||||
// monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReassignsContactWhenContactReplacesMailbox()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We don't have a mailbox but the
|
||||
// contact does.
|
||||
//
|
||||
// We're online, so the manager should create a client for the
|
||||
// contact's mailbox and assign the contact to the contact's mailbox
|
||||
// for upload and download.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
manager.startService();
|
||||
|
||||
// When the contact replaces their mailbox, the manager should replace
|
||||
// the client for the contact's mailbox and assign the contact to
|
||||
// the contact's new mailbox for upload and download.
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(
|
||||
remotePropertiesForNewMailbox);
|
||||
expectAssignContactToContactMailboxForDownload(
|
||||
remotePropertiesForNewMailbox);
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithNewMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotReassignContactWhenRemotePropertiesAreUnchanged()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We don't have a mailbox but the
|
||||
// contact does.
|
||||
//
|
||||
// We're online, so the manager should create a client for the
|
||||
// contact's mailbox and assign the contact to the contact's mailbox
|
||||
// for upload and download.
|
||||
expectLoadUpdates(localUpdateWithoutMailbox,
|
||||
remoteUpdateWithMailbox, null);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectAssignContactToContactMailboxForDownload(remoteProperties);
|
||||
manager.startService();
|
||||
|
||||
// When the contact sends an update with unchanged properties, the
|
||||
// clients and assignments should not be affected.
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignsContactToOurMailboxWhenClientVersionsBecomeCompatible()
|
||||
throws Exception {
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. We have a mailbox but the contact
|
||||
// doesn't. The contact's client API versions are incompatible with
|
||||
// our mailbox.
|
||||
//
|
||||
// We're online, so the manager should create a client for our own
|
||||
// mailbox. The contact's client API versions are incompatible with
|
||||
// our mailbox, so the contact should not be assigned to our mailbox.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithIncompatibleClientVersions, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForOwnMailbox();
|
||||
manager.startService();
|
||||
|
||||
// When the contact sends an update indicating that their client API
|
||||
// versions are now compatible with our mailbox, the manager should
|
||||
// assign the contact to our mailbox for upload and download.
|
||||
expectAssignContactToOwnMailboxForUpload();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.eventOccurred(new RemoteMailboxUpdateEvent(contact.getId(),
|
||||
remoteUpdateWithoutMailbox));
|
||||
|
||||
// At shutdown the manager should destroy the client for our own
|
||||
// mailbox and the reachability monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecreatesClientsWhenOwnMailboxServerVersionsChange()
|
||||
throws Exception {
|
||||
long now = System.currentTimeMillis();
|
||||
List<MailboxVersion> compatibleVersions =
|
||||
new ArrayList<>(CLIENT_SUPPORTS);
|
||||
compatibleVersions.add(new MailboxVersion(999, 0));
|
||||
MailboxStatus mailboxStatus =
|
||||
new MailboxStatus(now, now, 0, compatibleVersions);
|
||||
|
||||
// At startup the manager should load the local and remote updates
|
||||
// and our own mailbox properties. The contact has a mailbox, so the
|
||||
// remote update contains the properties received from the contact.
|
||||
// We also have a mailbox, so the local update contains the properties
|
||||
// we sent to the contact.
|
||||
//
|
||||
// We're online, so the manager should create clients for the
|
||||
// contact's mailbox and our mailbox. The manager should assign the
|
||||
// contact to the contact's mailbox for upload and our mailbox for
|
||||
// download.
|
||||
expectLoadUpdates(localUpdateWithMailbox,
|
||||
remoteUpdateWithMailbox, ownProperties);
|
||||
expectCheckPluginState(ACTIVE);
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.startService();
|
||||
|
||||
// When we learn that our mailbox's API versions have changed, the
|
||||
// manager should destroy and recreate the clients for our own mailbox
|
||||
// and the contact's mailbox and assign the contact to the new clients.
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectCreateClientForContactMailbox();
|
||||
expectAssignContactToContactMailboxForUpload(remoteProperties);
|
||||
expectCreateClientForOwnMailbox();
|
||||
expectAssignContactToOwnMailboxForDownload();
|
||||
manager.eventOccurred(
|
||||
new OwnMailboxConnectionStatusEvent(mailboxStatus));
|
||||
|
||||
// At shutdown the manager should destroy the client for the contact's
|
||||
// mailbox, the client for our own mailbox, and the reachability
|
||||
// monitor.
|
||||
expectRunTaskOnEventExecutor();
|
||||
expectDestroyClientForContactMailbox();
|
||||
expectDestroyClientForOwnMailbox();
|
||||
expectDestroyReachabilityMonitor();
|
||||
manager.stopService();
|
||||
}
|
||||
|
||||
private void expectLoadUpdates(MailboxUpdate local,
|
||||
@Nullable MailboxUpdate remote,
|
||||
@Nullable MailboxProperties own) throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(reachabilityMonitor).start();
|
||||
oneOf(dbExecutor).execute(with(any(Runnable.class)));
|
||||
will(new RunAction());
|
||||
oneOf(db).transaction(with(true), withDbRunnable(txn));
|
||||
oneOf(contactManager).getContacts(txn);
|
||||
will(returnValue(singletonList(contact)));
|
||||
oneOf(mailboxUpdateManager).getLocalUpdate(txn, contact.getId());
|
||||
will(returnValue(local));
|
||||
oneOf(mailboxUpdateManager).getRemoteUpdate(txn, contact.getId());
|
||||
will(returnValue(remote));
|
||||
oneOf(mailboxSettingsManager).getOwnMailboxProperties(txn);
|
||||
will(returnValue(own));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCheckPluginState(State state) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(pluginManager).getPlugin(ID);
|
||||
will(returnValue(plugin));
|
||||
oneOf(plugin).getState();
|
||||
will(returnValue(state));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCreateClientForOwnMailbox() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxClientFactory).createOwnMailboxClient(
|
||||
reachabilityMonitor, ownProperties);
|
||||
will(returnValue(ownClient));
|
||||
oneOf(ownClient).start();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCreateClientForContactMailbox() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(mailboxClientFactory)
|
||||
.createContactMailboxClient(reachabilityMonitor);
|
||||
will(returnValue(contactClient));
|
||||
oneOf(contactClient).start();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectAssignContactToOwnMailboxForDownload() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ownClient).assignContactForDownload(contact.getId(),
|
||||
ownProperties,
|
||||
requireNonNull(localProperties.getOutboxId()));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectAssignContactToOwnMailboxForUpload() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ownClient).assignContactForUpload(contact.getId(),
|
||||
ownProperties,
|
||||
requireNonNull(localProperties.getInboxId()));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectAssignContactToContactMailboxForDownload(
|
||||
MailboxProperties remoteProperties) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactClient).assignContactForDownload(contact.getId(),
|
||||
remoteProperties,
|
||||
requireNonNull(remoteProperties.getInboxId()));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectAssignContactToContactMailboxForUpload(
|
||||
MailboxProperties remoteProperties) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactClient).assignContactForUpload(contact.getId(),
|
||||
remoteProperties,
|
||||
requireNonNull(remoteProperties.getOutboxId()));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDeassignContactFromOwnMailboxForUpload() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ownClient).deassignContactForUpload(contact.getId());
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDeassignContactFromOwnMailboxForDownload() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ownClient).deassignContactForDownload(contact.getId());
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDeassignContactFromContactMailboxForDownload() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactClient).deassignContactForDownload(contact.getId());
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectRunTaskOnEventExecutor() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(eventExecutor).execute(with(any(Runnable.class)));
|
||||
will(new RunAction());
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyClientForOwnMailbox() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ownClient).destroy();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyClientForContactMailbox() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(contactClient).destroy();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyReachabilityMonitor() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(reachabilityMonitor).destroy();
|
||||
}});
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFileId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
@@ -11,6 +13,7 @@ import org.briarproject.bramble.mailbox.MailboxApi.MailboxFile;
|
||||
import org.briarproject.bramble.mailbox.MailboxApi.TolerableFailureException;
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -22,11 +25,14 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.briarproject.bramble.mailbox.MailboxIntegrationTestUtils.SETUP_TOKEN;
|
||||
import static org.briarproject.bramble.mailbox.MailboxIntegrationTestUtils.URL_BASE;
|
||||
import static org.briarproject.bramble.mailbox.MailboxIntegrationTestUtils.createMailboxApi;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
|
||||
@@ -34,31 +40,58 @@ import static org.briarproject.bramble.test.TestUtils.readBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.writeBytes;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
public class MailboxApiIntegrationTest extends BrambleTestCase {
|
||||
public class MailboxIntegrationTest extends BrambleTestCase {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder folder = new TemporaryFolder();
|
||||
|
||||
private static final MailboxApi api = createMailboxApi();
|
||||
private static final String URL_BASE = "http://127.0.0.1:8000";
|
||||
private static final MailboxAuthToken SETUP_TOKEN;
|
||||
|
||||
static {
|
||||
try {
|
||||
SETUP_TOKEN = MailboxAuthToken.fromString(
|
||||
"54686973206973206120736574757020746f6b656e20666f722042726961722e");
|
||||
} catch (InvalidMailboxIdException e) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private static final OkHttpClient client = new OkHttpClient.Builder()
|
||||
.socketFactory(SocketFactory.getDefault())
|
||||
.connectTimeout(60_000, MILLISECONDS)
|
||||
.build();
|
||||
private static final WeakSingletonProvider<OkHttpClient>
|
||||
httpClientProvider =
|
||||
new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public OkHttpClient createInstance() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
// We aren't using a real onion address, so use the given address verbatim
|
||||
private static final UrlConverter urlConverter = onion -> onion;
|
||||
private static final MailboxApiImpl api =
|
||||
new MailboxApiImpl(httpClientProvider, urlConverter);
|
||||
// needs to be static to keep values across different tests
|
||||
private static MailboxProperties ownerProperties;
|
||||
|
||||
/**
|
||||
* Called before each test to make sure the mailbox is setup once
|
||||
* before starting with individual tests.
|
||||
* {@link BeforeClass} needs to be static, so we can't use the API class.
|
||||
*/
|
||||
@BeforeClass
|
||||
public static void setUp() throws IOException, ApiException {
|
||||
@Before
|
||||
public void ensureSetup() throws IOException, ApiException {
|
||||
// Skip this test unless it's explicitly enabled in the environment
|
||||
assumeTrue(isOptionalTestEnabled(MailboxApiIntegrationTest.class));
|
||||
assumeTrue(isOptionalTestEnabled(MailboxIntegrationTest.class));
|
||||
|
||||
assertNull(ownerProperties);
|
||||
if (ownerProperties != null) return;
|
||||
MailboxProperties setupProperties = new MailboxProperties(
|
||||
URL_BASE, SETUP_TOKEN, new ArrayList<>());
|
||||
ownerProperties = api.setup(setupProperties);
|
||||
@@ -67,7 +100,7 @@ public class MailboxApiIntegrationTest extends BrambleTestCase {
|
||||
@AfterClass
|
||||
// we can't test wiping as a regular test as it stops the mailbox
|
||||
public static void wipe() throws IOException, ApiException {
|
||||
if (!isOptionalTestEnabled(MailboxApiIntegrationTest.class)) return;
|
||||
if (!isOptionalTestEnabled(MailboxIntegrationTest.class)) return;
|
||||
|
||||
api.wipeMailbox(ownerProperties);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.BrambleIntegrationTestComponent;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class
|
||||
})
|
||||
interface MailboxIntegrationTestComponent extends
|
||||
BrambleIntegrationTestComponent {
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||
import org.briarproject.bramble.api.WeakSingletonProvider;
|
||||
import org.briarproject.bramble.api.mailbox.InvalidMailboxIdException;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxAuthToken;
|
||||
import org.briarproject.bramble.io.IoModule;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.inject.Singleton;
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
class MailboxIntegrationTestUtils {
|
||||
|
||||
static final String URL_BASE = "http://127.0.0.1:8000";
|
||||
static final MailboxAuthToken SETUP_TOKEN;
|
||||
|
||||
static {
|
||||
try {
|
||||
SETUP_TOKEN = MailboxAuthToken.fromString(
|
||||
"54686973206973206120736574757020746f6b656e20666f722042726961722e");
|
||||
} catch (InvalidMailboxIdException e) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
static WeakSingletonProvider<OkHttpClient> createHttpClientProvider() {
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.socketFactory(SocketFactory.getDefault())
|
||||
.connectTimeout(60_000, MILLISECONDS)
|
||||
.build();
|
||||
return new WeakSingletonProvider<OkHttpClient>() {
|
||||
@Override
|
||||
@Nonnull
|
||||
public OkHttpClient createInstance() {
|
||||
return client;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static MailboxApi createMailboxApi() {
|
||||
return new MailboxApiImpl(createHttpClientProvider(), onion -> onion);
|
||||
}
|
||||
|
||||
static MailboxIntegrationTestComponent createTestComponent() {
|
||||
MailboxIntegrationTestComponent component =
|
||||
DaggerMailboxIntegrationTestComponent
|
||||
.builder()
|
||||
.ioModule(new TestIoModule())
|
||||
.mailboxModule(new TestMailboxModule())
|
||||
.build();
|
||||
BrambleCoreEagerSingletons.Helper.injectEagerSingletons(component);
|
||||
return component;
|
||||
}
|
||||
|
||||
@Module
|
||||
static class TestIoModule extends IoModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
WeakSingletonProvider<OkHttpClient> provideOkHttpClientProvider() {
|
||||
return createHttpClientProvider();
|
||||
}
|
||||
}
|
||||
|
||||
@Module
|
||||
static class TestMailboxModule extends MailboxModule {
|
||||
|
||||
@Provides
|
||||
UrlConverter provideUrlConverter() {
|
||||
return onion -> onion;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,12 @@ 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 +41,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,7 +50,10 @@ 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 Settings pairedSettings;
|
||||
private final ContactId contactId1 = new ContactId(random.nextInt());
|
||||
private final ContactId contactId2 = new ContactId(random.nextInt());
|
||||
private final ContactId contactId3 = new ContactId(random.nextInt());
|
||||
@@ -56,6 +62,14 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
private final long lastSuccess = now - 2345;
|
||||
private final int attempts = 123;
|
||||
|
||||
public MailboxSettingsManagerImplTest() {
|
||||
pairedSettings = new Settings();
|
||||
pairedSettings.put(SETTINGS_KEY_ONION, onion);
|
||||
pairedSettings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||
pairedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS,
|
||||
serverSupportsInts);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReturnsNullPropertiesIfSettingsAreEmpty() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
@@ -72,14 +86,10 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testReturnsProperties() throws Exception {
|
||||
Transaction txn = new Transaction(null, true);
|
||||
Settings settings = new Settings();
|
||||
settings.put(SETTINGS_KEY_ONION, onion);
|
||||
settings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||
settings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS, serverSupportsInts);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(settings));
|
||||
will(returnValue(pairedSettings));
|
||||
}});
|
||||
|
||||
MailboxProperties properties = manager.getOwnMailboxProperties(txn);
|
||||
@@ -93,20 +103,38 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testStoresProperties() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
manager.registerMailboxHook(hook);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).mergeSettings(txn, pairedSettings,
|
||||
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, onion);
|
||||
expectedSettings.put(SETTINGS_KEY_TOKEN, token.toString());
|
||||
expectedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS,
|
||||
serverSupportsInts);
|
||||
MailboxProperties properties = new MailboxProperties(onion, token,
|
||||
serverSupports);
|
||||
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.setOwnMailboxProperties(txn, properties);
|
||||
manager.removeOwnMailboxProperties(txn);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -147,63 +175,60 @@ 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);
|
||||
expectedSettings.putIntArray(SETTINGS_KEY_SERVER_SUPPORTS,
|
||||
serverSupportsInts);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(oldSettings));
|
||||
will(returnValue(pairedSettings));
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
}});
|
||||
|
||||
manager.recordSuccessfulConnection(txn, now);
|
||||
assertTrue(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
|
||||
public void testRecordsSuccessWithVersions() throws Exception {
|
||||
public void testDoesNotRecordSuccessIfNotPaired() 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);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).mergeSettings(txn, expectedSettings,
|
||||
SETTINGS_NAMESPACE);
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(new Settings()));
|
||||
}});
|
||||
|
||||
manager.recordSuccessfulConnection(txn, now, versions);
|
||||
hasEvent(txn, OwnMailboxConnectionStatusEvent.class);
|
||||
manager.recordSuccessfulConnection(txn, now, serverSupports);
|
||||
assertFalse(hasEvent(txn, OwnMailboxConnectionStatusEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordsFailureOnFirstAttempt() throws Exception {
|
||||
testRecordsFailure(new Settings(), 0);
|
||||
testRecordsFailure(pairedSettings, 0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecordsFailureOnLaterAttempt() throws Exception {
|
||||
Settings oldSettings = new Settings();
|
||||
oldSettings.putAll(pairedSettings);
|
||||
oldSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, lastAttempt);
|
||||
oldSettings.putLong(SETTINGS_KEY_LAST_SUCCESS, lastSuccess);
|
||||
oldSettings.putInt(SETTINGS_KEY_ATTEMPTS, attempts);
|
||||
testRecordsFailure(oldSettings, attempts);
|
||||
testRecordsFailure(oldSettings, attempts, lastSuccess);
|
||||
}
|
||||
|
||||
private void testRecordsFailure(Settings oldSettings, int oldAttempts)
|
||||
throws Exception {
|
||||
private void testRecordsFailure(Settings oldSettings, int oldAttempts,
|
||||
long lastSuccess) throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Settings expectedSettings = new Settings();
|
||||
expectedSettings.putLong(SETTINGS_KEY_LAST_ATTEMPT, now);
|
||||
@@ -217,6 +242,25 @@ public class MailboxSettingsManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
manager.recordFailedConnectionAttempt(txn, now);
|
||||
OwnMailboxConnectionStatusEvent e =
|
||||
getEvent(txn, OwnMailboxConnectionStatusEvent.class);
|
||||
MailboxStatus status = e.getStatus();
|
||||
assertEquals(now, status.getTimeOfLastAttempt());
|
||||
assertEquals(lastSuccess, status.getTimeOfLastSuccess());
|
||||
assertEquals(oldAttempts + 1, status.getAttemptsSinceSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotRecordFailureIfNotPaired() throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(settingsManager).getSettings(txn, SETTINGS_NAMESPACE);
|
||||
will(returnValue(new Settings()));
|
||||
}});
|
||||
|
||||
manager.recordFailedConnectionAttempt(txn, now);
|
||||
assertFalse(hasEvent(txn, OwnMailboxConnectionStatusEvent.class));
|
||||
}
|
||||
|
||||
@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.MailboxUpdateSentToNewContactEvent;
|
||||
import org.briarproject.bramble.api.mailbox.event.RemoteMailboxUpdateEvent;
|
||||
import org.briarproject.bramble.api.sync.Group;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
@@ -32,9 +36,12 @@ 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.data.BdfDictionary.NULL_VALUE;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.CLIENT_ID;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.GROUP_KEY_SENT_CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.GROUP_KEY_SENT_SERVER_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MAJOR_VERSION;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MSG_KEY_LOCAL;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxUpdateManager.MSG_KEY_VERSION;
|
||||
@@ -45,6 +52,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 +79,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();
|
||||
@@ -80,6 +93,8 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
private final BdfList newerClientSupports;
|
||||
private final List<MailboxVersion> someServerSupportsList;
|
||||
private final BdfList someServerSupports;
|
||||
private final List<MailboxVersion> newerServerSupportsList;
|
||||
private final BdfList newerServerSupports;
|
||||
private final BdfList emptyServerSupports = new BdfList();
|
||||
private final MailboxProperties updateProps;
|
||||
private final MailboxUpdateWithMailbox updateWithMailbox;
|
||||
@@ -105,6 +120,12 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
someServerSupports = BdfList.of(BdfList.of(
|
||||
someServerSupportsList.get(0).getMajor(),
|
||||
someServerSupportsList.get(0).getMinor()));
|
||||
newerServerSupportsList = singletonList(new MailboxVersion(
|
||||
someServerSupportsList.get(0).getMajor(),
|
||||
someServerSupportsList.get(0).getMinor() + 1));
|
||||
newerServerSupports = BdfList.of(BdfList.of(
|
||||
newerServerSupportsList.get(0).getMajor(),
|
||||
newerServerSupportsList.get(0).getMinor()));
|
||||
|
||||
updateNoMailbox = new MailboxUpdate(someClientSupportsList);
|
||||
|
||||
@@ -135,8 +156,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 +177,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 +186,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 +197,14 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
|
||||
assertFalse(hasEvent(txn, MailboxUpdateSentToNewContactEvent.class));
|
||||
}
|
||||
|
||||
@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 +226,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 +241,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 +252,14 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
|
||||
assertFalse(hasEvent(txn, MailboxUpdateSentToNewContactEvent.class));
|
||||
}
|
||||
|
||||
@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 +268,48 @@ 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);
|
||||
|
||||
assertFalse(hasEvent(txn1, MailboxUpdateSentToNewContactEvent.class));
|
||||
|
||||
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 +317,16 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
}});
|
||||
|
||||
t = createInstance(someClientSupportsList);
|
||||
t.onDatabaseOpened(txn);
|
||||
t.onDatabaseOpened(txn2);
|
||||
|
||||
assertFalse(hasEvent(txn2, MailboxUpdateSentToNewContactEvent.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 +335,43 @@ 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);
|
||||
|
||||
assertFalse(hasEvent(txn1, MailboxUpdateSentToNewContactEvent.class));
|
||||
|
||||
BdfDictionary metaDictionary = BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
@@ -357,66 +380,66 @@ 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);
|
||||
|
||||
assertFalse(hasEvent(txn2, MailboxUpdateSentToNewContactEvent.class));
|
||||
}
|
||||
|
||||
@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 +452,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 +461,24 @@ 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);
|
||||
|
||||
MailboxUpdateSentToNewContactEvent
|
||||
e = getEvent(txn, MailboxUpdateSentToNewContactEvent.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 +491,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 +506,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,
|
||||
someServerSupports, propsDict);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.addingContact(txn, contact);
|
||||
|
||||
MailboxUpdateSentToNewContactEvent
|
||||
e = getEvent(txn, MailboxUpdateSentToNewContactEvent.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 +539,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 +573,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 +632,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 +674,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());
|
||||
@@ -657,37 +693,55 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||
));
|
||||
BdfDictionary groupMetadata = new BdfDictionary();
|
||||
groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, someServerSupports);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// Generate mailbox properties for contact
|
||||
oneOf(crypto).generateUniqueId();
|
||||
will(returnValue(updateProps.getAuthToken()));
|
||||
oneOf(crypto).generateUniqueId();
|
||||
will(returnValue(updateProps.getInboxId()));
|
||||
oneOf(crypto).generateUniqueId();
|
||||
will(returnValue(updateProps.getOutboxId()));
|
||||
// Find latest update
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
// Replace latest update with new update
|
||||
expectStoreMessage(txn, contactGroupId, 2, someClientSupports,
|
||||
someServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
// Store sent server-supported versions
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
groupMetadata);
|
||||
}});
|
||||
|
||||
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, MailboxUpdateSentToNewContactEvent.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<>();
|
||||
@@ -701,30 +755,117 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
new BdfEntry(MSG_KEY_VERSION, 3),
|
||||
new BdfEntry(MSG_KEY_LOCAL, false)
|
||||
));
|
||||
BdfDictionary groupMetadata = new BdfDictionary();
|
||||
groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, NULL_VALUE);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// Find latest update
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
expectStoreMessage(txn, contactGroup.getId(), 2, someClientSupports,
|
||||
// Replace latest update with new update
|
||||
expectStoreMessage(txn, contactGroupId, 2, someClientSupports,
|
||||
emptyServerSupports, emptyPropsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
// Remove sent server-supported versions
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
groupMetadata);
|
||||
}});
|
||||
|
||||
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, MailboxUpdateSentToNewContactEvent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoresLocalUpdateWhenServerSupportsChange()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||
MessageId latestId = new MessageId(getRandomId());
|
||||
messageMetadata.put(latestId, BdfDictionary.of(
|
||||
new BdfEntry(MSG_KEY_VERSION, 1),
|
||||
new BdfEntry(MSG_KEY_LOCAL, true)
|
||||
));
|
||||
BdfList body = BdfList.of(1, someClientSupports, someServerSupports,
|
||||
propsDict);
|
||||
BdfDictionary oldGroupMetadata = new BdfDictionary();
|
||||
oldGroupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS,
|
||||
someServerSupports);
|
||||
BdfDictionary newGroupMetadata = new BdfDictionary();
|
||||
newGroupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS,
|
||||
newerServerSupports);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Load sent server-supported versions
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(oldGroupMetadata));
|
||||
oneOf(clientHelper).parseMailboxVersionList(someServerSupports);
|
||||
will(returnValue(someServerSupportsList));
|
||||
// Update sent server-supported versions
|
||||
oneOf(clientHelper).mergeGroupMetadata(txn, localGroup.getId(),
|
||||
newGroupMetadata);
|
||||
oneOf(db).getContacts(txn);
|
||||
will(returnValue(contacts));
|
||||
// Find latest update
|
||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroupId);
|
||||
will(returnValue(messageMetadata));
|
||||
// Load and parse latest update
|
||||
oneOf(clientHelper).getMessageAsList(txn, latestId);
|
||||
will(returnValue(body));
|
||||
oneOf(clientHelper).parseAndValidateMailboxUpdate(
|
||||
someClientSupports, someServerSupports, propsDict);
|
||||
will(returnValue(updateWithMailbox));
|
||||
// Replace latest update with new update
|
||||
expectStoreMessage(txn, contactGroupId, 2, someClientSupports,
|
||||
newerServerSupports, propsDict);
|
||||
oneOf(db).removeMessage(txn, latestId);
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.serverSupportedVersionsReceived(txn, newerServerSupportsList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotStoreLocalUpdateWhenServerSupportsAreUnchanged()
|
||||
throws Exception {
|
||||
Transaction txn = new Transaction(null, false);
|
||||
BdfDictionary groupMetadata = new BdfDictionary();
|
||||
groupMetadata.put(GROUP_KEY_SENT_SERVER_SUPPORTS, someServerSupports);
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
// Load sent server-supported versions
|
||||
oneOf(clientHelper).getGroupMetadataAsDictionary(txn,
|
||||
localGroup.getId());
|
||||
will(returnValue(groupMetadata));
|
||||
oneOf(clientHelper).parseMailboxVersionList(someServerSupports);
|
||||
will(returnValue(someServerSupportsList));
|
||||
}});
|
||||
|
||||
MailboxUpdateManagerImpl t = createInstance(someClientSupportsList);
|
||||
t.serverSupportedVersionsReceived(txn, someServerSupportsList);
|
||||
}
|
||||
|
||||
@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 +883,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 +901,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 +911,7 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
MAJOR_VERSION, contact);
|
||||
will(returnValue(contactGroup));
|
||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||
contactGroup.getId());
|
||||
contactGroupId);
|
||||
will(returnValue(emptyMessageMetadata));
|
||||
}});
|
||||
|
||||
@@ -783,8 +922,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 +939,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 +956,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 +973,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 +990,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 +1007,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 +1042,24 @@ public class MailboxUpdateManagerImplTest extends BrambleMockTestCase {
|
||||
false);
|
||||
}});
|
||||
}
|
||||
|
||||
private void assertNoMailboxPropertiesSent(
|
||||
MailboxUpdateSentToNewContactEvent e,
|
||||
List<MailboxVersion> clientSupports) {
|
||||
assertEquals(contact.getId(), e.getContactId());
|
||||
MailboxUpdate u = e.getMailboxUpdate();
|
||||
assertEquals(clientSupports, u.getClientSupports());
|
||||
assertFalse(u.hasMailbox());
|
||||
}
|
||||
|
||||
private void assertMailboxPropertiesSent(
|
||||
MailboxUpdateSentToNewContactEvent 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
import org.briarproject.bramble.api.connection.ConnectionRegistry;
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||
import org.briarproject.bramble.api.db.Transaction;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxFolderId;
|
||||
import org.briarproject.bramble.api.mailbox.MailboxProperties;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactConnectedEvent;
|
||||
import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.bramble.api.sync.OutgoingSessionRecord;
|
||||
import org.briarproject.bramble.api.sync.event.MessageSharedEvent;
|
||||
@@ -25,10 +29,13 @@ import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.MAX_LATENCY;
|
||||
@@ -50,6 +57,8 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
private final TaskScheduler taskScheduler =
|
||||
context.mock(TaskScheduler.class);
|
||||
private final EventBus eventBus = context.mock(EventBus.class);
|
||||
private final ConnectionRegistry connectionRegistry =
|
||||
context.mock(ConnectionRegistry.class);
|
||||
private final ConnectivityChecker connectivityChecker =
|
||||
context.mock(ConnectivityChecker.class);
|
||||
private final MailboxApiCaller mailboxApiCaller =
|
||||
@@ -72,6 +81,9 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
private final MessageId ackedId = new MessageId(getRandomId());
|
||||
private final MessageId sentId = new MessageId(getRandomId());
|
||||
private final MessageId newMessageId = new MessageId(getRandomId());
|
||||
private final GroupId groupId = new GroupId(getRandomId());
|
||||
private final Map<ContactId, Boolean> groupVisibility =
|
||||
singletonMap(contactId, true);
|
||||
|
||||
private File testDir, tempFile;
|
||||
private MailboxUploadWorker worker;
|
||||
@@ -81,8 +93,9 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
testDir = getTestDirectory();
|
||||
tempFile = new File(testDir, "temp");
|
||||
worker = new MailboxUploadWorker(ioExecutor, db, clock, taskScheduler,
|
||||
eventBus, connectivityChecker, mailboxApiCaller, mailboxApi,
|
||||
mailboxFileManager, mailboxProperties, folderId, contactId);
|
||||
eventBus, connectionRegistry, connectivityChecker,
|
||||
mailboxApiCaller, mailboxApi, mailboxFileManager,
|
||||
mailboxProperties, folderId, contactId);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -93,8 +106,11 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testChecksForDataWhenStartedAndRemovesObserverWhenDestroyed()
|
||||
throws Exception {
|
||||
// When the worker is started it should check for data to send
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.start();
|
||||
@@ -106,15 +122,59 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotCheckForDataWhenStartedIfConnectedToContact() {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're connected to the contact, so the worker should not check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(true);
|
||||
|
||||
worker.start();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksForDataWhenContactDisconnects() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're connected to the contact, so the worker should not check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(true);
|
||||
|
||||
worker.start();
|
||||
|
||||
// When the contact disconnects, the worker should start a task to
|
||||
// check for data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.eventOccurred(new ContactDisconnectedEvent(contactId));
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChecksConnectivityWhenStartedIfDataIsReady()
|
||||
throws Exception {
|
||||
Transaction recordTxn = new Transaction(null, false);
|
||||
|
||||
// When the worker is started it should check for data to send. As
|
||||
// there's data ready to send immediately, the worker should start a
|
||||
// connectivity check
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As there's data ready to send immediately, the worker
|
||||
// should start a connectivity check
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndStartConnectivityCheck();
|
||||
|
||||
worker.start();
|
||||
@@ -149,7 +209,9 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the upload task runs, it should upload the file, record
|
||||
// the acked/sent messages in the DB, and check for more data to send
|
||||
// the acked/sent messages in the DB, and check the connection
|
||||
// registry. We're not connected to the contact, so the worker should
|
||||
// check for more data to send
|
||||
context.checking(new DbExpectations() {{
|
||||
oneOf(mailboxApi).addFile(mailboxProperties, folderId, tempFile);
|
||||
oneOf(db).transaction(with(false), withDbRunnable(recordTxn));
|
||||
@@ -157,6 +219,7 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
oneOf(db).setMessagesSent(recordTxn, contactId,
|
||||
singletonList(sentId), MAX_LATENCY);
|
||||
}});
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
assertFalse(upload.get().callApi());
|
||||
@@ -172,11 +235,41 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsApiCallWhenDestroyed() throws Exception {
|
||||
// When the worker is started it should check for data to send. As
|
||||
// there's data ready to send immediately, the worker should start a
|
||||
// connectivity check
|
||||
public void testDoesNotWriteFileIfContactConnectsDuringConnectivityCheck()
|
||||
throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As there's data ready to send immediately, the worker
|
||||
// should start a connectivity check
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndStartConnectivityCheck();
|
||||
|
||||
worker.start();
|
||||
|
||||
// Before the connectivity check succeeds, we make a direct connection
|
||||
// to the contact
|
||||
worker.eventOccurred(new ContactConnectedEvent(contactId));
|
||||
|
||||
// When the connectivity check succeeds, the worker should not start
|
||||
// writing and uploading a file
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsApiCallWhenDestroyed() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As there's data ready to send immediately, the worker
|
||||
// should start a connectivity check
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndStartConnectivityCheck();
|
||||
|
||||
worker.start();
|
||||
@@ -212,9 +305,7 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener and cancel the upload task
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(apiCall).cancel();
|
||||
}});
|
||||
expectCancelTask(apiCall);
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
@@ -230,16 +321,21 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testSchedulesWakeupWhenStartedIfDataIsNotReady()
|
||||
throws Exception {
|
||||
// When the worker is started it should check for data to send. As
|
||||
// the data isn't ready to send immediately, the worker should
|
||||
// schedule a wakeup
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As the data isn't ready to send immediately, the
|
||||
// worker should schedule a wakeup
|
||||
expectRunTaskOnIoExecutor();
|
||||
AtomicReference<Runnable> wakeup = new AtomicReference<>();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndScheduleWakeup(wakeup);
|
||||
|
||||
worker.start();
|
||||
|
||||
// When the wakeup task runs it should check for data to send
|
||||
// When the wakeup task runs it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
wakeup.get().run();
|
||||
@@ -252,21 +348,51 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsWakeupIfDestroyedBeforeWakingUp() throws Exception {
|
||||
public void testCancelsWakeupIfContactConnectsBeforeWakingUp()
|
||||
throws Exception {
|
||||
// When the worker is started it should check for data to send. As
|
||||
// the data isn't ready to send immediately, the worker should
|
||||
// schedule a wakeup
|
||||
expectRunTaskOnIoExecutor();
|
||||
AtomicReference<Runnable> wakeup = new AtomicReference<>();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndScheduleWakeup(wakeup);
|
||||
|
||||
worker.start();
|
||||
|
||||
// Before the wakeup task runs, we make a direct connection to the
|
||||
// contact. The worker should cancel the wakeup task
|
||||
expectCancelTask(wakeupTask);
|
||||
|
||||
worker.eventOccurred(new ContactConnectedEvent(contactId));
|
||||
|
||||
// If the wakeup task runs anyway (cancellation came too late), it
|
||||
// should return without doing anything
|
||||
wakeup.get().run();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsWakeupIfDestroyedBeforeWakingUp() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As the data isn't ready to send immediately, the
|
||||
// worker should schedule a wakeup
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
AtomicReference<Runnable> wakeup = new AtomicReference<>();
|
||||
expectCheckForDataToSendAndScheduleWakeup(wakeup);
|
||||
|
||||
worker.start();
|
||||
|
||||
// When the worker is destroyed it should cancel the wakeup and
|
||||
// remove the connectivity observer and event listener
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(wakeupTask).cancel();
|
||||
}});
|
||||
expectCancelTask(wakeupTask);
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
@@ -279,10 +405,12 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
@Test
|
||||
public void testCancelsWakeupIfEventIsReceivedBeforeWakingUp()
|
||||
throws Exception {
|
||||
// When the worker is started it should check for data to send. As
|
||||
// the data isn't ready to send immediately, the worker should
|
||||
// schedule a wakeup
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As the data isn't ready to send immediately, the
|
||||
// worker should schedule a wakeup
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
AtomicReference<Runnable> wakeup = new AtomicReference<>();
|
||||
expectCheckForDataToSendAndScheduleWakeup(wakeup);
|
||||
|
||||
@@ -293,11 +421,10 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
// wakeup task and schedule a check for new data after a short delay
|
||||
AtomicReference<Runnable> check = new AtomicReference<>();
|
||||
expectScheduleCheck(check, CHECK_DELAY_MS);
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(wakeupTask).cancel();
|
||||
}});
|
||||
expectCancelTask(wakeupTask);
|
||||
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId));
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
groupVisibility));
|
||||
|
||||
// If the wakeup task runs anyway (cancellation came too late), it
|
||||
// should return early when it finds the state has changed
|
||||
@@ -306,9 +433,13 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
// Before the check task runs, the worker receives another event that
|
||||
// indicates new data may be available. The event should be ignored,
|
||||
// as a check for new data has already been scheduled
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId));
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
groupVisibility));
|
||||
|
||||
// When the check task runs, it should check for new data
|
||||
// When the check task runs, it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// new data
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
check.get().run();
|
||||
@@ -322,8 +453,11 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
public void testCancelsCheckWhenDestroyed() throws Exception {
|
||||
// When the worker is started it should check for data to send
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.start();
|
||||
@@ -334,13 +468,12 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
AtomicReference<Runnable> check = new AtomicReference<>();
|
||||
expectScheduleCheck(check, CHECK_DELAY_MS);
|
||||
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId));
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
groupVisibility));
|
||||
|
||||
// When the worker is destroyed it should cancel the check and
|
||||
// remove the connectivity observer and event listener
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(checkTask).cancel();
|
||||
}});
|
||||
expectCancelTask(checkTask);
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
@@ -350,13 +483,100 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
check.get().run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsCheckIfContactConnects() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.start();
|
||||
|
||||
// The worker receives an event that indicates new data may be
|
||||
// available. The worker should schedule a check for new data after
|
||||
// a short delay
|
||||
AtomicReference<Runnable> check = new AtomicReference<>();
|
||||
expectScheduleCheck(check, CHECK_DELAY_MS);
|
||||
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
groupVisibility));
|
||||
|
||||
// Before the check task runs, we make a direct connection to the
|
||||
// contact. The worker should cancel the check
|
||||
expectCancelTask(checkTask);
|
||||
|
||||
worker.eventOccurred(new ContactConnectedEvent(contactId));
|
||||
|
||||
// If the check runs anyway (cancellation came too late), it should
|
||||
// return early when it finds the state has changed
|
||||
check.get().run();
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotScheduleCheckIfGroupIsVisible() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.start();
|
||||
|
||||
// The worker receives an event that indicates new data may be
|
||||
// available. The group is visible to the contact but not shared, so
|
||||
// the worker should not schedule a check for new data
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
singletonMap(contactId, false)));
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoesNotScheduleCheckIfGroupIsInvisible() throws Exception {
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
worker.start();
|
||||
|
||||
// The worker receives an event that indicates new data may be
|
||||
// available. The group is not visible to the contact, so the worker
|
||||
// should not schedule a check for new data
|
||||
worker.eventOccurred(new MessageSharedEvent(newMessageId, groupId,
|
||||
emptyMap()));
|
||||
|
||||
// When the worker is destroyed it should remove the connectivity
|
||||
// observer and event listener
|
||||
expectRemoveObserverAndListener();
|
||||
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetriesAfterDelayIfExceptionOccursWhileWritingFile()
|
||||
throws Exception {
|
||||
// When the worker is started it should check for data to send. As
|
||||
// there's data ready to send immediately, the worker should start a
|
||||
// connectivity check
|
||||
// When the worker is started it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// data to send. As there's data ready to send immediately, the worker
|
||||
// should start a connectivity check
|
||||
expectRunTaskOnIoExecutor();
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendAndStartConnectivityCheck();
|
||||
|
||||
worker.start();
|
||||
@@ -375,7 +595,10 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
|
||||
worker.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the check task runs it should check for new data
|
||||
// When the check task runs it should check the connection registry.
|
||||
// We're not connected to the contact, so the worker should check for
|
||||
// new data
|
||||
expectCheckConnectionRegistry(false);
|
||||
expectCheckForDataToSendNoDataWaiting();
|
||||
|
||||
check.get().run();
|
||||
@@ -387,6 +610,13 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
worker.destroy();
|
||||
}
|
||||
|
||||
private void expectCheckConnectionRegistry(boolean connected) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectionRegistry).isConnected(contactId);
|
||||
will(returnValue(connected));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectRunTaskOnIoExecutor() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(ioExecutor).execute(with(any(Runnable.class)));
|
||||
@@ -456,6 +686,12 @@ public class MailboxUploadWorkerTest extends BrambleMockTestCase {
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectCancelTask(Cancellable task) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(task).cancel();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectRemoveObserverAndListener() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).removeObserver(worker);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.api.Cancellable;
|
||||
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.system.TaskScheduler;
|
||||
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.Test;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.briarproject.bramble.api.mailbox.MailboxConstants.CLIENT_SUPPORTS;
|
||||
import static org.briarproject.bramble.mailbox.OwnMailboxClient.CONNECTIVITY_CHECK_INTERVAL_MS;
|
||||
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||
import static org.briarproject.bramble.test.TestUtils.getMailboxProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||
@@ -20,6 +29,9 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
context.mock(ConnectivityChecker.class);
|
||||
private final TorReachabilityMonitor reachabilityMonitor =
|
||||
context.mock(TorReachabilityMonitor.class);
|
||||
private final TaskScheduler taskScheduler =
|
||||
context.mock(TaskScheduler.class);
|
||||
private final Executor ioExecutor = context.mock(Executor.class);
|
||||
private final MailboxWorker contactListWorker =
|
||||
context.mock(MailboxWorker.class, "contactListWorker");
|
||||
private final MailboxWorker uploadWorker1 =
|
||||
@@ -28,6 +40,8 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
context.mock(MailboxWorker.class, "uploadWorker2");
|
||||
private final MailboxWorker downloadWorker =
|
||||
context.mock(MailboxWorker.class, "downloadWorker");
|
||||
private final Cancellable connectivityCheck =
|
||||
context.mock(Cancellable.class);
|
||||
|
||||
private final MailboxProperties properties =
|
||||
getMailboxProperties(true, CLIENT_SUPPORTS);
|
||||
@@ -40,21 +54,24 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
public OwnMailboxClientTest() {
|
||||
expectCreateContactListWorker();
|
||||
client = new OwnMailboxClient(workerFactory, connectivityChecker,
|
||||
reachabilityMonitor, properties);
|
||||
reachabilityMonitor, taskScheduler, ioExecutor, properties);
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartAndDestroyWithNoContactsAssigned() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignContactForUploadAndDestroyClient() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
@@ -67,11 +84,13 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(uploadWorker1);
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignContactForUpload() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
@@ -87,11 +106,13 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignTwoContactsForUpload() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
@@ -120,11 +141,13 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignContactForDownloadAndDestroyClient() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
@@ -137,11 +160,13 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
// When the client is destroyed, the worker should be destroyed
|
||||
expectDestroyWorker(downloadWorker);
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssignAndDeassignTwoContactsForDownload() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
@@ -166,9 +191,72 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
context.assertIsSatisfied();
|
||||
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCancelsConnectivityCheckWhenClientIsDestroyed() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the first connectivity check succeeds, the worker should
|
||||
// schedule a second check
|
||||
AtomicReference<Runnable> task = new AtomicReference<>();
|
||||
expectScheduleConnectivityCheck(task);
|
||||
client.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the task runs, the worker should check the mailbox's
|
||||
// connectivity
|
||||
expectStartConnectivityCheck();
|
||||
task.get().run();
|
||||
|
||||
// When the second connectivity check succeeds, the worker should
|
||||
// schedule a third check
|
||||
expectScheduleConnectivityCheck(task);
|
||||
client.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the client is destroyed, the scheduled check should be cancelled
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityCheck).cancel();
|
||||
}});
|
||||
client.destroy();
|
||||
|
||||
// If the task runs anyway (cancellation came too late), it should
|
||||
// return when it finds that the client has been destroyed
|
||||
task.get().run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIgnoresConnectivityResultWhenClientIsDestroyed() {
|
||||
expectStartConnectivityCheck();
|
||||
expectStartWorker(contactListWorker);
|
||||
client.start();
|
||||
|
||||
// When the first connectivity check succeeds, the worker should
|
||||
// schedule a second check
|
||||
AtomicReference<Runnable> task = new AtomicReference<>();
|
||||
expectScheduleConnectivityCheck(task);
|
||||
client.onConnectivityCheckSucceeded();
|
||||
|
||||
// When the task runs, the worker should check the mailbox's
|
||||
// connectivity
|
||||
expectStartConnectivityCheck();
|
||||
task.get().run();
|
||||
|
||||
// Before the connectivity check succeeds, the client is destroyed
|
||||
expectDestroyWorker(contactListWorker);
|
||||
expectDestroyConnectivityChecker();
|
||||
client.destroy();
|
||||
|
||||
// If the connectivity check succeeds despite the connectivity checker
|
||||
// having been destroyed, the client should not schedule another check
|
||||
client.onConnectivityCheckSucceeded();
|
||||
}
|
||||
|
||||
private void expectCreateContactListWorker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(workerFactory).createContactListWorkerForOwnMailbox(
|
||||
@@ -200,9 +288,34 @@ public class OwnMailboxClientTest extends BrambleMockTestCase {
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectStartConnectivityCheck() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).checkConnectivity(properties, client);
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectScheduleConnectivityCheck(
|
||||
AtomicReference<Runnable> task) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(taskScheduler).schedule(with(any(Runnable.class)),
|
||||
with(ioExecutor), with(CONNECTIVITY_CHECK_INTERVAL_MS),
|
||||
with(TimeUnit.MILLISECONDS));
|
||||
will(new DoAllAction(
|
||||
new CaptureArgumentAction<>(task, Runnable.class, 0),
|
||||
returnValue(connectivityCheck)
|
||||
));
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyWorker(MailboxWorker worker) {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(worker).destroy();
|
||||
}});
|
||||
}
|
||||
|
||||
private void expectDestroyConnectivityChecker() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(connectivityChecker).destroy();
|
||||
}});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package org.briarproject.bramble.mailbox;
|
||||
|
||||
import org.briarproject.bramble.test.BrambleTestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.briarproject.bramble.mailbox.MailboxIntegrationTestUtils.createTestComponent;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class OwnMailboxContactListWorkerIntegrationTest
|
||||
extends BrambleTestCase {
|
||||
|
||||
private MailboxIntegrationTestComponent component;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
component = createTestComponent();
|
||||
}
|
||||
|
||||
// Just test that we can build the component. TODO: Write actual tests
|
||||
@Test
|
||||
public void testBuild() {
|
||||
assertTrue(true);
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,11 @@ import org.briarproject.bramble.event.DefaultEventExecutorModule;
|
||||
import org.briarproject.bramble.system.DefaultWakefulIoExecutorModule;
|
||||
import org.briarproject.bramble.system.TimeTravelModule;
|
||||
import org.briarproject.bramble.test.TestDatabaseConfigModule;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestFeatureFlagModule;
|
||||
import org.briarproject.bramble.test.TestMailboxDirectoryModule;
|
||||
import org.briarproject.bramble.test.TestSecureRandomModule;
|
||||
import org.briarproject.bramble.test.TestSocksModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -27,12 +29,14 @@ import dagger.Component;
|
||||
DefaultEventExecutorModule.class,
|
||||
DefaultWakefulIoExecutorModule.class,
|
||||
TestDatabaseConfigModule.class,
|
||||
TestDnsModule.class,
|
||||
TestFeatureFlagModule.class,
|
||||
TestMailboxDirectoryModule.class,
|
||||
RemovableDriveIntegrationTestModule.class,
|
||||
RemovableDriveModule.class,
|
||||
TestSecureRandomModule.class,
|
||||
TimeTravelModule.class
|
||||
TimeTravelModule.class,
|
||||
TestSocksModule.class
|
||||
})
|
||||
interface RemovableDriveIntegrationTestComponent
|
||||
extends BrambleCoreEagerSingletons {
|
||||
|
||||
@@ -12,16 +12,19 @@ import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BRIDGES;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DEFAULT_BRIDGES;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.DPI_BRIDGES;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.NON_DEFAULT_BRIDGES;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CircumventionProviderTest extends BrambleTestCase {
|
||||
public class CircumventionProviderImplTest extends BrambleTestCase {
|
||||
|
||||
private final CircumventionProvider provider =
|
||||
private final CircumventionProviderImpl provider =
|
||||
new CircumventionProviderImpl();
|
||||
|
||||
@Test
|
||||
@@ -56,13 +59,31 @@ public class CircumventionProviderTest extends BrambleTestCase {
|
||||
provider.getSuitableBridgeTypes(country));
|
||||
}
|
||||
for (String country : DPI_BRIDGES) {
|
||||
assertEquals(asList(NON_DEFAULT_OBFS4, MEEK),
|
||||
assertEquals(asList(NON_DEFAULT_OBFS4, MEEK, SNOWFLAKE),
|
||||
provider.getSuitableBridgeTypes(country));
|
||||
}
|
||||
assertEquals(asList(DEFAULT_OBFS4, VANILLA),
|
||||
provider.getSuitableBridgeTypes("ZZ"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSnowflakeParamsWithLetsEncrypt() {
|
||||
testHasSnowflakeParams(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSnowflakeParamsWithoutLetsEncrypt() {
|
||||
testHasSnowflakeParams(false);
|
||||
}
|
||||
|
||||
private void testHasSnowflakeParams(boolean letsEncrypt) {
|
||||
String tmParams = provider.getSnowflakeParams("TM", letsEncrypt);
|
||||
String defaultParams = provider.getSnowflakeParams("ZZ", letsEncrypt);
|
||||
assertFalse(tmParams.isEmpty());
|
||||
assertFalse(defaultParams.isEmpty());
|
||||
assertNotEquals(defaultParams, tmParams);
|
||||
}
|
||||
|
||||
private <T> void assertEmptyIntersection(Set<T> a, Set<T> b) {
|
||||
Set<T> intersection = new HashSet<>(a);
|
||||
intersection.retainAll(b);
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package org.briarproject.bramble.sync;
|
||||
import org.briarproject.bramble.BrambleCoreIntegrationTestEagerSingletons;
|
||||
import org.briarproject.bramble.BrambleCoreModule;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestSocksModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -11,7 +13,9 @@ import dagger.Component;
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class
|
||||
BrambleCoreModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class
|
||||
})
|
||||
interface SyncIntegrationTestComponent extends
|
||||
BrambleCoreIntegrationTestEagerSingletons {
|
||||
|
||||
@@ -14,7 +14,9 @@ import dagger.Component;
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class
|
||||
BrambleCoreModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class
|
||||
})
|
||||
public interface BrambleIntegrationTestComponent
|
||||
extends BrambleCoreIntegrationTestEagerSingletons {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.briar.test;
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
@@ -9,6 +9,8 @@ import org.briarproject.bramble.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.bramble.api.transport.KeyManager;
|
||||
import org.briarproject.bramble.test.BrambleCoreIntegrationTestModule;
|
||||
import org.briarproject.bramble.test.BrambleIntegrationTestComponent;
|
||||
import org.briarproject.bramble.test.TestDnsModule;
|
||||
import org.briarproject.bramble.test.TestSocksModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -17,7 +19,9 @@ import dagger.Component;
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class
|
||||
BrambleCoreModule.class,
|
||||
TestDnsModule.class,
|
||||
TestSocksModule.class
|
||||
})
|
||||
interface TransportKeyAgreementTestComponent
|
||||
extends BrambleIntegrationTestComponent {
|
||||
|
||||
@@ -21,6 +21,8 @@ dependencies {
|
||||
tor "org.briarproject:tor-windows:$tor_version"
|
||||
tor "org.briarproject:obfs4proxy-linux:$obfs4proxy_version"
|
||||
tor "org.briarproject:obfs4proxy-windows:$obfs4proxy_version"
|
||||
tor "org.briarproject:snowflake-linux:$snowflake_version"
|
||||
tor "org.briarproject:snowflake-windows:$snowflake_version"
|
||||
|
||||
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.briarproject.bramble;
|
||||
|
||||
import org.briarproject.bramble.io.DnsModule;
|
||||
import org.briarproject.bramble.network.JavaNetworkModule;
|
||||
import org.briarproject.bramble.plugin.tor.CircumventionModule;
|
||||
import org.briarproject.bramble.socks.SocksModule;
|
||||
@@ -9,6 +10,7 @@ import dagger.Module;
|
||||
|
||||
@Module(includes = {
|
||||
CircumventionModule.class,
|
||||
DnsModule.class,
|
||||
JavaNetworkModule.class,
|
||||
JavaSystemModule.class,
|
||||
SocksModule.class
|
||||
|
||||
@@ -9,6 +9,8 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.network.NetworkManager;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
|
||||
import org.briarproject.bramble.api.system.Clock;
|
||||
import org.briarproject.bramble.api.system.LocationUtils;
|
||||
@@ -42,11 +44,10 @@ import static java.util.Collections.singletonList;
|
||||
import static java.util.concurrent.TimeUnit.MINUTES;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.MEEK;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.NON_DEFAULT_OBFS4;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.SNOWFLAKE;
|
||||
import static org.briarproject.bramble.plugin.tor.CircumventionProvider.BridgeType.VANILLA;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
@@ -58,6 +59,8 @@ import static org.junit.Assume.assumeTrue;
|
||||
@RunWith(Parameterized.class)
|
||||
public class BridgeTest extends BrambleTestCase {
|
||||
|
||||
private static final String[] SNOWFLAKE_COUNTRY_CODES = {"TM", "ZZ"};
|
||||
|
||||
@Parameters
|
||||
public static Iterable<Params> data() {
|
||||
BrambleJavaIntegrationTestComponent component =
|
||||
@@ -69,29 +72,39 @@ public class BridgeTest extends BrambleTestCase {
|
||||
CircumventionProvider provider = component.getCircumventionProvider();
|
||||
List<Params> states = new ArrayList<>();
|
||||
for (int i = 0; i < ATTEMPTS_PER_BRIDGE; i++) {
|
||||
for (String bridge : provider.getBridges(DEFAULT_OBFS4)) {
|
||||
for (String bridge :
|
||||
provider.getBridges(DEFAULT_OBFS4, "", true)) {
|
||||
states.add(new Params(bridge, DEFAULT_OBFS4, stats, false));
|
||||
}
|
||||
for (String bridge : provider.getBridges(NON_DEFAULT_OBFS4)) {
|
||||
states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats, false));
|
||||
for (String bridge :
|
||||
provider.getBridges(NON_DEFAULT_OBFS4, "", true)) {
|
||||
states.add(new Params(bridge, NON_DEFAULT_OBFS4, stats,
|
||||
false));
|
||||
}
|
||||
for (String bridge : provider.getBridges(VANILLA)) {
|
||||
for (String bridge : provider.getBridges(VANILLA, "", true)) {
|
||||
states.add(new Params(bridge, VANILLA, stats, false));
|
||||
}
|
||||
for (String bridge : provider.getBridges(MEEK)) {
|
||||
for (String bridge : provider.getBridges(MEEK, "", true)) {
|
||||
states.add(new Params(bridge, MEEK, stats, true));
|
||||
}
|
||||
for (String countryCode : SNOWFLAKE_COUNTRY_CODES) {
|
||||
for (String bridge :
|
||||
provider.getBridges(SNOWFLAKE, countryCode, true)) {
|
||||
states.add(new Params(bridge, SNOWFLAKE, stats, true));
|
||||
}
|
||||
for (String bridge :
|
||||
provider.getBridges(SNOWFLAKE, countryCode, false)) {
|
||||
states.add(new Params(bridge, SNOWFLAKE, stats, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
private final static long OBFS4_TIMEOUT = MINUTES.toMillis(2);
|
||||
private final static long TIMEOUT = MINUTES.toMillis(2);
|
||||
private final static long MEEK_TIMEOUT = MINUTES.toMillis(6);
|
||||
private final static int UNREACHABLE_BRIDGES_ALLOWED = 6;
|
||||
private final static int ATTEMPTS_PER_BRIDGE = 5;
|
||||
// Use different ports from Briar Desktop to avoid conflicts
|
||||
private final static int SOCKS_PORT = DEFAULT_SOCKS_PORT + 10;
|
||||
private final static int CONTROL_PORT = DEFAULT_CONTROL_PORT + 10;
|
||||
|
||||
private final static Logger LOG = getLogger(BridgeTest.class.getName());
|
||||
|
||||
@@ -115,6 +128,12 @@ public class BridgeTest extends BrambleTestCase {
|
||||
Clock clock;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
@TorSocksPort
|
||||
int torSocksPort;
|
||||
@Inject
|
||||
@TorControlPort
|
||||
int torControlPort;
|
||||
|
||||
private final File torDir = getTestDirectory();
|
||||
private final Params params;
|
||||
@@ -160,15 +179,16 @@ public class BridgeTest extends BrambleTestCase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getBridges(BridgeType bridgeType) {
|
||||
public List<String> getBridges(BridgeType bridgeType,
|
||||
String countryCode, boolean letsEncrypt) {
|
||||
return singletonList(params.bridge);
|
||||
}
|
||||
};
|
||||
factory = new UnixTorPluginFactory(ioExecutor, wakefulIoExecutor,
|
||||
networkManager, locationUtils, eventBus, torSocketFactory,
|
||||
backoffFactory, resourceProvider, bridgeProvider,
|
||||
batteryManager, clock, crypto, torDir,
|
||||
SOCKS_PORT, CONTROL_PORT);
|
||||
batteryManager, clock, crypto, torDir, torSocksPort,
|
||||
torControlPort);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -187,8 +207,7 @@ public class BridgeTest extends BrambleTestCase {
|
||||
try {
|
||||
plugin.start();
|
||||
long start = clock.currentTimeMillis();
|
||||
long timeout = params.bridgeType == MEEK
|
||||
? MEEK_TIMEOUT : OBFS4_TIMEOUT;
|
||||
long timeout = params.bridgeType == MEEK ? MEEK_TIMEOUT : TIMEOUT;
|
||||
while (clock.currentTimeMillis() - start < timeout) {
|
||||
if (plugin.getState() == ACTIVE) return;
|
||||
clock.sleep(500);
|
||||
|
||||
@@ -14,7 +14,8 @@ import dagger.Component;
|
||||
@Component(modules = {
|
||||
BrambleCoreIntegrationTestModule.class,
|
||||
BrambleCoreModule.class,
|
||||
BrambleJavaModule.class
|
||||
BrambleJavaModule.class,
|
||||
TestTorPortsModule.class
|
||||
})
|
||||
public interface BrambleJavaIntegrationTestComponent
|
||||
extends BrambleCoreIntegrationTestEagerSingletons {
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
import org.briarproject.bramble.api.plugin.TorControlPort;
|
||||
import org.briarproject.bramble.api.plugin.TorSocksPort;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT;
|
||||
|
||||
@Module
|
||||
class TestTorPortsModule {
|
||||
|
||||
@Provides
|
||||
@TorSocksPort
|
||||
int provideTorSocksPort() {
|
||||
return DEFAULT_SOCKS_PORT + 10;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@TorControlPort
|
||||
int provideTorControlPort() {
|
||||
return DEFAULT_CONTROL_PORT + 10;
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,12 @@ dependencyVerification {
|
||||
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
|
||||
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
|
||||
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047',
|
||||
'org.briarproject:obfs4proxy-linux:0.0.12:obfs4proxy-linux-0.0.12.jar:3dd83aff25fe1cb3e4eab78a02c76ac921f552be6877b3af83a472438525df2a',
|
||||
'org.briarproject:obfs4proxy-windows:0.0.12:obfs4proxy-windows-0.0.12.jar:392aa4b9d9c6fef0c659c4068d019d6c6471991bbb62ff00553884ec36018c7b',
|
||||
'org.briarproject:tor-linux:0.4.5.12-2:tor-linux-0.4.5.12-2.jar:d275f323faf5e70b33d2c8a1bdab1bb3ab5a0d8f4e23c4a6dda03d86f4e95838',
|
||||
'org.briarproject:tor-windows:0.4.5.12-2:tor-windows-0.4.5.12-2.jar:46599a15d099ed35a360113293f66acc119571c24ec2e37e85e4fb54b4722e07',
|
||||
'org.briarproject:obfs4proxy-linux:0.0.14:obfs4proxy-linux-0.0.14.jar:6391d323d45a279362236c7c62e21b903d07d4f31f5e0c8d49d009769b720cc6',
|
||||
'org.briarproject:obfs4proxy-windows:0.0.14:obfs4proxy-windows-0.0.14.jar:801d48525f52583a470a1671026b87992176d4432b299774989387cb87bc8ba3',
|
||||
'org.briarproject:snowflake-linux:2.3.1:snowflake-linux-2.3.1.jar:99ecf4546d8f79eb8408168c09380fec596558ac934554bf7d4247ea7ef2c9f3',
|
||||
'org.briarproject:snowflake-windows:2.3.1:snowflake-windows-2.3.1.jar:d011f1a72c00a221f56380c19aad8ff11db8c2bb1adb0784125572d80b4d275a',
|
||||
'org.briarproject:tor-linux:0.4.5.14:tor-linux-0.4.5.14.jar:1844e54cf6df0c85cec219381a3364c759ae444a6b63f7558b757becb7d41d08',
|
||||
'org.briarproject:tor-windows:0.4.5.14:tor-windows-0.4.5.14.jar:d337afa1043f0cfa7e6e8c2473d682a5663a2c8052bb97a770450893c78c9b4f',
|
||||
'org.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:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
|
||||
|
||||
@@ -26,8 +26,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 30
|
||||
versionCode 10410
|
||||
versionName "1.4.10"
|
||||
versionCode 10412
|
||||
versionName "1.4.12"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
buildConfigField "String", "TorVersion", "\"$tor_version\""
|
||||
|
||||
|
||||
@@ -785,7 +785,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
|
||||
NotificationCompat.Builder b = new NotificationCompat.Builder(
|
||||
appContext, MAILBOX_PROBLEM_CHANNEL_ID);
|
||||
b.setSmallIcon(R.drawable.ic_mailbox);
|
||||
b.setSmallIcon(R.drawable.notification_mailbox);
|
||||
b.setColor(getColor(appContext, R.color.briar_red_500));
|
||||
b.setContentTitle(
|
||||
appContext.getText(R.string.mailbox_error_notification_title));
|
||||
|
||||
@@ -7,6 +7,8 @@ import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.view.TrustIndicatorView;
|
||||
import org.briarproject.briar.api.identity.AuthorInfo;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@@ -26,6 +28,10 @@ public class ContactItemViewHolder<I extends ContactItem>
|
||||
protected final TextView name;
|
||||
@Nullable
|
||||
protected final ImageView bulb;
|
||||
@Nullable
|
||||
protected final TrustIndicatorView trustIndicator;
|
||||
@Nullable
|
||||
protected final TextView trustIndicatorDescription;
|
||||
|
||||
public ContactItemViewHolder(View v) {
|
||||
super(v);
|
||||
@@ -35,6 +41,11 @@ public class ContactItemViewHolder<I extends ContactItem>
|
||||
name = v.findViewById(R.id.nameView);
|
||||
// this can be null as not all layouts that use this ViewHolder have it
|
||||
bulb = v.findViewById(R.id.bulbView);
|
||||
// this can be null as not all layouts that use this ViewHolder have it
|
||||
trustIndicator = v.findViewById(R.id.trustIndicator);
|
||||
// this can be null as not all layouts that use this ViewHolder have it
|
||||
trustIndicatorDescription =
|
||||
v.findViewById(R.id.trustIndicatorDescription);
|
||||
}
|
||||
|
||||
protected void bind(I item, @Nullable OnContactClickListener<I> listener) {
|
||||
@@ -50,6 +61,29 @@ public class ContactItemViewHolder<I extends ContactItem>
|
||||
}
|
||||
}
|
||||
|
||||
if (trustIndicator != null && trustIndicatorDescription != null) {
|
||||
final AuthorInfo.Status status = item.getAuthorInfo().getStatus();
|
||||
trustIndicator.setTrustLevel(status);
|
||||
|
||||
switch (status) {
|
||||
case UNVERIFIED:
|
||||
trustIndicatorDescription.setText(
|
||||
R.string.peer_trust_level_unverified);
|
||||
break;
|
||||
case VERIFIED:
|
||||
trustIndicatorDescription.setText(
|
||||
R.string.peer_trust_level_verified);
|
||||
break;
|
||||
case OURSELVES:
|
||||
trustIndicatorDescription.setText(
|
||||
R.string.peer_trust_level_ourselves);
|
||||
break;
|
||||
default:
|
||||
trustIndicatorDescription.setText(
|
||||
R.string.peer_trust_level_stranger);
|
||||
}
|
||||
}
|
||||
|
||||
layout.setOnClickListener(v -> {
|
||||
if (listener != null) listener.onItemClick(avatar, item);
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.mailbox;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.mailbox.MailboxPairingState;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
@@ -23,6 +24,7 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static org.briarproject.briar.android.util.UiUtils.showFragment;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@@ -215,6 +217,8 @@ public class MailboxActivity extends BriarActivity {
|
||||
dialog -> supportFinishAfterTransition());
|
||||
builder.show();
|
||||
} else {
|
||||
Toast.makeText(this, R.string.mailbox_status_unlink_success,
|
||||
LENGTH_LONG).show();
|
||||
supportFinishAfterTransition();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package org.briarproject.briar.android.reporting;
|
||||
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -10,6 +13,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
@@ -18,6 +22,8 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -27,14 +33,20 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.INVISIBLE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static android.widget.Toast.LENGTH_LONG;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static java.util.logging.Logger.getLogger;
|
||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||
import static org.briarproject.briar.android.util.UiUtils.onSingleLinkClick;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ReportFormFragment extends BaseFragment {
|
||||
|
||||
public final static String TAG = ReportFormFragment.class.getName();
|
||||
private static final Logger LOG = getLogger(TAG);
|
||||
|
||||
@Inject
|
||||
ViewModelProvider.Factory viewModelFactory;
|
||||
@@ -43,6 +55,7 @@ public class ReportFormFragment extends BaseFragment {
|
||||
|
||||
private EditText userCommentView;
|
||||
private EditText userEmailView;
|
||||
private TextView privacyPolicy;
|
||||
private CheckBox includeDebugReport;
|
||||
private Button chevron;
|
||||
private RecyclerView list;
|
||||
@@ -73,6 +86,7 @@ public class ReportFormFragment extends BaseFragment {
|
||||
|
||||
userCommentView = v.findViewById(R.id.user_comment);
|
||||
userEmailView = v.findViewById(R.id.user_email);
|
||||
privacyPolicy = v.findViewById(R.id.PrivacyPolicy);
|
||||
includeDebugReport = v.findViewById(R.id.include_debug_report);
|
||||
chevron = v.findViewById(R.id.chevron);
|
||||
list = v.findViewById(R.id.list);
|
||||
@@ -90,6 +104,8 @@ public class ReportFormFragment extends BaseFragment {
|
||||
userCommentView.setHint(R.string.describe_crash);
|
||||
}
|
||||
|
||||
onSingleLinkClick(privacyPolicy, this::triggerPrivacyPolicy);
|
||||
|
||||
chevron.setOnClickListener(view -> {
|
||||
boolean show = chevron.getText().equals(getString(R.string.show));
|
||||
viewModel.showReportData(show);
|
||||
@@ -161,4 +177,16 @@ public class ReportFormFragment extends BaseFragment {
|
||||
}
|
||||
}
|
||||
|
||||
private void triggerPrivacyPolicy() {
|
||||
Intent i = new Intent(Intent.ACTION_VIEW);
|
||||
i.setData(Uri.parse("https://briarproject.org/privacy-policy/\\"));
|
||||
try {
|
||||
startActivity(i);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
logException(LOG, WARNING, e);
|
||||
Toast.makeText(requireContext(),
|
||||
R.string.error_start_activity, LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public class AboutFragment extends Fragment {
|
||||
private TextView briarWebsite;
|
||||
private TextView briarSourceCode;
|
||||
private TextView briarChangelog;
|
||||
private TextView briarPrivacyPolicy;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -61,6 +62,8 @@ public class AboutFragment extends Fragment {
|
||||
briarWebsite = requireActivity().findViewById(R.id.BriarWebsite);
|
||||
briarSourceCode = requireActivity().findViewById(R.id.BriarSourceCode);
|
||||
briarChangelog = requireActivity().findViewById(R.id.BriarChangelog);
|
||||
briarPrivacyPolicy =
|
||||
requireActivity().findViewById(R.id.BriarPrivacyPolicy);
|
||||
briarWebsite.setOnClickListener(View -> {
|
||||
String url = "https://briarproject.org/";
|
||||
goToUrl(url);
|
||||
@@ -74,6 +77,11 @@ public class AboutFragment extends Fragment {
|
||||
"https://code.briarproject.org/briar/briar/-/wikis/changelog";
|
||||
goToUrl(url);
|
||||
});
|
||||
briarPrivacyPolicy.setOnClickListener(View -> {
|
||||
String url =
|
||||
"https://briarproject.org/privacy-policy/";
|
||||
goToUrl(url);
|
||||
});
|
||||
}
|
||||
|
||||
private void goToUrl(String url) {
|
||||
|
||||
@@ -28,9 +28,6 @@ public class TrustIndicatorView extends AppCompatImageView {
|
||||
public void setTrustLevel(Status status) {
|
||||
int res;
|
||||
switch (status) {
|
||||
case ANONYMOUS:
|
||||
res = R.drawable.trust_indicator_anonymous;
|
||||
break;
|
||||
case UNVERIFIED:
|
||||
res = R.drawable.trust_indicator_unverified;
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF">
|
||||
<group
|
||||
android:scaleX="1.104"
|
||||
android:scaleY="1.104"
|
||||
android:translateX="-1.248"
|
||||
android:translateY="-1.248">
|
||||
<path
|
||||
android:pathData="m5.078,2c-0.525,0 -1.028,0.211 -1.399,0.585C3.309,2.959 3.1,3.466 3.1,3.996V16.768c0,0.529 0.209,1.038 0.579,1.412 0.371,0.374 0.874,0.584 1.399,0.584H15.851l3.589,3.026C20.019,22.278 20.9,21.862 20.9,21.1V3.996C20.9,3.466 20.691,2.959 20.321,2.585 19.95,2.211 19.446,2 18.922,2ZM6.265,5.193H17.735v5.65h-4.007v1.954h1.466c0.264,0 0.397,0.323 0.21,0.511l-3.16,3.188c-0.116,0.117 -0.304,0.117 -0.42,0L8.664,13.308C8.477,13.12 8.609,12.797 8.873,12.797H10.339V10.843H6.265Z"
|
||||
android:strokeWidth="0.590769"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#00000000" />
|
||||
</group>
|
||||
</vector>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 407 B |
Binary file not shown.
|
After Width: | Height: | Size: 274 B |
Binary file not shown.
|
After Width: | Height: | Size: 651 B |
Binary file not shown.
|
After Width: | Height: | Size: 858 B |
@@ -1,40 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="31dp"
|
||||
android:height="12dp"
|
||||
android:viewportHeight="20"
|
||||
android:viewportWidth="49">
|
||||
<path
|
||||
android:fillColor="#a7a7a7"
|
||||
android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
|
||||
L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
|
||||
L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
|
||||
L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
|
||||
L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
|
||||
L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
|
||||
L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
|
||||
L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
|
||||
L10.5752,8.38194 L10.5752,11.8208 Z"/>
|
||||
<path
|
||||
android:fillColor="#a7a7a7"
|
||||
android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
|
||||
L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
|
||||
L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
|
||||
L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
|
||||
L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
|
||||
L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
|
||||
L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
|
||||
L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
|
||||
L26.0752,8.38194 L26.0752,11.8208 Z"/>
|
||||
<path
|
||||
android:fillColor="#a7a7a7"
|
||||
android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
|
||||
L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
|
||||
L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
|
||||
L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
|
||||
L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
|
||||
L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
|
||||
L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
|
||||
L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
|
||||
L41.5752,8.38194 L41.5752,11.8208 Z"/>
|
||||
</vector>
|
||||
@@ -60,6 +60,15 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarSourceCode" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/BriarPrivacyPolicy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/briar_privacy_policy"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarChangelog" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/TranslatorThanks"
|
||||
android:layout_width="match_parent"
|
||||
@@ -68,6 +77,6 @@
|
||||
android:text="@string/translator_thanks"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarChangelog" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/BriarPrivacyPolicy" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,119 +1,126 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
tools:context=".android.mailbox.MailboxActivity">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusTitleView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:srcCompat="@drawable/ic_help_outline_white"
|
||||
tools:tint="@color/briar_orange_500" />
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusTitleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusMessageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:text="@string/mailbox_status_problem_title" />
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusTitleView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.25"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:ignore="ContentDescription"
|
||||
tools:srcCompat="@drawable/ic_help_outline_white"
|
||||
tools:tint="@color/briar_orange_500" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusMessageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||
tools:text="@string/mailbox_status_mailbox_too_old_message"
|
||||
tools:visibility="visible" />
|
||||
<TextView
|
||||
android:id="@+id/statusTitleView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusMessageView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageView"
|
||||
tools:text="@string/mailbox_status_problem_title" />
|
||||
|
||||
<org.briarproject.briar.android.view.BriarButton
|
||||
android:id="@+id/checkButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:buttonStyle="@style/BriarButtonFlat.Neutral"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusMessageView"
|
||||
app:text="@string/mailbox_status_check_button" />
|
||||
<TextView
|
||||
android:id="@+id/statusMessageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:gravity="center"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:visibility="gone"
|
||||
app:layout_constrainedWidth="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/checkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusTitleView"
|
||||
tools:text="@string/mailbox_status_mailbox_too_old_message"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/statusInfoView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/checkButton"
|
||||
tools:text="@string/mailbox_status_connected_info" />
|
||||
<org.briarproject.briar.android.view.BriarButton
|
||||
android:id="@+id/checkButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:buttonStyle="@style/BriarButtonFlat.Neutral"
|
||||
app:layout_constraintBottom_toTopOf="@+id/statusInfoView"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusMessageView"
|
||||
app:text="@string/mailbox_status_check_button" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/wizardButton"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_error_wizard_button"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="@color/briar_button_text_positive"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusInfoView"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:visibility="visible" />
|
||||
<TextView
|
||||
android:id="@+id/statusInfoView"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/checkButton"
|
||||
tools:text="@string/mailbox_status_connected_info" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/unlinkButton"
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_status_unlink_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
<Button
|
||||
android:id="@+id/wizardButton"
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_error_wizard_button"
|
||||
android:visibility="gone"
|
||||
app:drawableTint="@color/briar_button_text_positive"
|
||||
app:layout_constraintBottom_toTopOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/statusInfoView"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/unlinkProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="@+id/unlinkButton"
|
||||
app:layout_constraintStart_toStartOf="@+id/unlinkButton"
|
||||
app:layout_constraintTop_toTopOf="@+id/unlinkButton"
|
||||
tools:visibility="visible" />
|
||||
<Button
|
||||
android:id="@+id/unlinkButton"
|
||||
style="@style/BriarButtonFlat.Negative"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="16dp"
|
||||
android:text="@string/mailbox_status_unlink_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
<ProgressBar
|
||||
android:id="@+id/unlinkProgress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/unlinkButton"
|
||||
app:layout_constraintEnd_toEndOf="@+id/unlinkButton"
|
||||
app:layout_constraintStart_toStartOf="@+id/unlinkButton"
|
||||
app:layout_constraintTop_toTopOf="@+id/unlinkButton"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
@@ -56,6 +56,18 @@
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/PrivacyPolicy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="@dimen/margin_large"
|
||||
android:layout_marginRight="@dimen/margin_large"
|
||||
android:layout_marginTop="@dimen/margin_large"
|
||||
android:text="@string/privacy_policy"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/user_email_layout" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/include_debug_report"
|
||||
android:layout_width="0dp"
|
||||
@@ -74,9 +86,10 @@
|
||||
style="@style/BriarButtonFlat.Positive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/margin_medium"
|
||||
android:text="@string/show"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/user_email_layout" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/PrivacyPolicy" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
|
||||
@@ -55,13 +55,42 @@
|
||||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dateView"
|
||||
app:layout_constraintBottom_toTopOf="@+id/trustIndicatorDescription"
|
||||
app:layout_constraintEnd_toStartOf="@+id/bulbView"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatarFrameView"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
tools:text="This is a name of a contact" />
|
||||
|
||||
<org.briarproject.briar.android.view.TrustIndicatorView
|
||||
android:id="@+id/trustIndicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginLeft="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dateView"
|
||||
app:layout_constraintEnd_toStartOf="@+id/trustIndicatorDescription"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatarFrameView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/nameView"
|
||||
tools:src="@drawable/trust_indicator_verified" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/trustIndicatorDescription"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginRight="8dp"
|
||||
android:paddingStart="@dimen/margin_medium"
|
||||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBottom_toTopOf="@+id/dateView"
|
||||
app:layout_constraintEnd_toStartOf="@+id/bulbView"
|
||||
app:layout_constraintStart_toEndOf="@+id/trustIndicator"
|
||||
app:layout_constraintTop_toBottomOf="@+id/nameView"
|
||||
tools:text="verified contact"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dateView"
|
||||
android:layout_width="0dp"
|
||||
@@ -70,15 +99,16 @@
|
||||
android:layout_marginLeft="@dimen/margin_medium"
|
||||
android:layout_marginEnd="@dimen/margin_medium"
|
||||
android:layout_marginRight="@dimen/margin_medium"
|
||||
android:layout_marginBottom="@dimen/listitem_vertical_margin"
|
||||
android:layout_marginBottom="10dp"
|
||||
android:paddingStart="@dimen/margin_medium"
|
||||
android:paddingEnd="@dimen/margin_medium"
|
||||
android:textColor="?android:attr/textColorSecondary"
|
||||
android:textSize="@dimen/text_size_small"
|
||||
app:layout_constraintBottom_toTopOf="@+id/divider"
|
||||
app:layout_constraintBottom_toTopOf="@id/divider"
|
||||
app:layout_constraintEnd_toStartOf="@+id/bulbView"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/avatarFrameView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/nameView"
|
||||
app:layout_constraintTop_toBottomOf="@+id/trustIndicatorDescription"
|
||||
tools:text="Dec 24" />
|
||||
|
||||
<ImageView
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
android:title="@string/lock_button"
|
||||
android:visible="false"
|
||||
tools:visible="false" />
|
||||
</group>
|
||||
|
||||
<group
|
||||
android:id="@+id/nav_btn_signout_separated"
|
||||
android:checkableBehavior="single">
|
||||
<item
|
||||
android:id="@+id/nav_btn_signout"
|
||||
android:icon="@drawable/ic_signout"
|
||||
|
||||
@@ -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,48 @@
|
||||
<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_status_unlink_success">Връзката с пощенската кутия е прекъсната</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="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>
|
||||
<string name="briar_privacy_policy">\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">При включване, тази настройка прави бъдещите съобщения в този разговор да изчезват след 7\u00A0дни.
|
||||
@@ -640,6 +684,7 @@
|
||||
<string name="describe_crash">Опишете случилото се (незадължително)</string>
|
||||
<string name="enter_feedback">Въведете обратна връзка</string>
|
||||
<string name="optional_contact_email">Адрес на електронна поща (по желание)</string>
|
||||
<string name="privacy_policy">Като ни изпращате данни, се съгласявате с <a href="">политиката за личните данни</a></string>
|
||||
<string name="include_debug_report_crash">Изпращане на анонимни данни за срива</string>
|
||||
<string name="include_debug_report_feedback">Изпращане на анонимни данни за устройството</string>
|
||||
<string name="dev_report_user_info">Данни за потербителя</string>
|
||||
@@ -730,6 +775,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 +797,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>
|
||||
@@ -620,6 +625,7 @@
|
||||
<string name="mailbox_status_unlink_dialog_warning">Wenn du die Verknüpfung mit deiner Mailbox aufhebst, kannst du keine Nachrichten empfangen, während Briar offline ist.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Deine Mailbox wurde getrennt</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">Wenn du das nächste Mal Zugriff auf dein Mailbox-Gerät hast, öffne bitte die Mailbox-App und tippe auf die Schaltfläche \"Trennen\", um den Vorgang abzuschließen.\n\nWenn du keinen Zugriff mehr auf dein Mailbox-Gerät hast, mach dir keine Sorgen. Deine Daten sind verschlüsselt, sodass sie sicher bleiben, auch wenn du den Vorgang nicht abschließt.</string>
|
||||
<string name="mailbox_status_unlink_success">Deine Mailbox wurde getrennt</string>
|
||||
<string name="mailbox_error_notification_channel_title">Briar Mailbox Problem</string>
|
||||
<string name="mailbox_error_notification_title">Briar Mailbox ist nicht verfügbar</string>
|
||||
<string name="mailbox_error_notification_text">Antippen, um das Problem zu beheben.</string>
|
||||
@@ -644,6 +650,17 @@
|
||||
<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>
|
||||
<string name="briar_privacy_policy">\u2022 <a href="">Datenschutzerklärung</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
|
||||
@@ -675,6 +692,7 @@
|
||||
<string name="describe_crash">Beschreibe, was passiert ist (optional)</string>
|
||||
<string name="enter_feedback">Gib dein Feedback ein</string>
|
||||
<string name="optional_contact_email">Deine E-Mail-Adresse (optional)</string>
|
||||
<string name="privacy_policy">Indem du uns Daten sendest, stimmst du unserer <a href="">Datenschutzerklärung</a> zu</string>
|
||||
<string name="include_debug_report_crash">Anonymisierte Daten über den Absturz anhängen</string>
|
||||
<string name="include_debug_report_feedback">Anonymisierte Daten über dieses Gerät anhängen</string>
|
||||
<string name="dev_report_user_info">Benutzerinformation</string>
|
||||
@@ -787,7 +805,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-->
|
||||
|
||||
@@ -26,6 +26,10 @@
|
||||
<string name="dnkm_xiaomi_button">Proteger Briar</string>
|
||||
<string name="dnkm_xiaomi_help">Si Briar no está bloqueado en la lista de aplicaciones recientes, no podrá ejecutarse en segundo plano.</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_old">1. Abre la lista de aplicaciones recientes (también llamada selectora de aplicaciones)\n\n2. Desliza hacia abajo sobre la imagen de Briar para mostrar el ícono de un candado\n\n3. Si el candado no está bloqueado, púlsalo para hacerlo</string>
|
||||
<string name="dnkm_xiaomi_dialog_body_new">1. Abra la lista de aplicaciones recientes (también llamada el interruptor de aplicaciones)\n\n2. Si Briar tiene una pequeña imagen de un candado junto a su nombre, entonces no necesita hacer nada\n\n3. Si no hay ningún candado, mantenga pulsada la imagen de Briar hasta que aparezca el botón del candado, y luego púlselo.</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_text">Toca el botón de abajo para abrir la configuración de seguridad. Toca \"Aumentar la velocidad\", luego toca \"Bloquear aplicaciones\" y asegúrate de que Briar esté configurado como \"Bloqueado\".</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_help">Si Briar no está configurado como \"Bloqueado\" en la pantalla \"Bloquear aplicaciones\", no podrá ejecutarse en segundo plano.</string>
|
||||
<string name="dnkm_warning_dozed_1">Briar no ha podido ejecutarse en segundo plano</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">Contraseña</string>
|
||||
<string name="try_again">Contraseña incorrecta, inténtalo de nuevo</string>
|
||||
@@ -238,6 +242,8 @@
|
||||
<string name="contact_added_toast">Contacto añadido: %s</string>
|
||||
<string name="contact_already_exists">El contacto %s ya existe</string>
|
||||
<string name="qr_code_invalid">El código QR no es válido</string>
|
||||
<string name="qr_code_too_old_1">El código QR que has escaneado procede de una versión antigua de Briar.\n\nPor favor, pide a tu contacto que actualice a la última versión y vuelve a intentarlo.</string>
|
||||
<string name="qr_code_too_new_1">El código QR que has escaneado proviene de una versión más reciente de Briar.\n\nPor favor, actualiza a la última versión y vuelve a intentarlo.</string>
|
||||
<string name="camera_error">Error de cámara</string>
|
||||
<string name="connecting_to_device">Conectando al dispositivo\u2026</string>
|
||||
<string name="authenticating_with_device">Autentificándose con el dispositivo\u2026</string>
|
||||
@@ -301,8 +307,14 @@
|
||||
<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_unverified">Contacto no verificado</string>
|
||||
<string name="peer_trust_level_verified">Contacto verificado</string>
|
||||
<string name="peer_trust_level_ourselves">Yo</string>
|
||||
<string name="peer_trust_level_stranger">Extraño</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Presenta a tus contactos</string>
|
||||
<string name="introduction_onboarding_text">Presenta a tus contactos para que puedan conectarse en Briar.</string>
|
||||
<string name="introduction_menu_item">Hacer presentación</string>
|
||||
<string name="introduction_activity_title">Seleccionar contacto</string>
|
||||
<string name="introduction_not_possible">Ya tienes una presentación en curso con estos contactos. Por favor, deja que esto termine primero. Si tu o tus contactos raramente están en línea, esto puede tomar algún tiempo.</string>
|
||||
@@ -610,13 +622,59 @@
|
||||
<string name="tor_offline_button_check">Comprobar configuración de conexión</string>
|
||||
<string name="mailbox_status_title">Estado del buzón</string>
|
||||
<string name="mailbox_status_connected_title">El buzón está en ejecución</string>
|
||||
<string name="mailbox_status_problem_title">Briar tiene problemas para conectarse al Buzón de Correo</string>
|
||||
<string name="mailbox_status_failure_title">El buzón no está disponible</string>
|
||||
<string name="mailbox_status_app_too_old_title">Briar es demasiado viejo</string>
|
||||
<string name="mailbox_status_app_too_old_message">Actualiza Briar a la última versión de la aplicación y vuelve a intentarlo.</string>
|
||||
<string name="mailbox_status_mailbox_too_old_title">El buzón es demasiado viejo</string>
|
||||
<string name="mailbox_status_mailbox_too_old_message">Actualiza tu buzón a la última versión de la aplicación y vuelve a intentarlo.</string>
|
||||
<string name="mailbox_status_check_button">Comprobar conexión</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Última conexión: %s</string>
|
||||
<!--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>
|
||||
<string name="mailbox_status_unlink_dialog_title">¿Desenlazar el buzón?</string>
|
||||
<string name="mailbox_status_unlink_dialog_question">¿Está seguro de que quiere desvincular su buzón?</string>
|
||||
<string name="mailbox_status_unlink_dialog_warning">Si desvinculas tu buzón, no podrás recibir mensajes mientras Briar esté desconectado.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Su buzón ha sido desvinculado</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">La próxima vez que tengas acceso a tu dispositivo Mailbox, abre la aplicación Mailbox y toca el botón \"Desvincular\" para completar el proceso.\n\nIf ya no tienes acceso a tu dispositivo Mailbox, no te preocupes. Tus datos están encriptados, por lo que seguirán estando seguros aunque no completes el proceso.</string>
|
||||
<string name="mailbox_status_unlink_success">Su buzón ha sido desvinculado</string>
|
||||
<string name="mailbox_error_notification_channel_title">Problema con el buzón de Briar</string>
|
||||
<string name="mailbox_error_notification_title">El buzón de Briar no está disponible</string>
|
||||
<string name="mailbox_error_notification_text">Pulse para solucionar el problema.</string>
|
||||
<string name="mailbox_error_wizard_button">Solucionar el problema</string>
|
||||
<string name="mailbox_error_wizard_title">Asistente para la resolución de problemas del Buzón de Correo</string>
|
||||
<string name="mailbox_error_wizard_question1">¿Tiene acceso a su dispositivo de Buzón de Correo?</string>
|
||||
<string name="mailbox_error_wizard_answer1">Sí, tengo acceso a ella ahora mismo.</string>
|
||||
<string name="mailbox_error_wizard_answer2">Ahora mismo no, pero puedo acceder a él más tarde.</string>
|
||||
<string name="mailbox_error_wizard_answer3">No, ya no tengo acceso a ella.</string>
|
||||
<string name="mailbox_error_wizard_info1_1">Compruebe que el dispositivo del Buzón de Correo está encendido y conectado a Internet.</string>
|
||||
<string name="mailbox_error_wizard_question1_1">Abre la aplicación del Buzón de Correo. ¿Qué ves?</string>
|
||||
<string name="mailbox_error_wizard_answer1_1">Veo las instrucciones para configurar el Buzón de Correo</string>
|
||||
<string name="mailbox_error_wizard_answer1_2">Veo un código QR</string>
|
||||
<string name="mailbox_error_wizard_answer1_3">Veo que \"El buzón está funcionando\"</string>
|
||||
<string name="mailbox_error_wizard_answer1_4">Veo \"Dispositivo fuera de línea\"</string>
|
||||
<string name="mailbox_error_wizard_info1_1_1">Por favor, desvincule su buzón utilizando el botón de abajo, luego siga las instrucciones del dispositivo del buzón para vincularlo de nuevo.</string>
|
||||
<string name="mailbox_error_wizard_info_1_1_2">Por favor, desvincula tu buzón usando el botón de abajo, y luego escanea el código QR para vincularlo de nuevo.</string>
|
||||
<string name="mailbox_error_wizard_info1_1_3">Por favor, utilice el botón de abajo para comprobar la conexión entre Briar y el Buzón de Correo.\n\n
|
||||
Si la conexión vuelve a fallar:\n
|
||||
\u2022 Comprueba que las aplicaciones del Buzón de Correo y de Briar están actualizadas a la última versión.\n
|
||||
\u2022 Reinicie sus dispositivos de Buzón de Correo y Briar e inténtelo de nuevo.</string>
|
||||
<string name="mailbox_error_wizard_info1_1_4">Comprueba que el dispositivo de correo está correctamente conectado a Internet.\n\nComprueba que el reloj del dispositivo de buzón muestra la hora, la fecha y la zona horaria correctas.\n\nComprueba que las aplicaciones de Correo y Briar están actualizadas a la última versión.\n\nReinicia tus dispositivos de Correo y Briar y vuelve a intentarlo.</string>
|
||||
<string name="mailbox_error_wizard_info2">Vuelva a esta pantalla cuando tenga acceso al dispositivo.</string>
|
||||
<string name="mailbox_error_wizard_info3">Por favor, desvincule su buzón utilizando el botón de abajo.\n\nDespués de desvincular su antiguo buzón, puede configurar un nuevo buzón en cualquier momento.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Acerca de</string>
|
||||
<string name="briar_version">Versión de Briar: %s</string>
|
||||
<string name="tor_version">Versión de Tor: %s</string>
|
||||
<string name="links">Enlaces</string>
|
||||
<string name="briar_website">\u2022 <a href="">Página web</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Código fuente</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Registro de cambios</a></string>
|
||||
<string name="briar_privacy_policy">\u2022 <a href="">Política de privacidad</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">Gracias a todos los colaboradores del Laboratorio de Traducción</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Mensajes con caducidad</string>
|
||||
<string name="disappearing_messages_explanation_long">Activar este ajuste hará que los nuevos
|
||||
@@ -648,6 +706,7 @@
|
||||
<string name="describe_crash">Describe qué ha ocurrido (opcional)</string>
|
||||
<string name="enter_feedback">Escribe tu comentario</string>
|
||||
<string name="optional_contact_email">Tu correo electrónico (opcional)</string>
|
||||
<string name="privacy_policy">Al enviarnos datos, acepta nuestra <a href="">política de privacidad</a></string>
|
||||
<string name="include_debug_report_crash">Incluir datos anónimos sobre la falla</string>
|
||||
<string name="include_debug_report_feedback">Incluir datos anónimos sobre este dispositivo</string>
|
||||
<string name="dev_report_user_info">Información de usuario</string>
|
||||
@@ -733,6 +792,9 @@
|
||||
<string name="hotspot_manual_site_address">Dirección (URL)</string>
|
||||
<string name="hotspot_qr_site">Tu teléfono está proporcionando un punto de acceso Wi-Fi. Las personas que están conectadas al mismo pueden descargar Briar escaneando este código QR.</string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">Descargar Briar %s</string>
|
||||
<string name="website_download_intro_1">Alguien cercano compartió a Briar con usted.</string>
|
||||
<string name="website_download_button">Descargar Briar</string>
|
||||
<string name="website_download_outro">Luego de que la descarga se complete, abre el archivo descargado e instálalo.</string>
|
||||
<string name="website_troubleshooting_title">Resolución de problemas</string>
|
||||
<string name="website_troubleshooting_1">Si no puedes descargar la aplicación, inténtalo con una aplicación de navegación web diferente.</string>
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
<!--Transports: Wi-Fi-->
|
||||
<string name="transport_lan">وای فای</string>
|
||||
<string name="transport_lan_long">همان شبکه وای-فای</string>
|
||||
<string name="lan_device_status_on">موبایل شما به وای-فای وصل می باشد</string>
|
||||
<string name="lan_device_status_on">تلفن شما به Wi-Fi متصل است</string>
|
||||
<string name="lan_device_status_off">موبایل شما به وای-فای وصل نیست</string>
|
||||
<string name="lan_plugin_status_enabling">Briar در حال اتصال به شبکه وای-فای می باشد</string>
|
||||
<string name="lan_plugin_status_active">Briar به شبکه وای-فای متصل می باشد</string>
|
||||
@@ -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>
|
||||
@@ -535,7 +540,7 @@
|
||||
<string name="wifi_setting">اتصال به مخاطبان روی یک شبکه وای-فای یکسان</string>
|
||||
<string name="tor_enable_title">اتصال به مخاطبان از طریق اینترنت</string>
|
||||
<string name="tor_enable_summary">تمام اتصال ها برای حفظ حریم خصوصی از شبکه تور عبور میکنند</string>
|
||||
<string name="tor_network_setting">روش اتصال برای شبکه تور</string>
|
||||
<string name="tor_network_setting">روش اتصال برای شبکه Tor</string>
|
||||
<string name="tor_network_setting_automatic">خودکار مبتنی بر موقعیت</string>
|
||||
<string name="tor_network_setting_without_bridges">استفاده از شبکه تور بدون پل</string>
|
||||
<string name="tor_network_setting_with_bridges">استفاده از شبکه تور با پل</string>
|
||||
@@ -652,6 +657,7 @@
|
||||
<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_status_unlink_success">پیوند صندوق پستی شما لغو شد</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>
|
||||
@@ -676,6 +682,17 @@
|
||||
<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>
|
||||
<string name="briar_privacy_policy">\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">روشن کردن این تنظیمات موجب خواهد شد تا پیامهای جدید
|
||||
@@ -707,6 +724,7 @@
|
||||
<string name="describe_crash">توضیح دهید چه اتفاقی افتاد (اختیاری)</string>
|
||||
<string name="enter_feedback">بازخورد خود را وارد کنید</string>
|
||||
<string name="optional_contact_email">آدرس ایمیل شما (اختیاری)</string>
|
||||
<string name="privacy_policy">با ارسال داده به ما، با <a href="">سیاست حفظ حریم خصوصی</a> ما موافقت میکنید</string>
|
||||
<string name="include_debug_report_crash">قرار دادن داده های ناشناس مربوط به خرابی</string>
|
||||
<string name="include_debug_report_feedback">قرار دادن داده های ناشناس درباره این دستگاه</string>
|
||||
<string name="dev_report_user_info">اطلاعات کاربر</string>
|
||||
|
||||
@@ -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ý
|
||||
|
||||
@@ -307,6 +307,11 @@
|
||||
<string name="different_person_button">Persone diverse</string>
|
||||
<string name="duplicate_link_dialog_text_3">%1$s e %2$s ti hanno inviato lo stesso link.\n\nUno dei due potrebbe tentare di scoprire chi sono i tuoi contatti.\n\nNon dirgli che hai ricevuto lo stesso link da qualcun altro.</string>
|
||||
<string name="pending_contact_updated_toast">Contatto in attesa aggiornato</string>
|
||||
<!--Peer trust levels-->
|
||||
<string name="peer_trust_level_unverified">Contatto non verificato</string>
|
||||
<string name="peer_trust_level_verified">Contatto verificato</string>
|
||||
<string name="peer_trust_level_ourselves">Io</string>
|
||||
<string name="peer_trust_level_stranger">Sconosciuto</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">Presenta i tuoi contatti</string>
|
||||
<string name="introduction_onboarding_text">Presenta i tuoi contatti fra di loro in modo che possano connettersi su Briar.</string>
|
||||
@@ -634,6 +639,7 @@
|
||||
<string name="mailbox_status_unlink_dialog_warning">Se la scolleghi non potrai più ricevere messaggi mentre Briar è offline.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">La casella postale è stata scollegata</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">La prossima volta che avrai accesso al dispositivo della casella postale, apri l\'app Mailbox e tocca il pulsante \"Scollega\" per completare il processo.\n\nSe non hai più accesso al dispositivo, non preoccuparti. I dati sono cifrati e rimarranno sicuri anche se non completi il processo.</string>
|
||||
<string name="mailbox_status_unlink_success">La casella postale è stata scollegata</string>
|
||||
<string name="mailbox_error_notification_channel_title">Problema casella postale di Briar</string>
|
||||
<string name="mailbox_error_notification_title">Casella postale di Briar non disponibile</string>
|
||||
<string name="mailbox_error_notification_text">Tocca per risolvere il problema.</string>
|
||||
@@ -658,6 +664,17 @@
|
||||
<string name="mailbox_error_wizard_info1_1_4">Controlla che il dispositivo della casella postale sia connesso a internet correttamente.\n\nControlla che l\'orologio nel dispositivo della casella postale mostri l\'orario, la data e il fuso orario giusti.\n\nControlla che le app Mailbox e Briar siano aggiornate alla versione più recente.\n\nRiavvia i dispositivi di Mailbox e Briar e riprova.</string>
|
||||
<string name="mailbox_error_wizard_info2">Torna in questa schermata quando avrai accesso al dispositivo.</string>
|
||||
<string name="mailbox_error_wizard_info3">Scollega la casella postale usando il pulsante sottostante.\n\nDopo avere sbloccato la vecchia casella postale, puoi configurarne una nuova in qualsiasi momento.</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Informazioni</string>
|
||||
<string name="briar_version">Versione di Briar: %s</string>
|
||||
<string name="tor_version">Versione di Tor: %s</string>
|
||||
<string name="links">Collegamenti</string>
|
||||
<string name="briar_website">\u2022 <a href="">Sito web</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Codice sorgente</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Cronologia</a></string>
|
||||
<string name="briar_privacy_policy">\u2022 <a href="">Informativa sulla privacy</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">Grazie a tutti i collaboratori nel Localization Lab</string>
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Messaggi dissolventi</string>
|
||||
<string name="disappearing_messages_explanation_long">L\'attivazione di questa impostazione farà sparire
|
||||
@@ -689,6 +706,7 @@
|
||||
<string name="describe_crash">Descrivi cosa è successo (facoltativo)</string>
|
||||
<string name="enter_feedback">Immetti il tuo feedback</string>
|
||||
<string name="optional_contact_email">Il tuo indirizzo email (facoltativo)</string>
|
||||
<string name="privacy_policy">Inviandoci dati, accetti la nostra <a href="">informativa sulla privacy</a></string>
|
||||
<string name="include_debug_report_crash">Includere dati anonimi riguardo al crash</string>
|
||||
<string name="include_debug_report_feedback">Includere dati anonimi riguardo al tuo dispositivo</string>
|
||||
<string name="dev_report_user_info">Informazioni utente</string>
|
||||
|
||||
@@ -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>
|
||||
@@ -603,6 +608,7 @@
|
||||
<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_status_unlink_success">メールボックスはリンク解除されました</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>
|
||||
@@ -627,6 +633,17 @@
|
||||
<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>
|
||||
<string name="briar_privacy_policy">\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">この設定を有効にすると、
|
||||
@@ -658,6 +675,7 @@
|
||||
<string name="describe_crash">何が起こったかのかを説明してください(任意)</string>
|
||||
<string name="enter_feedback">フィードバックを入力してください</string>
|
||||
<string name="optional_contact_email">あなたのメールアドレス(任意)</string>
|
||||
<string name="privacy_policy">データを我々に送信することで、<a href="">プライバシーポリシー</a>に同意したことになります。</string>
|
||||
<string name="include_debug_report_crash">クラッシュに関する匿名のデータを添付する</string>
|
||||
<string name="include_debug_report_feedback">この端末に関する匿名のデータを添付する</string>
|
||||
<string name="dev_report_user_info">ユーザー情報</string>
|
||||
|
||||
845
briar-android/src/main/res/values-ka/strings.xml
Normal file
845
briar-android/src/main/res/values-ka/strings.xml
Normal file
@@ -0,0 +1,845 @@
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!--Setup-->
|
||||
<string name="setup_title">მოგესალმებათ Briar</string>
|
||||
<string name="setup_name_explanation">თქვენი მეტსახელი გამოჩნდება ნებისმიერი თქვენი პოსტის გვერდით. თქვენ ვაღარ შეცვლით მას თქვენი ანგარიშის შექმნის შემდგომ.</string>
|
||||
<string name="setup_next">შემდეგ</string>
|
||||
<string name="setup_password_intro">აირჩიეთ პაროლი</string>
|
||||
<string name="setup_password_explanation">თქვენი Briar-ანგარიში დაშიფრული ინახება თქვენს მოწყობილობაზე და არა ონლაინ-საცავში. პაროლის დავიწყების ან Briar-ის დეინსტალაციის შემთხვევაში, თქვენს ანგარიშს ვეღარ აღადგენთ.\n\nშეარჩიეთ გრძელი და ძნელად გამოსაცნობი პაროლი, მაგალითად ოთხი შემთხვევითი სიტყვა, ან ათი შემთხვევითი ასო, ციფრი და სიმბოლო.</string>
|
||||
<string name="dnkm_doze_intro">შეტყობინებების მისაღებად Briar უნდა დარჩეს დაკავშირებული ფონურ რეჟიმში.</string>
|
||||
<string name="dnkm_doze_explanation">წერილების მისაღებად Briar უნდა დარჩეს კავშირზე ფონურად. გთხოვთ გათიშოთ ბატარეის დამზოგი, რომ Briar მუდმივად დაკავშირებული იყოს.</string>
|
||||
<string name="choose_nickname">აირჩიეთ თქვენი მეტსახელი</string>
|
||||
<string name="choose_password">აირჩიეთ თქვენი პაროლი</string>
|
||||
<string name="confirm_password">დაადასტურეთ თქვენი პაროლი</string>
|
||||
<string name="name_too_long">სახელი ძალიან გრძელია</string>
|
||||
<string name="password_too_weak">პაროლი ძალიან სუსტია</string>
|
||||
<string name="passwords_do_not_match">პაროლები არ ემთხვევა</string>
|
||||
<string name="create_account_button">ანგარიშის შექმნა</string>
|
||||
<string name="more_info">ვრცლად</string>
|
||||
<string name="don_t_ask_again">აღარ შემეკითხო</string>
|
||||
<string name="dnkm_huawei_protected_text">შეეხეთ ღილაკს ქვემოთ და დარწმუნდით, რომ Briar დაცულია „დაცულ აპების“ ეკრანზე.</string>
|
||||
<string name="dnkm_huawei_protected_button">დაიცავით Briar</string>
|
||||
<string name="dnkm_huawei_protected_help">თუ Briar არ დაემატება დაცული აპების სიაში, ის ვერ გაიშვება ფონურ რეჟიმში.</string>
|
||||
<string name="dnkm_huawei_app_launch_text">შეეხეთ ღილაკს ქვემოთ, გახსენით „აპების გაშვების“ ეკრანი და დარწმუნდით, რომ Briar დაყენებულია „ხელით მართვაზე“.</string>
|
||||
<string name="dnkm_huawei_app_launch_help">თუ Briar-ს „ხელით მართვა“ არ არის დაყენებული „ხელით მართვაზე“ „აპების გაშვების“ ეკრანზე, ის ვერ იმუშავებს ფონურ რეჟიმში.</string>
|
||||
<string name="dnkm_xiaomi_text">ფონურ რეჟიმში გასაშვებად Briar უნდა დაიბლოკოს ბოლო აპების სიაში.</string>
|
||||
<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. თუ იქ არ არის ბოქლომის ნიშანი, შეეხეთ და დააყოვნეთ აპის გამოსახულებაზე სანამ არ გამოჩნდება ბოქლომის ღილაკი, შემდეგ შეეხეთ მას</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_text">უსაფრთხოების პარამეტრების გასახსნელად, დააჭირეთ ღილაკს ქვემოთ. შეეხეთ „სიჩქარის გაზრდას“ და შემდეგ „აპების დაბლოკვას“, დარწმუნდით, რომ Briar დაყენებულია „დაბლოკილზე“.</string>
|
||||
<string name="dnkm_xiaomi_lock_apps_help">თუ Briar „აპების დაბლოკვის“ ეკრანზე არ არის დაყენებული „დაბლოკილზე“, ის ვერ გაიშვება ფონურ რეჟიმში.</string>
|
||||
<string name="dnkm_warning_dozed_1">Briar-ის გაშვება ფონურ რეჟიმში შეუძლებელია</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">პაროლი</string>
|
||||
<string name="try_again">პაროლი არასწორია, ისევ სცადეთ</string>
|
||||
<string name="dialog_title_cannot_check_password">პაროლის შემოწმება ვერ ხერხდება</string>
|
||||
<string name="dialog_message_cannot_check_password">Briar ვერ ამოწმებს თქვენს პაროლს. ამ პრობლემის გადასაჭრელად, სცადეთ თქვენი მოწყობილობის გადატვირთვა.</string>
|
||||
<string name="sign_in_button">შესვლა</string>
|
||||
<string name="forgotten_password">დამავიწყდა პაროლი</string>
|
||||
<string name="dialog_title_lost_password">დაკარგული პაროლი</string>
|
||||
<string name="dialog_message_lost_password">თქვენი Briar-ანგარიში დაშიფრული ინახება თქვენს მოწყობილობაზე და არა ონლაინ-საცავში. ასე რომ, ჩვენ ვერ გადავტვირთავთ თქვენს პაროლს. გსურთ წაშალოთ თქვენი ანგარიში და ახლიდან შექმნათ?\n\nგაფრთხილება: თქვენი ვინაობა, კონტაქტები და შეტყობინებები სამუდამოდ დაიკარგება.</string>
|
||||
<string name="startup_failed_activity_title">Briar-ის გაშვების შეცდომა</string>
|
||||
<string name="startup_failed_clock_error">Briar-ის გაშვება ვერ ხერხდება, რადგან თქვენი მოწყობილობის საათი არასწორია.\n\nგთხოვთ, დააყენოთ თქვენს მოწყობილობაზე სწორი დრო და ისევ სცადოთ.</string>
|
||||
<string name="startup_failed_db_error">Briar ვერ ახერხებს მონაცემთა ბაზის გახსნას, რომელიც შეიცავს თქვენს ანგარიშს, კონტაქტებსა და შეტყობინებებს.\n\nგთხოვთ განაახლოთ აპი ბოლო ვერსიამდე და ისევ სცადოთ, ან დააყენეთ ახალი ანგარიში პაროლის მოთხოვნაში „დამავიწყდა პაროლის“ არჩევით.</string>
|
||||
<string name="startup_failed_data_too_old_error">თქვენი ანგარიში შექმნილია ამ აპის ძველი ვერსიის მეშვეობით და ვერ გაიხსნება ამ ვერსიაში.\n\nან ხელახლა უნდა დააინსტალიროთ ძველი ვერსია, ან დააყენეთ ახალი ანგარიში პაროლის მოთხოვნაში „დამავიწყდა პაროლის“ არჩევით.</string>
|
||||
<string name="startup_failed_data_too_new_error">თქვენი ანგარიში შეიქმნა ამ აპის ახალი ვერსიის მეშვეობით და ვერ გაიხსნება ამ ვერსიაში.\n\nგთხოვთ, განაახლოთ ბოლო ვერსიამდე და ხელახლა სცადოთ.</string>
|
||||
<string name="startup_failed_service_error">ვერ ახერხებს საჭირო კომპონენტების გაშვებას.\n\nგთხოვთ, განაახლოთ აპის ბოლო ვერსიამდე და ისევ სცადოთ.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">ესაა საცდელი ვერსიის Briar. თქვენს ანგარიშს %d დღეში გაუვა ვადა და ვეღარ გახანგრძლივდება.</item>
|
||||
<item quantity="other">ეს არის Briar-ის საცდელი ვერსია, რომლის ვადა %d დღეში ამოიწურება და მისი განახლება შეუძლებელია.</item>
|
||||
</plurals>
|
||||
<plurals name="old_android_expiry_warning">
|
||||
<item quantity="one">Android 4 აღარაა მხარდაჭერილი. Briar-ის გაუქმების ვადაა %s (%d დღეში). გთხოვთ, Briar გამოიყენოთ უფრო ახალ მოწყობილობაზე და ახალი ანგარიში შექმნათ.</item>
|
||||
<item quantity="other">Android 4 აღარაა მხარდაჭერილი. Briar შეწყვეტს მუშაობას %s (%d დღეში). გთხოვთ, დააინსტალიროთ Briar უფრო ახალ მოწყობილობაზე და ახალი ანგარიში შექმნათ.</item>
|
||||
</plurals>
|
||||
<string name="expiry_date_reached">პროგრამული უზრუნველყოფის ვადა ამოიწურა.\nგმადლობთ ტესტირებისთვის!</string>
|
||||
<string name="download_briar">თუ გსურთ, კვლავ გამოიყენოთ Briar, ჩამოტვირთეთ ბოლო გამოშვება.</string>
|
||||
<string name="create_new_account">დაგჭირდებათ ახალი ანგარიშის შექმნა, თუმცა იმავე მეტსახელის გამოყენება შეგეძლებათ.</string>
|
||||
<string name="download_briar_button">ბოლო გამოშვების ჩამოტვირთვა</string>
|
||||
<string name="old_android_expiry_date_reached">Briar აღარ გაეშვება Android 4 ვერსიაზე.\nგთხოვთ, დააყენოთ Briar უფრო ახალ მოწყობილობაზე.</string>
|
||||
<string name="old_android_delete_account">ამ მოწყობილობიდან თქვენი ანგარიშის წასაშლელად, შეგიძლიათ შეეხოთ ღილაკს ქვემოთ.</string>
|
||||
<string name="delete_account_button">ანგარიშის წაშლა</string>
|
||||
<string name="startup_open_database">მიმდინარეობს მონაცემთა ბაზის გაშიფვრა…</string>
|
||||
<string name="startup_migrate_database">მიმდინარეობს მონაცემთა ბაზის განახლება…</string>
|
||||
<string name="startup_compact_database">მიმდინარეობს მონაცემთა ბაზის შეკუმშვა…</string>
|
||||
<!--Navigation Drawer-->
|
||||
<string name="nav_drawer_open_description">გახსენით ნავიგაციის პანელი</string>
|
||||
<string name="nav_drawer_close_description">დახურეთ ნავიგაციის პანელი</string>
|
||||
<string name="contact_list_button">კონტაქტები</string>
|
||||
<string name="groups_button">დახურული ჯგუფები</string>
|
||||
<string name="forums_button">ფორუმები</string>
|
||||
<string name="blogs_button">ბლოგები</string>
|
||||
<!--This is part of the main menu. The app will be locked when this is tapped.-->
|
||||
<string name="lock_button">აპის დაბლოკვა</string>
|
||||
<string name="settings_button">პარამეტრები</string>
|
||||
<string name="sign_out_button">გამოსვლა</string>
|
||||
<string name="transports_onboarding_text">შეეხეთ აქ, რომ განსაზღვროთ, როგორ დაუკავშირდეს Briar თქვენს კონტაქტებს.</string>
|
||||
<!--Transports: Tor-->
|
||||
<string name="transport_tor">ინტერნეტი</string>
|
||||
<string name="tor_device_status_online_wifi">თქვენს ტელეფონს აქვს ინტერნეტზე წვდომა Wi-Fi-ს მეშვეობით</string>
|
||||
<string name="tor_device_status_online_mobile">თქვენს ტელეფონს აქვს ინტერნეტზე წვდომა მობილური მონაცემების მეშვეობით</string>
|
||||
<string name="tor_device_status_offline">თქვენს ტელეფონს არ აქვს ინტერნეტზე წვდომა</string>
|
||||
<string name="tor_plugin_status_enabling">Briar უკავშირდება ინტერნეტს</string>
|
||||
<string name="tor_plugin_status_active">Briar დაკავშირებულია ინტერნეტთან</string>
|
||||
<string name="tor_plugin_status_inactive">Briar ვერ უკავშირდება ინტერნეტს</string>
|
||||
<string name="tor_plugin_status_disabled">Briar კონფიგურირებულია, რომ არ გამოიყენოს ინტერნეტი</string>
|
||||
<string name="tor_plugin_status_disabled_mobile_data">Briar კონფიგურირებულია, რომ არ გამოიყენოს მობილური მონაცემები</string>
|
||||
<string name="tor_plugin_status_disabled_battery">Briar კონფიგურირებულია, რომ არ გამოიყენოს ინტერნეტი, თუ ბატარეით მუშაობს.</string>
|
||||
<string name="tor_plugin_status_disabled_country_blocked">Briar კონფიგურირებულია, რომ არ გამოიყენოს ინტერნეტი ამ ქვეყანაში</string>
|
||||
<!--Transports: Wi-Fi-->
|
||||
<string name="transport_lan">Wi-Fi</string>
|
||||
<string name="transport_lan_long">იგივე Wi-Fi ქსელი</string>
|
||||
<string name="lan_device_status_on">თქვენი ტელეფონი დაკავშირებულია Wi-Fi-თან</string>
|
||||
<string name="lan_device_status_off">თქვენი ტელეფონი არ არის დაკავშირებული Wi-Fi-თან</string>
|
||||
<string name="lan_plugin_status_enabling">Briar უკავშირდება Wi-Fi ქსელს</string>
|
||||
<string name="lan_plugin_status_active">Briar დაკავშირებულია Wi-Fi ქსელთან</string>
|
||||
<string name="lan_plugin_status_inactive">Briar ვერ უკავშირდება Wi-Fi ქსელს</string>
|
||||
<string name="lan_plugin_status_disabled">Briar კონფიგურირებულია, რომ არ გამოიყენოს Wi-Fi ქსელი</string>
|
||||
<!--Transports: Bluetooth-->
|
||||
<string name="transport_bt">Bluetooth</string>
|
||||
<string name="bt_device_status_on">თქვენი ტელეფონის Bluetooth ჩართულია</string>
|
||||
<string name="bt_device_status_off">თქვენი ტელეფონის Bluetooth გამორთულია</string>
|
||||
<string name="bt_plugin_status_enabling">Briar უკავშირდება Bluetooth-ს</string>
|
||||
<string name="bt_plugin_status_active">Briar დაკავშირებულია Bluetooth-თან</string>
|
||||
<string name="bt_plugin_status_inactive">Briar ვერ უკავშირდება Bluetooth-ს</string>
|
||||
<string name="bt_plugin_status_disabled">Briar კონფიგურირებულია, რომ არ გამოიყენოს Bluetooth</string>
|
||||
<!--Notifications-->
|
||||
<string name="reminder_notification_title">გამოსულია Briar-იდან</string>
|
||||
<string name="reminder_notification_text">შეეხეთ ანგარიშზე დასაბრუნებლად.</string>
|
||||
<string name="reminder_notification_channel_title">Briar-ში შესვლის შეხსენება</string>
|
||||
<string name="reminder_notification_dismiss">გაუქმება</string>
|
||||
<string name="ongoing_notification_title">შესულია Briar-ში</string>
|
||||
<string name="ongoing_notification_text">შეეხეთ Briar-ის გასახსნელად.</string>
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">ახალი პირადი წერილი.</item>
|
||||
<item quantity="other">%d ახალი პირადი შეტყობინება.</item>
|
||||
</plurals>
|
||||
<plurals name="group_message_notification_text">
|
||||
<item quantity="one">ახალი ჯგუფური წერილი.</item>
|
||||
<item quantity="other">%d ახალი ჯგუფური შეტყობინება.</item>
|
||||
</plurals>
|
||||
<plurals name="forum_post_notification_text">
|
||||
<item quantity="one">ახალი პოსტი ფორუმზე.</item>
|
||||
<item quantity="other">%d ახალი პოსტი ფორუმზე.</item>
|
||||
</plurals>
|
||||
<plurals name="blog_post_notification_text">
|
||||
<item quantity="one">ახალი ჩანაწერი ბლოგზე.</item>
|
||||
<item quantity="other">%d ახალი პოსტი ბლოგზე.</item>
|
||||
</plurals>
|
||||
<!--Misc-->
|
||||
<string name="now">ახლა</string>
|
||||
<string name="show">ჩვენება</string>
|
||||
<string name="hide">დამალვა</string>
|
||||
<string name="ok">კარგი</string>
|
||||
<string name="cancel">გაუქმება</string>
|
||||
<string name="got_it">გასაგებია</string>
|
||||
<string name="delete">წაშლა</string>
|
||||
<string name="accept">მიღება</string>
|
||||
<string name="decline">უარყოფა</string>
|
||||
<string name="online">ონლაინ</string>
|
||||
<string name="offline">ხაზგარეშე</string>
|
||||
<string name="send">გაგზავნა</string>
|
||||
<string name="allow">ნებართვა</string>
|
||||
<string name="open">გახსნა</string>
|
||||
<string name="change">შეცვლა</string>
|
||||
<string name="start">დაწყება</string>
|
||||
<string name="finish">დასრულება</string>
|
||||
<string name="no_data">არაა მონაცემი</string>
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="text_too_long">შეყვანილი ტექსტი მეტად ვრცელია</string>
|
||||
<string name="show_onboarding">გამოჩნდეს დახმარების არე</string>
|
||||
<string name="fix">მოგვარება</string>
|
||||
<string name="help">დახმარება</string>
|
||||
<string name="sorry">ვწუხვართ</string>
|
||||
<string name="error_start_activity">მიუწვდომელია თქვენს სისტემაზე</string>
|
||||
<string name="status_heading">მდგომარეობა:</string>
|
||||
<string name="error">შეცდომა</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<string name="no_contacts">კონტაქტები არაა</string>
|
||||
<string name="no_contacts_action">შეეხეთ + ნიშანს კონტაქტის დასამატებლად</string>
|
||||
<string name="date_no_private_messages">წერილები არაა.</string>
|
||||
<string name="no_private_messages">გამოსაჩენი წერილები არაა</string>
|
||||
<string name="message_hint">ახალი წერილი</string>
|
||||
<string name="message_hint_auto_delete">ახალი თვითწაშლადი წერილი</string>
|
||||
<string name="message_error">შეცდომა წერილის გაგზავნისას</string>
|
||||
<string name="image_caption_hint">წარწერის დამატება (არასავალდებულო)</string>
|
||||
<string name="image_attach">სურათის მიმაგრება</string>
|
||||
<string name="image_attach_error">ვერ მიმაგრდა სურათ(ებ)ი</string>
|
||||
<string name="image_attach_error_too_big">სურათი მეტად დიდია. ზღვარი %d მბაიტი.</string>
|
||||
<string name="image_attach_error_invalid_mime_type">სურათის სახეობა მხარდაუჭერელია: %s</string>
|
||||
<string name="set_contact_alias">კონტაქტის გადარქმევა</string>
|
||||
<string name="set_contact_alias_hint">კონტაქტის სახელი</string>
|
||||
<string name="menu_item_disappearing_messages">თვითწაშლადი წერილები</string>
|
||||
<!--The first placeholder will show a duration like "7 days". The second placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_you_enabled">თქვენი წერილების თვითწაშლამდე დარჩენილია %1$s. %2$s</string>
|
||||
<!--The placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_you_disabled">თქვენი წერილები არ წაიშლება თავისთავად. %1$s</string>
|
||||
<!--The first placeholder will show a contact's name. The second placeholder will show a duration like "7 days". The third placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_contact_enabled">%1$s-ის წერილების თვითწაშლამდე დარჩენილია %2$s. %3$s</string>
|
||||
<plurals name="duration_minutes">
|
||||
<item quantity="one">%d წუთი</item>
|
||||
<item quantity="other">%d წუთი</item>
|
||||
</plurals>
|
||||
<plurals name="duration_hours">
|
||||
<item quantity="one">%d საათი</item>
|
||||
<item quantity="other">%d საათი</item>
|
||||
</plurals>
|
||||
<plurals name="duration_days">
|
||||
<item quantity="one">%d დღე</item>
|
||||
<item quantity="other">%d დღე</item>
|
||||
</plurals>
|
||||
<!--The first placeholder will show a contact's name. The second placeholder at the end will add "Tap to learn more."-->
|
||||
<string name="auto_delete_msg_contact_disabled">%1$s-ის წერილები თავისთავად არ წაიშლება. %2$s</string>
|
||||
<string name="tap_to_learn_more">შეეხეთ, რომ იხილოთ ვრცლად.</string>
|
||||
<string name="auto_delete_changed_warning_title">თვითწაშლადი წერილები შეიცვალა</string>
|
||||
<string name="auto_delete_changed_warning_message_enabled">ვინაიდან დაიწყეთ წერილის შედგენა, თვითწაშლადი წერილები გათიშულია.</string>
|
||||
<string name="auto_delete_changed_warning_message_disabled">ვინაიდან დაიწყეთ წერილის შედგენა, თვითწაშლადი წერილები გათიშულია.</string>
|
||||
<string name="auto_delete_changed_warning_send">მაინც გაგზავნა</string>
|
||||
<string name="delete_all_messages">ყველა წერილის წაშლა</string>
|
||||
<string name="dialog_title_delete_all_messages">შეტყობინების წაშლის დასტური</string>
|
||||
<string name="dialog_message_delete_all_messages">ნამდვილად გსურთ წაშალოთ ყველა წერილი?</string>
|
||||
<string name="dialog_title_not_all_messages_deleted">ვერ წაიშალა ყველა წერილი</string>
|
||||
<string name="dialog_message_not_deleted_ongoing_both">მიმდინარე მოწვევასთან ან წარდგენასთან დაკავშირებული წერილები ვერ წაიშლება მათ დასრულებამდე.</string>
|
||||
<string name="dialog_message_not_deleted_ongoing_introductions">მიმდინარე წარდგენებთან დაკავშირებული წერილები ვერ წაიშლება მათ დასრულებამდე.</string>
|
||||
<string name="dialog_message_not_deleted_ongoing_invitations">მიმდინარე მოწვევებთან დაკავშირებული წერილები ვერ წაიშლება მათ დასრულებამდე.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_both">მოწვევის ან წარდგენის წასაშლელად უნდა მონიშნოთ მოთხოვნაც და პასუხიც.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_introductions">წარდგენის წასაშლელად უნდა მონიშნოთ მოთხოვნაც და პასუხიც.</string>
|
||||
<string name="dialog_message_not_deleted_not_all_selected_invitations">მოწვევის წასაშლელად უნდა მონიშნოთ მოთხოვნაც და პასუხიც.</string>
|
||||
<string name="delete_contact">კონტაქტის წაშლა</string>
|
||||
<string name="dialog_title_delete_contact">კონტაქტის წაშლის დასტური</string>
|
||||
<string name="dialog_message_delete_contact">ნამდვილად გსურთ ამოშალოთ ეს კონტაქტი და მასთან მიმოცვლილი ყველა შეტყობინება?</string>
|
||||
<string name="contact_deleted_toast">კონტაქტი წაიშალა</string>
|
||||
<!--This is shown in the action bar when opening an image in fullscreen that the user sent-->
|
||||
<string name="you">თქვენ</string>
|
||||
<string name="save_image">სურათის შენახვა</string>
|
||||
<string name="dialog_title_save_image">შეინახოს სურათი?</string>
|
||||
<string name="dialog_message_save_image">სურათის შენახვის შემთხვევაში სხვა აპებსაც შეეძლება მასთან წვდომა.\n\nნამდვილად გსურთ შენახვა?</string>
|
||||
<string name="save_image_success">სურათი შენახულია</string>
|
||||
<string name="save_image_error">ვერ შეინახა სურათი</string>
|
||||
<string name="dialog_title_no_image_support">სურათები მიუწვდომელია</string>
|
||||
<string name="dialog_message_no_image_support">თქვენ მიერ დამატებული კონტაქტის Briar-ში ჯერ არაა მხარდაჭერილი სურათების მიმაგრება. როცა ახალ ვერსიაზე გადავლენ, განსხვავებული ნიშანი გამოჩნდება.</string>
|
||||
<string name="dialog_title_image_support">ახლა უკვე სურათებიც შეგიძლიათ გაუგზავნოთ ამ კონტაქტს</string>
|
||||
<string name="dialog_message_image_support">შეეხეთ ამ ნიშანს სურათების მისამაგრებლად.</string>
|
||||
<string name="messaging_too_many_attachments_toast">მხოლოდ პირველი %d სურათი გაიგზავნება</string>
|
||||
<string name="menu_contact">კონტაქტი</string>
|
||||
<!--Adding Contacts-->
|
||||
<string name="add_contact_title">ახლომყოფი კონტაქტის დამატება</string>
|
||||
<string name="face_to_face">პირისპირ უნდა შეხვდეთ იმ ადამიანს, რომლის კონტაქტად დამატებაც გსურთ.\n\nეს იმის საწინდარი იქნება, რომ მომავალში ვერავინ გაასაღებს თავს სხვა პიროვნებად და ვერ გაეცნობა თქვენს წერილებს.</string>
|
||||
<string name="continue_button">განაგრძეთ</string>
|
||||
<string name="try_again_button">კვლავ სცადეთ</string>
|
||||
<string name="waiting_for_contact_to_scan">ელოდება კოდის წაკითხვასა და მიერთებას\u2026</string>
|
||||
<string name="exchanging_contact_details">კონტაქტის მონაცემების გაცვლა\u2026</string>
|
||||
<string name="contact_added_toast">კონტაქტი დაემატა: %s</string>
|
||||
<string name="contact_already_exists">კონტაქტი %s უკვე არსებობს</string>
|
||||
<string name="qr_code_invalid">არასწორი QR-კოდი</string>
|
||||
<string name="qr_code_too_old_1">ამოკითხული QR-კოდი ძველი ვერსიის Briar-იდანაა.\n\nსთხოვეთ თქვენს კონტაქტს, განაახლოს ბოლო ვერსიამდე და შემდეგ სცადეთ ხელახლა.</string>
|
||||
<string name="qr_code_too_new_1">ამოკითხული QR-კოდი ახალი ვერსიის Briar-იდანაა.\n\nგთხოვთ განაახლოთ ბოლო ვერსიამდე და შემდეგ სცადეთ ხელახლა.</string>
|
||||
<string name="camera_error">კამერის შეცდომა</string>
|
||||
<string name="connecting_to_device">უკავშირდება მოწყობილობას\u2026</string>
|
||||
<string name="authenticating_with_device">დამოწმება მოწყობილობით\u2026</string>
|
||||
<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_nearby_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>
|
||||
<string name="add_contact_button">კონტაქტის დამატება</string>
|
||||
<string name="copy_button">ასლი</string>
|
||||
<string name="share_button">გაზიარება</string>
|
||||
<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="your_link">მიაწოდეთ ეს ბმული კონტაქტს, რომლის დამატებაც გსურთ</string>
|
||||
<string name="link_clip_label">Briar-ის ბმული</string>
|
||||
<string name="link_copied_toast">ბმულის ასლი აღებულია</string>
|
||||
<string name="adding_contact_error">შეცდომა წარმოიშვა ამ კონტაქტის დამატებისას.</string>
|
||||
<string name="pending_contact_requests_snackbar">დამატების მომლოდინე მოთხოვნებია</string>
|
||||
<string name="pending_contact_requests">დამატების მომლოდინე მოთხოვნები</string>
|
||||
<string name="no_pending_contacts">მომლოდინე კონტაქტები არაა</string>
|
||||
<string name="waiting_for_contact_to_come_online">ელოდება კონტაქტის ხაზზე შემოსვლას…</string>
|
||||
<string name="connecting">უკავშირდება…</string>
|
||||
<string name="adding_contact">კონტაქტი ემატება…</string>
|
||||
<string name="adding_contact_failed">კონტაქტის დამატება ვერ მოხერხდა</string>
|
||||
<string name="dialog_title_remove_pending_contact">ამოშლის დასტური</string>
|
||||
<string name="dialog_message_remove_pending_contact">ამ კონტაქტის დამატება ჯერ კიდევ მიმდინარეობს. თუ ამოშლით მას ახლა, აღარ დაემატება.</string>
|
||||
<string name="own_link_error">კონტაქტის ბმული შეიყვანეთ და არა თქვენი</string>
|
||||
<string name="nickname_missing">გთხოვთ მიუთითოთ მეტსახელი</string>
|
||||
<string name="invalid_link">არასწორი ბმული</string>
|
||||
<string name="unsupported_link">ბმული ახალი ვერსიის Briar-იდანაა. გთხოვთ განაახლოთ ბოლო ვერსიამდე და შემდეგ სცადეთ ხელახლა.</string>
|
||||
<string name="intent_own_link">საკუთარი ბმული გახსენით. გამოიყენეთ კონტაქტის ბმული!</string>
|
||||
<string name="missing_link">გთხოვთ შეიყვანოთ ბმული</string>
|
||||
<!--This is a numeral indicating the first step in a series of screens-->
|
||||
<string name="step_1">1</string>
|
||||
<!--This is a numeral indicating the second step in a series of screens-->
|
||||
<string name="step_2">2</string>
|
||||
<plurals name="contact_added_notification_text">
|
||||
<item quantity="one">ახალი კონტაქტი დაემატა.</item>
|
||||
<item quantity="other">%d ახალი კონტაქტი დაემატა.</item>
|
||||
</plurals>
|
||||
<string name="offline_state">ინტერნეტკავშირი არაა</string>
|
||||
<string name="duplicate_link_dialog_title">გამეორებული ბმული</string>
|
||||
<string name="duplicate_link_dialog_text_1">უკვე გყავთ მომლოდინე კონტაქტი ამ ბმულით: %s</string>
|
||||
<string name="duplicate_link_dialog_text_1_contact">უკვე გყავთ კონტაქტი ამ ბმულით: %s</string>
|
||||
<!--This is a question asking whether two nicknames refer to the same person-->
|
||||
<string name="duplicate_link_dialog_text_2">%1$s და %2$s ერთი და იგივე პიროვნებაა?</string>
|
||||
<!--This is a button for answering that two nicknames do indeed refer to the same person. This
|
||||
string will be used in a dialog button, so if the translation of this string is longer than 20
|
||||
characters, please use "Yes" instead, and use "No" for the "Different Person" button-->
|
||||
<string name="same_person_button">იგივეა</string>
|
||||
<!--This is a button for answering that two nicknames refer to different people. This string
|
||||
will be used in a dialog button, so if the translation of this string longer than 20 characters,
|
||||
please use "No" instead, and use "Yes" for the "Same Person" button-->
|
||||
<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>
|
||||
<string name="introduction_menu_item">წარდგენის დაწყება</string>
|
||||
<string name="introduction_activity_title">აირჩიეთ კონტაქტი</string>
|
||||
<string name="introduction_not_possible">ერთხელ უკვე წამოწყებული გაქვთ წარდგენა ამ კონტაქტებს შორის. გთხოვთ, ჯერ დაელოდოთ მის დასრულებას. დიდხანს შეიძლება გასტანოს, თუ თქვენ და თქვენი კონტაქტები იშვიათად იმყოფებით ხოლმე ხაზზე.</string>
|
||||
<string name="introduction_message_title">კონტაქტების წარდგენა</string>
|
||||
<string name="introduction_message_hint">წერილის დამატება (არასავალდებულო)</string>
|
||||
<string name="introduction_button">წარდგენის დაწყება</string>
|
||||
<string name="introduction_sent">თქვენი წარდგენა გაგზავნილია.</string>
|
||||
<string name="introduction_error">შეცდომა წარმოიშვა წარდგენის წამოწყებისას.</string>
|
||||
<string name="introduction_request_sent">თქვენ ითხოვეთ, რომ %1$s წარუდგინოთ %2$s-ს.</string>
|
||||
<string name="introduction_request_received">%1$s ითხოვს, რომ თქვენი თავი წარუდგინოს %2$s-ს. გსურთ დაიმატოთ %2$s ნაცნობი ხალხის სიაში?</string>
|
||||
<string name="introduction_request_exists_received">%1$s ცდილობს, რომ თქვენი თავი წარუდგინოს %2$s-ს, მაგრამ %2$s უკვე დამატებული გყავთ. ვინაიდან შეიძლება არ იცოდეს, მაინც გაქვთ საშუალება, უპასუხოთ %1$s-ს:</string>
|
||||
<string name="introduction_request_answered_received">%1$s ითხოვს, რომ თქვენი თავი წარუდგინოს %2$s-ს.</string>
|
||||
<string name="introduction_response_accepted_sent">თქვენ დათანხმდით, რომ თქვენი თავი წარუდგინონ %1$s-ს.</string>
|
||||
<string name="introduction_response_accepted_sent_info">სანამ %1$s დაემატება თქვენს კონტაქტებს, ჯერ უნდა დათანხმდეს წარდგენასაც. გარკვეულ ხანს შეიძლება გასტანოს.</string>
|
||||
<string name="introduction_response_declined_sent">თქვენ უარი განაცხადეთ, რომ თქვენი თავი წარუდგინონ %1$s-ს.</string>
|
||||
<string name="introduction_response_declined_auto">წარდგენა %1$s-სთან ავტომატურად უარყოფილია.</string>
|
||||
<string name="introduction_response_accepted_received">%1$s დათანხმდა, რომ წარუდგინონ %2$s-ს.</string>
|
||||
<string name="introduction_response_declined_received">%1$s უარს აცხადებს, რომ წარუდგინონ %2$s-ს.</string>
|
||||
<string name="introduction_response_declined_received_by_introducee">%1$s აცხადებს, რომ %2$s უარს ამბობს წარდგენაზე.</string>
|
||||
<!--Connect via Bluetooth-->
|
||||
<string name="menu_item_connect_via_bluetooth">დაკავშირება Bluetooth-ით</string>
|
||||
<string name="connect_via_bluetooth_title">დაკავშირება Bluetooth-ით</string>
|
||||
<string name="connect_via_bluetooth_intro">იმ შემთხვევაში, თუ Bluetooth-კავშირები ავტომატურად არ იმუშავებს, ამ ეკრანიდან შეგეძლებათ ხელით მიერთება.\n\nამისათვის თქვენი კონტაქტი ახლოს უნდა გყავდეთ.\n\nაუცილებელია, რომ ორივემ ერთდროულად დააჭიროთ „დაწყებას“.</string>
|
||||
<string name="connect_via_bluetooth_already_discovering">უკვე ცდილობს Bluetooth-ით დაკავშირებას. კვლავ სცადეთ მოგვიანებით.</string>
|
||||
<string name="connect_via_bluetooth_no_location_permission">ვერ განაგრძობს ადგილმდებარეობის ნებართვის გარეშე</string>
|
||||
<string name="connect_via_bluetooth_start">უკავშირდება Bluetooth-ით…</string>
|
||||
<string name="connect_via_bluetooth_success">წარმატებით დაუკავშირდა Bluetooth-ით</string>
|
||||
<string name="connect_via_bluetooth_error">ვერ დაუკავშირდა Bluetooth-ით.</string>
|
||||
<string name="connect_via_bluetooth_error_not_supported">Bluetooth მხარდაუჭერელია ამ მოწყობილობაზე.</string>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">ჯგუფები არაა</string>
|
||||
<string name="groups_list_empty_action">შეეხეთ + ნიშანს ჯგუფის შესაქმნელად ან სთხოვეთ თქვენს კონტაქტებს, გაგიზიარონ ჯგუფები</string>
|
||||
<string name="groups_created_by">შემქმნელია %s</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d წერილი</item>
|
||||
<item quantity="other">%d წერილი</item>
|
||||
</plurals>
|
||||
<string name="groups_group_is_empty">ჯგუფი ცარიელია</string>
|
||||
<string name="groups_group_is_dissolved">ჯგუფი დაიშალა</string>
|
||||
<string name="groups_remove">მოცილება</string>
|
||||
<string name="groups_create_group_title">დახურული ჯგუფის შექმნა</string>
|
||||
<string name="groups_create_group_button">ჯგუფის შექმნა</string>
|
||||
<string name="groups_create_group_invitation_button">მოწვევის გაგზავნა</string>
|
||||
<string name="groups_create_group_hint">აირჩიეთ თქვენი დახურული ჯგუფის სახელი</string>
|
||||
<string name="groups_invitation_sent">ჯგუფის მოწვევა გაგზავნილია</string>
|
||||
<string name="groups_member_list">წევრთა სია</string>
|
||||
<string name="groups_invite_members">წევრების მოწვევა</string>
|
||||
<string name="groups_member_created_you">თქვენ შექმენით ჯგუფი</string>
|
||||
<string name="groups_member_created">%s-მა შექმნა ჯგუფი</string>
|
||||
<string name="groups_member_joined_you">თქვენ შეუერთდით ჯგუფს</string>
|
||||
<string name="groups_member_joined">%s შემოუერთდა ჯგუფს</string>
|
||||
<string name="groups_leave">ჯგუფის დატოვება</string>
|
||||
<string name="groups_leave_dialog_title">ჯგუფის დატოვების დადასტურება</string>
|
||||
<string name="groups_leave_dialog_message">ნამდვილად გსურთ დატოვოთ ეს ჯგუფი?</string>
|
||||
<string name="groups_dissolve">ჯგუფის დაშლა</string>
|
||||
<string name="groups_dissolve_dialog_title">ჯგუფის დაშლის დასტური</string>
|
||||
<string name="groups_dissolve_dialog_message">ნამდვილად გსურთ დაშალოთ ეს ჯგუფი?\n\nდანარჩენი წევრები ვეღარ შეძლებენ საუბრის გაგრძელებას და შესაძლოა, ვეღარ იხილონ ბოლო წერილები.</string>
|
||||
<string name="groups_dissolve_button">დაშლა</string>
|
||||
<string name="groups_dissolved_dialog_title">ჯგუფი დაიშალა</string>
|
||||
<string name="groups_dissolved_dialog_message">ჯგუფი დაიშალა შემქმნელის მიერ.\n\nთქვენ აღარ შეგეძლებათ წერილების დაწერა ამ ჯგუფში და შესაძლოა, ვეღარ იხილოთ ბოლოს გამოქვეყნებული ნაწერები.</string>
|
||||
<!--Private Group Invitations-->
|
||||
<string name="groups_invitations_title">ჯგუფის მოწვევები</string>
|
||||
<string name="groups_invitations_invitation_sent">თქვენ მოიწვიეთ %1$s, რომ შემოუერთდეს ჯგუფს „%2$s“.</string>
|
||||
<string name="groups_invitations_invitation_received">%1$s გიწვევთ, რომ შეუერთდეთ ჯგუფს „%2$s“.</string>
|
||||
<string name="groups_invitations_joined">შეუერთდა ჯგუფს</string>
|
||||
<string name="groups_invitations_declined">ჯგუფის მოწვევა უარყოფილია</string>
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">ღია ჯგუფის %d მოწვევა</item>
|
||||
<item quantity="other">ღია ჯგუფის %d მოწვევა</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">თქვენ დათანხმდით ჯგუფში მიწვევას, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="groups_invitations_response_declined_sent">თქვენ უარყავით ჯგუფში მიწვევა, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="groups_invitations_response_declined_auto">ჯგუფში მიწვევა, რომელსაც %s გიგზავნიდათ, ავტომატურად უარყოფილია.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s დათანხმდა ჯგუფში მოწვევას.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s უარს აცხადებს ჯგუფში მოწვევაზე.</string>
|
||||
<string name="sharing_status_groups">მხოლოდ ჯგუფის შემქმნელს შეუძლია ახალი წევრების მოწვევა. ქვემოთ მოცემულია ამ ჯგუფის ყველა წევრი.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">კონტაქტების გამხელა</string>
|
||||
<string name="groups_reveal_dialog_message">შეგიძლიათ გაუმხილოთ თქვენი კონტაქტები ჯგუფის ამჟამინდელ და მომავალ წევრებს.\n\nკონტაქტების გამხელით ჯგუფთან კავშირი იქნება მეტად სწრაფი და საიმედო, ვინაიდან გამხელილ კონტაქტებთან დაკავშირება მაშინაც შეგეძლებათ, როცა ჯგუფის შემქმნელი ხაზზე არ იქნება.</string>
|
||||
<string name="groups_reveal_visible">პირთან ურთიერთობა ხილულია ჯგუფისთვის</string>
|
||||
<string name="groups_reveal_visible_revealed_by_us">პირთან ურთიერთობა ხილულია ჯგუფისთვის (თქვენ მიერ გამხელილი)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_contact">პირთან ურთიერთობა ხილულია ჯგუფისთვის (%s-ის მიერ გამხელილი)</string>
|
||||
<string name="groups_reveal_invisible">პირთან ურთიერთობა დაფარულია ჯგუფისთვის</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">ფორუმები არაა</string>
|
||||
<string name="no_forums_action">შეეხეთ + ნიშანს ფორუმის შესაქმნელად ან სთხოვეთ თქვენს კონტაქტებს, გაგიზიარონ ფორუმები</string>
|
||||
<string name="create_forum_title">ფორუმის შექმნა</string>
|
||||
<string name="choose_forum_hint">აირჩიეთ თქვენი ფორუმის სახელი</string>
|
||||
<string name="create_forum_button">ფორუმის შექმნა</string>
|
||||
<string name="forum_created_toast">ფორუმი შექმნილია</string>
|
||||
<string name="no_forum_posts">გამოსაჩენი პოსტები არაა</string>
|
||||
<string name="no_posts">პოსტები არაა</string>
|
||||
<plurals name="posts">
|
||||
<item quantity="one">%d პოსტი</item>
|
||||
<item quantity="other">%d პოსტი</item>
|
||||
</plurals>
|
||||
<string name="forum_new_message_hint">ახალი პოსტი</string>
|
||||
<string name="forum_message_reply_hint">ახალი პასუხი</string>
|
||||
<string name="btn_reply">პასუხი</string>
|
||||
<string name="forum_leave">ფორუმის დატოვება</string>
|
||||
<string name="dialog_title_leave_forum">ფორუმის დატოვების დასტური</string>
|
||||
<string name="dialog_message_leave_forum">ნამდვილად გსურთ დატოვოთ ეს ფორუმი?\n\nკონტაქტები, რომელთაც უზიარებდით ამ ფორუმს, ვეღარ მიიღებენ სიახლეებს.</string>
|
||||
<string name="dialog_button_leave">დატოვება</string>
|
||||
<string name="forum_left_toast">ფორუმი დატოვებულია</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="forum_share_button">ფორუმის გაზიარება</string>
|
||||
<string name="contacts_selected">კონტაქტები შერჩეულია</string>
|
||||
<string name="activity_share_toolbar_header">კონტაქტების არჩევა</string>
|
||||
<string name="no_contacts_selector">კონტაქტები არაა</string>
|
||||
<string name="no_contacts_selector_action">გთხოვთ დაბრუნდეთ ხალხის დამატების შემდგომ</string>
|
||||
<string name="forum_shared_snackbar">ფორუმი გაზიარებულია შერჩეულ კონტაქტებთან</string>
|
||||
<string name="forum_share_message">წერილის დამატება (არასავალდებულო)</string>
|
||||
<string name="forum_share_error">შეცდომა წარმოიშვა ამ ფორუმის გაზიარებისას.</string>
|
||||
<string name="forum_invitation_received">%1$s გიზიარებთ „%2$s“ ფორუმს.</string>
|
||||
<string name="forum_invitation_sent">თქვენ ფორუმი „%1$s“ გაუზიარეთ %2$s-ს.</string>
|
||||
<string name="forum_invitations_title">ფორუმის მოწვევები</string>
|
||||
<string name="forum_invitation_exists">თქვენ უკვე დათანხმდით ამ ფორუმში მიწვევას.\n\nდამატებით მიწვევებზე დათანხმებით ფორუმთან კავშირი მეტად სწრაფი და მდგრადი გახდება.</string>
|
||||
<string name="forum_joined_toast">შეუერთდა ფორუმს</string>
|
||||
<string name="forum_declined_toast">მოწვევა უარყოფილია</string>
|
||||
<string name="shared_by_format">გამზიარებელია %s</string>
|
||||
<string name="forum_invitation_already_sharing">უკვე გაზიარებულია</string>
|
||||
<string name="forum_invitation_response_accepted_sent">თქვენ დათანხმდით ფორუმში მიწვევას, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="forum_invitation_response_declined_sent">თქვენ უარყავით ფორუმში მიწვევა, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="forum_invitation_response_declined_auto">ფორუმში მიწვევა, რომელსაც %s გიგზავნიდათ, ავტომატურად უარყოფილია.</string>
|
||||
<string name="forum_invitation_response_accepted_received">%s დათანხმდა ფორუმში მოწვევას.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s უარს აცხადებს ფორუმში მოწვევაზე.</string>
|
||||
<string name="sharing_status">გაზიარების მდგომარეობა</string>
|
||||
<string name="sharing_status_forum">ნებისმიერ წევრს შეუძლია გაუზიაროს ფორუმი საკუთარ კონტაქტებს. ამასთანავე, შესაძლოა, ჯგუფში კიდევ არიან წევრები, რომელთაც ვერ ხედავთ.</string>
|
||||
<string name="shared_with">იზიარებს %1$d (%2$d ხაზზეა)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d გაზიარებული ფორუმი კონტაქტების მიერ</item>
|
||||
<item quantity="other">%d გაზიარებული ფორუმი კონტაქტების მიერ</item>
|
||||
</plurals>
|
||||
<string name="nobody">არავინ</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">პოსტები არაა</string>
|
||||
<string name="read_more">ვრცლად</string>
|
||||
<string name="blogs_write_blog_post">ბლოგზე ჩანაწერის გაკეთება</string>
|
||||
<string name="blogs_write_blog_post_body_hint">აკრიფეთ პოსტი</string>
|
||||
<string name="blogs_publish_blog_post">გამოქვეყნება</string>
|
||||
<string name="blogs_blog_post_created">ბლოგზე ჩანაწერი შექმნილია</string>
|
||||
<string name="blogs_blog_post_received">ბლოგის ახალი ჩანაწერია მიღებული</string>
|
||||
<string name="blogs_blog_post_scroll_to">გადასვლა</string>
|
||||
<string name="blogs_feed_empty_state">პოსტები არაა</string>
|
||||
<string name="blogs_feed_empty_state_action">თქვენი კონტაქტების დადებული და თქვენ მიერ გამოწერილი ბლოგების პოსტები გამოჩნდება აქ\n\nშეეხეთ კალმის ნიშანს პოსტის დასაწერად</string>
|
||||
<string name="blogs_remove_blog">ბლოგის მოცილება</string>
|
||||
<string name="blogs_remove_blog_dialog_message">ნამდვილად გსურთ ამოშალოთ ეს ბლოგი?\n\nპოსტები მოცილდება თქვენი მოწყობილობიდან, მაგრამ დარჩება სხვების მოწყობილობებზე.\n\nკონტაქტები, რომელთაც უზიარებდით ამ ბლოგს, ვეღარ მიიღებენ სიახლეებს.</string>
|
||||
<string name="blogs_remove_blog_ok">მოცილება</string>
|
||||
<string name="blogs_blog_removed">ბლოგი მოცილებულია</string>
|
||||
<string name="blogs_reblog_comment_hint">შენიშვნის დამატება (არასავალდებულო)</string>
|
||||
<string name="blogs_reblog_button">ხელახლა დადება</string>
|
||||
<!--Blog Sharing-->
|
||||
<string name="blogs_sharing_share">ბლოგის გაზიარება</string>
|
||||
<string name="blogs_sharing_error">შეცდომა წარმოიშვა ბლოგის გაზიარებისას.</string>
|
||||
<string name="blogs_sharing_button">ბლოგის გაზიარება</string>
|
||||
<string name="blogs_sharing_snackbar">ბლოგი გაზიარებულია შერჩეულ კონტაქტებთან</string>
|
||||
<string name="blogs_sharing_response_accepted_sent">თქვენ დათანხმდით ბლოგზე მიწვევას, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="blogs_sharing_response_declined_sent">თქვენ უარყავით ბლოგზე მიწვევა, რომელსაც გიგზავნიდათ %s.</string>
|
||||
<string name="blogs_sharing_response_declined_auto">ბლოგზე მიწვევა, რომელსაც %s გიგზავნიდათ, ავტომატურად უარყოფილია.</string>
|
||||
<string name="blogs_sharing_response_accepted_received">%s დათანხმდა ბლოგზე მოწვევას.</string>
|
||||
<string name="blogs_sharing_response_declined_received">%s უარყოფს ბლოგზე მოწვევას.</string>
|
||||
<string name="blogs_sharing_invitation_received">%1$s გიზიარებთ „%2$s“ ბლოგს.</string>
|
||||
<string name="blogs_sharing_invitation_sent">თქვენ ბლოგი „%1$s“ გაუზიარეთ %2$s-ს.</string>
|
||||
<string name="blogs_sharing_invitations_title">ბლოგის მოწვევები</string>
|
||||
<string name="blogs_sharing_joined_toast">გამოწერილია ბლოგი</string>
|
||||
<string name="blogs_sharing_declined_toast">მოწვევა უარყოფილია</string>
|
||||
<string name="sharing_status_blog">ნებისმიერს, რომელსაც გამოწერილი აქვს ბლოგი, შეუძლია გაუზიაროს თავის კონტაქტებს. თქვენ უზიარებთ ბლოგს მოცემულ კონტაქტებს. ამასთანავე, შესაძლოა, კიდევ არიან სხვა გამომწერები, რომელთაც ვერ ხედავთ.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">RSS-არხის გადმოტანა</string>
|
||||
<string name="blogs_rss_feeds_import_button">გადმოტანა</string>
|
||||
<string name="blogs_rss_feeds_import_hint">შეიყვანეთ RSS-არხის ბმული</string>
|
||||
<string name="blogs_rss_feeds_import_error">ვწუხვართ! შეცდომა წარმოიშვა არხის გადმოტანისას.</string>
|
||||
<string name="blogs_rss_feeds_import_exists">არხი უკვე გადმოტანილია.</string>
|
||||
<string name="blogs_rss_feeds">RSS-არხები</string>
|
||||
<string name="blogs_rss_feeds_manage_imported">გადმოტანილი:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">ავტორი:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">ბოლო განახლება:</string>
|
||||
<string name="blogs_rss_remove_feed">არხის მოცილება</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">ნამდვილად გსურთ ამოშალოთ ეს არხი?\n\nპოსტები მოცილდება თქვენი მოწყობილობიდან, მაგრამ დარჩება სხვების მოწყობილობებზე.\n\nკონტაქტები, რომელთაც უზიარებდით ამ არხს, ვეღარ მიიღებენ სიახლეებს.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">მოცილება</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">RSS-არხები არაა\n\nშეეხეთ + ნიშანს არხის გადმოსატანად</string>
|
||||
<string name="blogs_rss_feeds_manage_error">რაღაც შეცდომა წარმოიშვა თქვენი არხების ჩატვირთვისას. გთხოვთ სცადოთ მოგვიანებით.</string>
|
||||
<!--Settings Profile Picture-->
|
||||
<string name="change_profile_picture">შეეხეთ პროფილის სურათის შესაცვლელად</string>
|
||||
<string name="dialog_confirm_profile_picture_title">პროფილის სურათის შეცვლა</string>
|
||||
<string name="dialog_confirm_profile_picture_remark">მხოლოდ თქვენი კონტაქტები იხილავენ ამ სურათს</string>
|
||||
<string name="change_profile_picture_failed_message">სამწუხაროდ, რაღაც ხარვეზი წარმოიშვა პროფილის სურათის განახლებისას</string>
|
||||
<!--Settings Display-->
|
||||
<string name="pref_language_title">ენა და რეგიონი</string>
|
||||
<string name="pref_language_changed">პარამეტრების ცვლილება აისახება, როცა ახლიდან ჩაირთვება Briar. გთხოვთ გამოხვიდეთ ანგარიშიდან და კვლავ გაუშვათ Briar.</string>
|
||||
<string name="pref_language_default">სისტემის მიხედვით</string>
|
||||
<string name="display_settings_title">ჩვენება</string>
|
||||
<string name="pref_theme_title">იერსახე</string>
|
||||
<string name="pref_theme_light">ნათელი</string>
|
||||
<string name="pref_theme_dark">მუქი</string>
|
||||
<string name="pref_theme_auto">ავტომატური (დღის მონაკვეთით)</string>
|
||||
<string name="pref_theme_system">სისტემის მიხედვით</string>
|
||||
<!--Settings Connections-->
|
||||
<string name="network_settings_title">კავშირი</string>
|
||||
<string name="bluetooth_setting">კონტაქტებთან დაკავშირება Bluetooth-ით</string>
|
||||
<string name="wifi_setting">კონტაქტებთან დაკავშირება საერთო WiFi-ქსელით</string>
|
||||
<string name="tor_enable_title">კონტაქტებთან დაკავშირება ინტერნეტით</string>
|
||||
<string name="tor_enable_summary">ყველა კავშირი Tor-ქსელით გაივლის პირადულობისთვის</string>
|
||||
<string name="tor_network_setting">Tor-ქსელთან დაკავშირების გზა</string>
|
||||
<string name="tor_network_setting_automatic">ავტომატურად მდებარეობის მიხედვით</string>
|
||||
<string name="tor_network_setting_without_bridges">Tor-ქსელთან მიერთება უხიდებოდ</string>
|
||||
<string name="tor_network_setting_with_bridges">Tor-ქსელთან მიერთება ხიდებით</string>
|
||||
<string name="tor_network_setting_never">ინტერნეტთან კავშირის გარეშე</string>
|
||||
<!--How and when Briar will connect to Tor: E.g. "Don't connect to the Internet (in China)" or "Use Tor network with bridges (in Belarus)"-->
|
||||
<string name="tor_network_setting_summary">ავტომატური: %1$s (%2$s)</string>
|
||||
<string name="tor_mobile_data_title">ფიჭური ინტერნეტის გამოყენება</string>
|
||||
<string name="tor_only_when_charging_title">ინტერნეტთან დაკავშირება მხოლოდ დამუხტვისას</string>
|
||||
<string name="tor_only_when_charging_summary">თიშავს ინტერნეტკავშირს, როცა მოწყობილობა ბატარეას იყენებს</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">უსაფრთხოება</string>
|
||||
<string name="pref_lock_title">აპის ჩაკეტვა</string>
|
||||
<string name="pref_lock_summary">მოწყობილობის ეკრანის ჩამკეტით Briar-ის დაცვა ანგარიშზე ყოფნისას</string>
|
||||
<string name="pref_lock_disabled_summary">ამ შესაძლებლობით სარგებლობისთვის დააყენეთ ეკრანის ჩამკეტი თქვენს მოწყობილობაზე</string>
|
||||
<string name="pref_lock_timeout_title">აპის ჩაკეტვა უმოქმედობისას</string>
|
||||
<!--The %s placeholder is replaced with the following time spans, e.g. 5 Minutes, 1 Hour-->
|
||||
<string name="pref_lock_timeout_summary">როცა არ იყენებთ Briar-ს, ავტომატურად ჩაიკეტება %s</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_1">1 წუთში</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_5">5 წუთში</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_15">15 წუთში</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_30">30 წუთში</string>
|
||||
<!--Will be shown in a list of lock times. Should fit into the %s of "automatically lock it after %s"-->
|
||||
<string name="pref_lock_timeout_60">1 საათში</string>
|
||||
<string name="pref_lock_timeout_never">არასდროს</string>
|
||||
<string name="pref_lock_timeout_never_summary">არასდროს ჩაიკეტოს Briar ავტომატურად</string>
|
||||
<string name="change_password">პაროლის შეცვლა</string>
|
||||
<string name="current_password">მიმდინარე პაროლი</string>
|
||||
<string name="choose_new_password">ახალი პაროლი</string>
|
||||
<string name="confirm_new_password">ახალი პაროლის დადასტურება</string>
|
||||
<string name="password_changed">პაროლი შეიცვალა.</string>
|
||||
<string name="panic_setting">განგაშის ღილაკის გამართვა</string>
|
||||
<string name="panic_setting_title">განგაშის ღილაკი</string>
|
||||
<string name="panic_setting_hint">განსაზღვრეთ, როგორ იმოქმედებს Briar განგაშის ღილაკზე დაჭერისას</string>
|
||||
<string name="panic_app_setting_title">განგაშის ღილაკის აპი</string>
|
||||
<string name="unknown_app">უცნობი აპი</string>
|
||||
<string name="panic_app_setting_summary">არაა აპი მითითებული</string>
|
||||
<string name="panic_app_setting_none">არცერთი</string>
|
||||
<string name="dialog_title_connect_panic_app">განგაშის აპის დადასტურება</string>
|
||||
<string name="dialog_message_connect_panic_app">ნამდვილად გსურთ, რომ %1$s გახდეს განგაშის ღილაკის გამანადგურებელი მოქმედებების გამომწვევი?</string>
|
||||
<string name="panic_setting_destructive_action">გამანადგურებელი მოქმედებები</string>
|
||||
<string name="panic_setting_signout_title">გამოსვლა</string>
|
||||
<string name="panic_setting_signout_summary">Briar-იდან გამოსვლა განგაშის ღილაკზე დაჭერისას</string>
|
||||
<string name="purge_setting_title">ანგარიშის წაშლა</string>
|
||||
<string name="purge_setting_summary">წაიშალოს თქვენი Briar-ანგარიში განგაშის ღილაკზე დაჭერისას. გაფრთხილება: შედეგად, სამუდამოდ დაიკარგება თქვენი ვინაობის მონაცემები, კონტაქტები და წერილები</string>
|
||||
<!--Settings Notifications-->
|
||||
<string name="notification_settings_title">შეტყობინებები</string>
|
||||
<string name="notify_sign_in_title">ანგარიშზე შესვლის შეხსენება</string>
|
||||
<string name="notify_sign_in_summary">შეხსენების ჩვენება ტელეფონის ჩართვისას ან აპის განახლების შემდგომ</string>
|
||||
<string name="notify_private_messages_setting_title">პირადი წერილები</string>
|
||||
<string name="notify_private_messages_setting_summary">შეტყობინება პირად წერილებზე</string>
|
||||
<string name="notify_private_messages_setting_summary_26">შეტყობინების გამართვა პირადი წერილებისთვის</string>
|
||||
<string name="notify_group_messages_setting_title">ჯგუფის წერილები</string>
|
||||
<string name="notify_group_messages_setting_summary">შეტყობინება ჯგუფის წერილებზე</string>
|
||||
<string name="notify_group_messages_setting_summary_26">შეტყობინების გამართვა ჯგუფის წერილებისთვის</string>
|
||||
<string name="notify_forum_posts_setting_title">ფორუმის პოსტები</string>
|
||||
<string name="notify_forum_posts_setting_summary">შეტყობინება ფორუმის პოსტებზე</string>
|
||||
<string name="notify_forum_posts_setting_summary_26">შეტყობინების გამართვა ფორუმის პოსტებისთვის</string>
|
||||
<string name="notify_blog_posts_setting_title">ბლოგის ჩანაწერები</string>
|
||||
<string name="notify_blog_posts_setting_summary">შეტყობინება ბლოგის პოსტებზე</string>
|
||||
<string name="notify_blog_posts_setting_summary_26">შეტყობინების გამართვა ბლოგის ჩანაწერებისთვის</string>
|
||||
<string name="notify_vibration_setting">ვიბრაცია</string>
|
||||
<string name="notify_sound_setting">ხმა</string>
|
||||
<string name="notify_sound_setting_default">ნაგულისხმევი ზარის ხმა</string>
|
||||
<string name="notify_sound_setting_disabled">არცერთი</string>
|
||||
<string name="choose_ringtone_title">აირჩიეთ ზარის ხმა</string>
|
||||
<string name="cannot_load_ringtone">ვერ ჩაიტვირთა ზარის ხმა</string>
|
||||
<!--Mailbox-->
|
||||
<string name="mailbox_settings_title">Mailbox</string>
|
||||
<string name="mailbox_setup_title">Mailbox – გამართვა</string>
|
||||
<string name="mailbox_setup_intro">Mailbox საშუალებას იძლევა, წერილები მიიღოთ თქვენი კონტაქტებისგან მაშინაც კი, როცა ხაზზე არ იმყოფებით. Mailbox მიიღებს თქვენს წერილებს და შეინახავს მანამ, სანამ ხაზზე არ გახვალთ.\n
|
||||
\nშეგიძლიათ დააყენოთ Briar Mailbox აპი სათადარიგო მოწყობილობაზე. დატოვეთ დაერთებული კვებასა და WiFi-ქსელზე, რომ მუდამ ხაზზე იყოს.</string>
|
||||
<string name="mailbox_setup_download">ჯერ დააყენეთ Mailbox აპი რამე სხვა მოწყობილობაზე, ამისთვის „Briar Mailbox“ მოძებნეთ Google Play-ში ან იქ, საიდანაც თავის დროზე ჩამოტვირთეთ Briar.\n
|
||||
\nშემდეგ თქვენი Mailbox დაუკავშირეთ Briar-ს QR-კოდის მეშვეობით, რომელიც გამოჩნდება Mailbox-ის აპში.</string>
|
||||
<string name="mailbox_setup_download_link">ჩამოსატვირთი ბმულის გაზიარება</string>
|
||||
<string name="mailbox_setup_button_scan">Mailbox-ის QR-კოდის წაკითხვა</string>
|
||||
<string name="permission_camera_qr_denied_body">თქვენ უარყოფილი გაქვთ კამერასთან წვდომის ნებართვა, მაგრამ QR-კოდის წასაკითხად აუცილებელია კამერა.\n\nგთხოვთ გაითვალისწინოთ და დართოთ ნება.</string>
|
||||
<string name="mailbox_setup_connecting">უკავშირდება…</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_title">არასწორი QR-კოდი</string>
|
||||
<string name="mailbox_setup_qr_code_wrong_description">ამოკითხული კოდი არასწორია. გთხოვთ გახსნათ Briar-ის Mailbox-აპი თქვენი Mailbox-ის მოწყობილობაზე და წააკითხოთ იქ ნაჩვენები QR-კოდი.</string>
|
||||
<string name="mailbox_setup_already_paired_title">Mailbox უკვე მიბმულია</string>
|
||||
<string name="mailbox_setup_already_paired_description">ჩახსენით Mailbox თქვენს სათადარიგო მოწყობილობაზე და კვლავ სცადეთ.</string>
|
||||
<string name="mailbox_setup_io_error_title">ვერ დაუკავშირდა</string>
|
||||
<string name="mailbox_setup_io_error_description">დარწმუნდით, რომ ორივე მოწყობილობა დაკავშირებულია ინტერნეტთან და კვლავ სცადეთ.</string>
|
||||
<string name="mailbox_setup_assertion_error_title">Mailbox-ის შეცდომა</string>
|
||||
<string name="mailbox_setup_assertion_error_description">გთხოვთ გამოგვეხმაუროთ (ვინაობის გაუმხელი მონაცემებით) Briar-პროგრამიდან, თუ ხარვეზი არ გამოსწორდება.</string>
|
||||
<string name="mailbox_setup_camera_error_description">ვერ დაუკავშირდა კამერას. კვლავ სცადეთ, უმჯობესია მოწყობილობის ახლიდან ჩართვის შემდეგ.</string>
|
||||
<string name="mailbox_setup_paired_title">დაკავშირებულია</string>
|
||||
<string name="mailbox_setup_paired_description">თქვენი Mailbox წარმატებით მიება Briar-ს.\n
|
||||
\nდატოვეთ Mailbox ჩართული კვებასა და WiFi-ქსელში, რომ მუდამ ხაზზე იყოს.</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">Mailbox-ის მდგომარეობა</string>
|
||||
<string name="mailbox_status_connected_title">Mailbox გაშვებულია</string>
|
||||
<string name="mailbox_status_problem_title">Briar ხარვეზებს აწყდება Mailbox-თან დაკავშირებისას</string>
|
||||
<string name="mailbox_status_failure_title">Mailbox მიუწვდომელია</string>
|
||||
<string name="mailbox_status_app_too_old_title">Briar ზედმეტად ძველია</string>
|
||||
<string name="mailbox_status_app_too_old_message">განაახლეთ Briar ბოლო ვერსიამდე და კვლავ სცადეთ.</string>
|
||||
<string name="mailbox_status_mailbox_too_old_title">Mailbox ზედმეტად ძველია</string>
|
||||
<string name="mailbox_status_mailbox_too_old_message">განაახლეთ თქვენი Mailbox ბოლო ვერსიამდე და კვლავ სცადეთ.</string>
|
||||
<string name="mailbox_status_check_button">კავშირის შემოწმება</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<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">ჩაიხსნას Mailbox?</string>
|
||||
<string name="mailbox_status_unlink_dialog_question">ნამდვილად გსურთ ჩაიხსნას თქვენი Mailbox?</string>
|
||||
<string name="mailbox_status_unlink_dialog_warning">თუ ჩაიხსნება Mailbox, ვეღარ მიიღებთ წერილებს, როცა Briar ხაზზე არ იქნება.</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">თქვენი Mailbox ჩახსნილია</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_message">შემდეგ ჯერზე, როცა წვდომას მოახერხებთ თქვენს Mailbox-მოწყობილობასთან, გახსენით Mailbox-აპი და შეეხეთ „ჩახსნის“ ღილაკს პროცესის დასასრულებლად.\n\nთუ აღარ გაქვთ წვდომა თქვენს Mailbox-მოწყობილობასთან, ნუ იღელვებთ. თქვენი მონაცემები დაშიფრულია და მაინც დაცული დარჩება, თუ ვერ დაასრულებთ ჩახსნის პროცესს.</string>
|
||||
<string name="mailbox_status_unlink_success">თქვენი Mailbox ჩახსნილია</string>
|
||||
<string name="mailbox_error_notification_channel_title">Briar Mailbox ხარვეზი</string>
|
||||
<string name="mailbox_error_notification_title">Briar Mailbox მიუწვდომელია</string>
|
||||
<string name="mailbox_error_notification_text">შეეხეთ ხარვეზის მოსაგვარებლად.</string>
|
||||
<string name="mailbox_error_wizard_button">ხარვეზის მოგვარება</string>
|
||||
<string name="mailbox_error_wizard_title">Mailbox-ის ხარვეზების აღმოფხვრის მეგზური</string>
|
||||
<string name="mailbox_error_wizard_question1">გაქვთ წვდომა Mailbox-მოწყობილობასთან?</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">შეამოწმეთ, ჩართულია თუ არა Mailbox-მოწყობილობა და დაკავშირებულია თუ არა ინტერნეტთან.</string>
|
||||
<string name="mailbox_error_wizard_question1_1">გახსენით Mailbox-აპი. რას ხედავთ?</string>
|
||||
<string name="mailbox_error_wizard_answer1_1">ვხედავ მითითებებს Mailbox-ის გასამართად</string>
|
||||
<string name="mailbox_error_wizard_answer1_2">ვხედავ QR-კოდს</string>
|
||||
<string name="mailbox_error_wizard_answer1_3">ვხედავ, რომ „Mailbox გაშვებულია“</string>
|
||||
<string name="mailbox_error_wizard_answer1_4">ვხედავ, რომ „მოწყობილობა კავშირგარეშეა“</string>
|
||||
<string name="mailbox_error_wizard_info1_1_1">გთხოვთ ჩახსნათ Mailbox ქვემოთ არსებული ღილაკით, შემდეგ კი მიჰყვეთ Mailbox-მოწყობილობაზე მითითებებს ხელახლა მიბმისთვის.</string>
|
||||
<string name="mailbox_error_wizard_info_1_1_2">გთხოვთ ჩახსნათ Mailbox ქვემოთ არსებული ღილაკით, შემდეგ წააკითხოთ QR-კოდი ხელახლა.</string>
|
||||
<string name="mailbox_error_wizard_info1_1_3">გთხოვთ გამოიყენოთ ქვემოთ არსებული ღილაკი კავშირის შესამოწმებლად Briar-სა და Mailbox-ს შორის.\n\n
|
||||
თუ კავშირი მაინც ვერ შედგება:\n
|
||||
\u2022 შეამოწმეთ, განახლებულია თუ არა Mailbox და Briar ბოლო ვერსიებამდე.\n
|
||||
\u2022 ხელახლა ჩართეთ Mailbox- და Briar-მოწყობილობები და კვლავ სცადეთ.</string>
|
||||
<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">შესახებ</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>
|
||||
<string name="briar_privacy_policy">\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">ამ პარამეტრის ჩართვის შედეგად ახალი
|
||||
წერილები მოცემულ მიმოწერაში თავისთავად წაიშლება 7\u00A0დღის შემდეგ.
|
||||
\n\nდროის ათვლა გამგზავნის ასლისთვის დაიწყება წერილის მიწოდების შემდგომ.
|
||||
დროის ათვლა მიმღების ასლისთვის დაიწყება წერილის გახსნის შემდგომ.
|
||||
\n\nთვითწაშლად წერილებს დაედება ბომბის ნიშანი.
|
||||
\n\nგაითვალისწინეთ, რომ მიმღებებს მაინც შეეძლებათ თქვენ მიერ გაგზავნილი წერილების ასლის შექმნა.
|
||||
\n\nთუ შეცვლით ამ პარამეტრს, დაუყოვნებლივ აისახება თქვენს ახალ წერილებზე, ხოლო მიმღები
|
||||
კონტაქტის წერილებზე მას შემდეგ, რაც თქვენგან გაგზავნილ მომდევნო წერილს მიიღებს.
|
||||
თქვენს კონტაქტსაც შეუძლია ამ პარამეტრის შეცვლა ორივე თქვენგანისთვის.</string>
|
||||
<string name="learn_more">ვრცლად</string>
|
||||
<string name="disappearing_messages_summary">ამ მიმოწერაში მომდევნო წერილები თავისთავად წაიშალოს 7\u00A0დღის შემდეგ.</string>
|
||||
<!--Settings Actions-->
|
||||
<string name="pref_category_actions">მოქმედებები</string>
|
||||
<string name="send_feedback">გამოხმაურება</string>
|
||||
<!--Link Warning-->
|
||||
<string name="link_warning_title">გაფრთხილება ბმულზე</string>
|
||||
<string name="link_warning_intro">თქვენ აპირებთ ამ ბმულის გახსნას გარეშე აპით</string>
|
||||
<string name="link_warning_text">შედეგად შეიძლება თქვენი ვინაობა გამჟღავნდეს. კარგად დაფიქრდით, ღირს თუ არა იმ პირის ნდობა, რომელმაც ეს ბმული გამოგიგზავნათ და უმჯობესია, მის გასახსნელად Tor-ბრაუზერი გამოიყენოთ.</string>
|
||||
<string name="link_warning_open_link">ბმულის გახსნა</string>
|
||||
<!--Crash Reporter-->
|
||||
<string name="crash_report_title">Briar-ის გათიშვის მოხსენება</string>
|
||||
<string name="briar_crashed">სამწუხაროდ, Briar უეცრად გაითიშა</string>
|
||||
<string name="not_your_fault">თქვენი ბრალი არაა.</string>
|
||||
<string name="please_send_report">დაგვეხმარეთ, რომ გავაუმჯობესოთ Briar და გამოგვიგზავნეთ მოხსენება გათიშვის შესახებ.</string>
|
||||
<string name="report_is_encrypted">გპირდებით, რომ ეს მოხსენება დაიშიფრება და გამოიგზავნება უსაფრთხოდ.</string>
|
||||
<string name="feedback_title">გამოხმაურება</string>
|
||||
<string name="describe_crash">აღწერეთ, რა მოხდა (არასავალდებულო)</string>
|
||||
<string name="enter_feedback">შეიყვანეთ გამოხმაურების ტექსტი</string>
|
||||
<string name="optional_contact_email">თქვენი ელფოსტა (არასავალდებულო)</string>
|
||||
<string name="privacy_policy">მონაცემთა გამოგზავნით ეთანხმებით ჩვენს <a href="">პირადულობის დაცვის დებულებას</a></string>
|
||||
<string name="include_debug_report_crash">დაერთვება გათიშვის შესახებ ვინაობის გამხელისგან დაცული მონაცემებიც</string>
|
||||
<string name="include_debug_report_feedback">დაერთვება მოწყობილობის შესახებ ვინაობის გამხელისგან დაცული მონაცემებიც</string>
|
||||
<string name="dev_report_user_info">მომხმარებლის მონაცემები</string>
|
||||
<string name="dev_report_basic_info">ძირითადი მონაცემები</string>
|
||||
<string name="dev_report_device_info">მოწყობილობის მონაცემები</string>
|
||||
<string name="dev_report_stacktrace">მიმდევრობითი აღრიცხვა</string>
|
||||
<string name="dev_report_time_info">დროის შესახებ მონაცემები</string>
|
||||
<string name="dev_report_memory">მეხსიერება</string>
|
||||
<string name="dev_report_storage">საცავი</string>
|
||||
<string name="dev_report_connectivity">დაკავშირებულობა</string>
|
||||
<string name="dev_report_network_usage">ქსელის მოხმარება</string>
|
||||
<string name="dev_report_build_config">ანაწყობის აგებულება</string>
|
||||
<string name="dev_report_logcat">აპის ჩანაწერები</string>
|
||||
<string name="dev_report_device_features">მოწყობილობის მახასიათებლები</string>
|
||||
<string name="send_report">მოხსენების გაგზავნა</string>
|
||||
<string name="close">დახურვა</string>
|
||||
<string name="dev_report_sending">იგზავნება გამოხმაურება…</string>
|
||||
<string name="dev_report_sent">გამოხმაურება გაგზავნილია</string>
|
||||
<string name="dev_report_saved">მოხსენება შენახულია. გაიგზავნება მომდევნო ჯერზე Briar-ის ანგარიშზე შესვლისას.</string>
|
||||
<string name="dev_report_error">შეცდომა: მოხსენების გაგზავნა ვერ მოხერხდა</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">გამოდის Briar-იდან…</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">გამოვლენილია ეკრანის გადაფარვა</string>
|
||||
<string name="screen_filter_body">რომელიღაც აპმა ზემოდან გადაფარა Briar. თქვენი უსაფრთხოებისთვის Briar აღარ აღიქვამს შეხებას, როცა ზემოდან სხვა პროგრამაა გამოსახული.\n\nმოცემული აპებიდან ერთ-ერთი შეიძლება ფარავდეს:\n\n%1$s</string>
|
||||
<string name="screen_filter_body_api_30">რომელიღაც აპმა ზემოდან გადაფარა Briar. თქვენი უსაფრთხოებისთვის Briar აღარ აღიქვამს შეხებას, როცა ზემოდან სხვა პროგრამაა გამოსახული.\n\nგადახედეთ ქვემოთ მოცემულ აპებს, რომ იპოვოთ, რომელია ამის გამომწვევი.</string>
|
||||
<string name="screen_filter_allow">ამ აპებისთვის ზემოდან გადაფარვის ნების მიცემა</string>
|
||||
<string name="screen_filter_review_apps">აპების გადახედვა</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">კამერის ნებართვა</string>
|
||||
<string name="permission_camera_request_body">QR-კოდის წასაკითხად Briar საჭიროებს კამერასთან წვდომას.</string>
|
||||
<string name="permission_location_title">მდებარეობის ნებართვა</string>
|
||||
<string name="permission_location_request_body">Bluetooth-მოწყობილობების აღმოსაჩენად Briar საჭიროებს თქვენი მდებარეობის გამოყენების ნებართვას.\n\nBriar არ ინახავს და არავის გაუზიარებს თქვენს ადგილმდებარეობას.</string>
|
||||
<string name="permission_camera_location_title">კამერა და მდებარეობა</string>
|
||||
<string name="permission_camera_location_request_body">QR-კოდის წასაკითხად Briar საჭიროებს კამერასთან წვდომას.\n\nBluetooth-მოწყობილობების აღმოსაჩენად Briar საჭიროებს თქვენი მდებარეობის გამოყენების ნებართვას.\n\nBriar არ ინახავს და არავის გაუზიარებს თქვენს ადგილმდებარეობას.</string>
|
||||
<string name="permission_camera_denied_body">თქვენ უარყოფილი გაქვთ კამერასთან წვდომის ნებართვა, მაგრამ კონტაქტების დასამატებლად აუცილებელია კამერა.\n\nგთხოვთ გაითვალისწინოთ და დართოთ ნება.</string>
|
||||
<string name="permission_location_denied_body">თქვენ უარყოფილი გაქვთ მდებარეობის მონაცემებთან წვდომის ნებართვა, მაგრამ Briar-ს ესაჭიროება იგი Bluetooth-მოწყობილობების აღმოსაჩენად.\n\nგთხოვთ გაითვალისწინოთ და დართოთ ნება.</string>
|
||||
<string name="permission_location_setting_title">მდებარეობის პარამეტრები</string>
|
||||
<string name="permission_location_setting_body">თქვენი მოწყობილობის მდებარეობის პარამეტრები ჩართული უნდა იყოს სხვა მოწყობილობების Bluetooth-ით მოსანახად. გთხოვთ ჩართოთ და ისე განაგრძოთ. შემდგომ შეგეძლებათ გამორთვა.</string>
|
||||
<string name="permission_location_setting_button">მდებარეობის ჩართვა</string>
|
||||
<string name="qr_code">QR-კოდი</string>
|
||||
<string name="show_qr_code_fullscreen">QR-კოდის სრულ ეკრანზე ჩვენება</string>
|
||||
<!--App Locking-->
|
||||
<string name="lock_unlock">Briar-ის გახსნა</string>
|
||||
<string name="lock_unlock_verbose">შეიყვანეთ მოწყობილობის PIN-კოდი, მონახაზი ან პაროლი Briar-ის გასახსნელად</string>
|
||||
<string name="lock_unlock_fingerprint_description">შეეხეთ თითის ანაბეჭდის აღმქმელს დამახსოვრებული თითით, რომ განაგრძოთ</string>
|
||||
<string name="lock_unlock_password">გამოიყენეთ პაროლი</string>
|
||||
<string name="lock_is_locked">Briar ჩაკეტილია</string>
|
||||
<string name="lock_tap_to_unlock">შეეხეთ გასახსნელად</string>
|
||||
<!--Connections Screen-->
|
||||
<string name="transports_help_text">Briar-ს შეუძლია თქვენს კონტაქტებთან დაკავშირება ინტერნეტით, WiFi-თ ან Bluetooth-ით.\n\nინტერნეტით მიმოცვლილი ყველა მონაცემი Tor-ქსელს გაივლის პირადულობის უზრუნველსაყოფად.\n\nთუ ცალკეულ პირთან დაკავშირება რამდენიმე გზითაა შესაძლებელი, Briar ყველა მათგანით ერთდროულად ისარგებლებს.</string>
|
||||
<!--Share app offline-->
|
||||
<string name="hotspot_title">ამ აპის გაზიარება კავშირგარეშედ</string>
|
||||
<string name="hotspot_intro">გაუზიარეთ ეს პროგრამა თქვენს მახლობლად მყოფ ხალხს ინტერნეტკავშირის გარეშე თქვენი ტელეფონის WiFi-ს მეშვეობით.
|
||||
\n\nტელეფონი იმუშავებს WiFi-ს წვდომის წერტილად. ახლოს მყოფნი კი შეძლებენ მიუერთდნენ და გადაიწერონ Briar თქვენი ტელეფონიდან.</string>
|
||||
<string name="hotspot_button_start_sharing">წვდომის წერტილის ჩართვა</string>
|
||||
<string name="hotspot_button_stop_sharing">წვდომის წერტილის გათიშვა</string>
|
||||
<string name="hotspot_progress_text_start">მზადდება წვდომის წერტილი…</string>
|
||||
<string name="hotspot_notification_channel_title">WiFi-ს წვდომის წერტილი</string>
|
||||
<string name="hotspot_notification_title">Briar-ის ხაზგარეშედ გაზიარება</string>
|
||||
<string name="hotspot_button_connected">შემდეგ</string>
|
||||
<string name="permission_hotspot_location_request_body">WiFi-ს წვდომის წერტილის შესაქმნელად Briar საჭიროებს თქვენი მდებარეობის გამოყენების ნებართვას.\n\nBriar არ ინახავს და არავის გაუზიარებს თქვენს ადგილმდებარეობას.</string>
|
||||
<string name="permission_hotspot_location_denied_body">თქვენ უარყოფილი გაქვთ მდებარეობის მონაცემებთან წვდომის ნებართვა, მაგრამ Briar-ს ესაჭიროება იგი WiFi-ს წვდომის წერტილის გასამართად.\n\nგთხოვთ გაითვალისწინოთ და დართოთ ნება.</string>
|
||||
<string name="wifi_settings_title">WiFi-ს პარამეტრები</string>
|
||||
<string name="wifi_settings_request_enable_body">WiFi-ს წვდომის წერტილის შესაქმნელად, Briar-ს ესაჭიროება WiFi. გთხოვთ ჩართოთ.</string>
|
||||
<string name="hotspot_tab_manual">ხელით</string>
|
||||
<!--The placeholder to be inserted into the string 'hotspot_manual_wifi': People can connect by %s-->
|
||||
<string name="hotspot_scanning_a_qr_code">იკითხება QR-კოდი</string>
|
||||
<!--Wi-Fi setup-->
|
||||
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
|
||||
<string name="hotspot_manual_wifi">თქვენი ტელეფონი წარმოადგენს WiFi-ს წვდომის წერტილს. ვისაც სურს ჩამოტვირთოს Briar, შეუძლია დაუკავშირდეს ამ წვდომის წერტილს მისი დამატებით საკუთარი მოწყობილობის WiFi-ს პარამეტრებში, რისთვისაც გამოადგება ქვემოთ მოცემული მონაცემები ან %s. როცა დაუკავშირდება წვდომის წერტილს, დააჭირეთ „შემდეგს“.</string>
|
||||
<string name="hotspot_manual_wifi_ssid">ქსელის სახელი</string>
|
||||
<string name="hotspot_qr_wifi">თქვენი ტელეფონი წარმოადგენს WiFi-ს წვდომის წერტილს. ვისაც სურს ჩამოტვირთოს Briar, შეუძლია დაუკავშირდეს ამ წვდომის წერტილს QR-კოდის წაკითხვით. როცა დაუკავშირდება წვდომის წერტილს, დააჭირეთ „შემდეგს“.</string>
|
||||
<string name="hotspot_no_peers_connected">არაა დაკავშირებული მოწყობილობები</string>
|
||||
<plurals name="hotspot_peers_connected">
|
||||
<item quantity="one">%s მოწყობილობაა დაკავშირებული</item>
|
||||
<item quantity="other">%s მოწყობილობაა დაკავშირებული</item>
|
||||
</plurals>
|
||||
<!--Download link-->
|
||||
<!--The %s placeholder will be replaced with the translation of 'hotspot_scanning_a_qr_code'-->
|
||||
<string name="hotspot_manual_site">თქვენი ტელეფონი წარმოადგენს WiFi-ს წვდომის წერტილს. ვინც დაკავშირებულია ამ წვდომის წერტილთან, შეუძლია ჩამოტვირთოს Briar ბრაუზერში მოცემული ბმულის აკრეფით ან %s.</string>
|
||||
<string name="hotspot_manual_site_address">მისამართი (URL)</string>
|
||||
<string name="hotspot_qr_site">თქვენი ტელეფონი წარმოადგენს WiFi-ს წვდომის წერტილს. ვინც დაკავშირებულია ამ წვდომის წერტილთან, შეუძლია ჩამოტვირთოს Briar ბრაუზერში QR-კოდის გამოყენებით.</string>
|
||||
<!--e.g. Download Briar 1.2.20-->
|
||||
<string name="website_download_title_1">ჩამოტვირთეთ Briar %s</string>
|
||||
<string name="website_download_intro_1">ახლომახლო ვიღაცამ გაგიზიარათ Briar.</string>
|
||||
<string name="website_download_button">ჩამოტვირთეთ Briar</string>
|
||||
<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">ხარვეზები WiFi-სთან დაკავშირებისას:</string>
|
||||
<string name="hotspot_help_wifi_1">გათიშეთ და ახლიდან ჩართეთ WiFi ორივე ტელეფონზე, შემდეგ კვლავ სცადეთ.</string>
|
||||
<string name="hotspot_help_wifi_2">თუ ტელეფონი გიწერთ, რომ WiFi-ს არ გააჩნია ინტერნეტწვდომა, მიუთითეთ, რომ მაინც გსურთ კავშირზე დარჩენა.</string>
|
||||
<string name="hotspot_help_wifi_3">ახლიდან ჩართეთ ტელეფონი, რომელზეც გაშვებულია WiFi-ს წვდომის წერტილი, შემდეგ კი გახსენით Briar და კვლავ სცადეთ გაზიარება.</string>
|
||||
<string name="hotspot_help_site_title">ხარვეზები ადგილობრივ საიტზე შესვლისას:</string>
|
||||
<string name="hotspot_help_site_1">კარგად გადაამოწმეთ, სწორად შეიყვანეთ თუ არა ნაჩვენები მისამართი. მცირე შეცდომაც საკმარისია, რომ არ იმუშაოს.</string>
|
||||
<string name="hotspot_help_site_2">დარწმუნდით, რომ თქვენი ტელეფონი კვლავ დაკავშირებულია სათანადო WiFi-ქსელთან (იხილეთ ზემოთ), როცა ცდილობთ საიტთან წვდომას.</string>
|
||||
<string name="hotspot_help_site_3">თუ გიყენიათ ქსელის ფარი, შეამოწმეთ, ხომ არ ზღუდავს წვდომას.</string>
|
||||
<string name="hotspot_help_site_4">თუ ახერხებთ საიტზე შესვლას, მაგრამ ვერ ჩამოტვირთეთ Briar, სცადეთ სხვა ბრაუზერი.</string>
|
||||
<string name="hotspot_help_fallback_title">ვერაფერმა უშველა?</string>
|
||||
<string name="hotspot_help_fallback_intro">შეგიძლიათ შეინახოთ .apk ფაილის სახითაც რამე განსხვავებული გზით გაზიარებისთვის. სხვა მოწყობილობაზე მოხვედრისას კი მისი მეშვეობით დაყენდება Briar.
|
||||
\n\nრჩევა: Bluetooth-ით გაზიარებამდე, სავარაუდოდ, საჭირო იქნება დაბოლოების შეცვლა .zip-ით.</string>
|
||||
<string name="hotspot_help_fallback_button">აპის შენახვა</string>
|
||||
<!--error handling-->
|
||||
<string name="hotspot_error_intro">რაღაც ხარვეზი წარმოიშვა აპის WiFi-თ გაზიარებისას:</string>
|
||||
<string name="hotspot_error_no_wifi_direct">მოწყობილობაზე არაა მხარდაჭერილი WiFi Direct</string>
|
||||
<string name="hotspot_error_start_callback_failed">წვდომის წერტილი ვერ გაეშვა: შეცდომა %s</string>
|
||||
<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თუ სხვა წვდომის წერტილი გაქვთ გაშვებული ან ინტერნეტს აზიარებთ WiFi-თ, შეწყვიტეთ და შემდგომ სცადეთ ხელახლა.</string>
|
||||
<!--Transfer Data via Removable Drives-->
|
||||
<string name="removable_drive_menu_title">დაკავშირება მოსახსნელი მეხსიერებით</string>
|
||||
<string name="removable_drive_intro">თუ ვერ უკავშირდებთ თქვენს კონტაქტს ინტერნეტით, WiFi-თ ან Bluetooth-ით, Briar-ს აგრეთვე შეუძლია წერილების გადატანა მოსახსნელ მოწყობილობებზე, როგორიცაა USB-მეხსიერება ან SD-ბარათი.</string>
|
||||
<string name="removable_drive_explanation">თუ ვერ უკავშირდებით თქვენს კონტაქტებს ინტერნეტით, WiFi-თ ან Bluetooth-ით, Briar-ს შეუძლია წერილების გადატანა მოსახსნელ საცავზე, როგორიცაა USB-მეხსიერება ან SD-ბარათი.\n\nროცა დააჭერთ „მონაცემთა გაგზავნის“ ღილაკს, ყველა გასაგზავნი მასალა ჩაიწერება ამ მოსახსნელ მეხსიერებაზე. მათ შორის შეიძლება იყოს პირადი წერილები, დანართები, ბლოგის ჩანაწერები და დახურული ჯგუფები.\n\nყველაფერი წინასწარ დაიშიფრება ამ მეხსიერებაზე ჩაწერამდე.\n\nთქვენს კონტაქტს როცა მიაწვდით მითითებულ მოსახსნელ საცავს, „მონაცემთა მიღების“ ღილაკით შეეძლება გადაიტანოს წერილები თავის Briar-პროგრამაში.</string>
|
||||
<string name="removable_drive_title_send">მონაცემთა გაგზავნა</string>
|
||||
<string name="removable_drive_title_receive">მონაცემთა მიღება</string>
|
||||
<string name="removable_drive_send_intro">შეეხეთ ღილაკს ქვემოთ დაშიფრული წერილების შემცველი ახალი ფაილის შესაქმნელად. შეგიძლიათ მიუთითოთ, სად შეინახოს.\n\nთუ გსურთ ფაილის შენახვა მოსახსნელ მეხსიერებაზე, შეუერთეთ ახლავე.</string>
|
||||
<string name="removable_drive_send_no_data">ამ კონტაქტთან გასაგზავნი წერილები ამჟამად არაა.</string>
|
||||
<string name="removable_drive_send_not_supported">ამ კონტაქტს მოძველებული ვერსიის Briar ან ძველი მოწყობილობა აქვს, რომელზეც მხარდაუჭერელია ეს შესაძლებლობა.</string>
|
||||
<string name="removable_drive_send_button">ფაილის არჩევა გადასატანად</string>
|
||||
<string name="removable_drive_ongoing">გთხოვთ მოითმინოთ, სანამ მიმდინარე მოქმედება შესრულდება</string>
|
||||
<string name="removable_drive_receive_intro">შეეხეთ ღილაკს ქვემოთ იმ ფაილის ასარჩევად, რომელიც საკონტაქტო პირმა გამოგიგზავნათ.\n\nთუ ფაილი მოსახსნელ მეხსიერებაზეა, შეუერთეთ ახლავე.</string>
|
||||
<string name="removable_drive_receive_button">ფაილის არჩევა გადმოსატანად</string>
|
||||
<string name="removable_drive_success_send_title">გადატანილია წარმატებით</string>
|
||||
<string name="removable_drive_success_send_text">მონაცემები გადატანილია წარმატებით. 28 დღე გაქვთ ფაილის კონტაქტისთვის მისაწოდებლად.\n\nთუ ფაილი მოსახსნელ მეხსიერებაზეა, გამოიყენეთ მდგომარეობის ზოლზე მოცემული შეტყობინება მის გასათიშად გამოერთებამდე.</string>
|
||||
<string name="removable_drive_success_receive_title">გადმოტანილია წარმატებით</string>
|
||||
<string name="removable_drive_success_receive_text">ყველა დაშიფრული წერილი ამ ფაილიდან მიღებულია.</string>
|
||||
<string name="removable_drive_error_send_title">შეცდომა მონაცემთა გადატანისას</string>
|
||||
<string name="removable_drive_error_send_text">შეცდომა წარმოიშვა ფაილში მონაცემთა ჩაწერისას.\n\nთუ მოსახსნელ მეხსიერებას იყენებთ, შეამოწმეთ, ხომ კარგადაა შეერთებული და კვლავ სცადეთ.\n\nთუ არ გამოსწორდება, გთხოვთ გამოგვეხმაუროთ და აცნობოთ Briar-ის გუნდს ამ ხარვეზის შესახებ.</string>
|
||||
<string name="removable_drive_error_receive_title">შეცდომა მონაცემთა გადმოტანისას</string>
|
||||
<string name="removable_drive_error_receive_text">შერჩეული ფაილი არაფერს შეიცავს, რის ამოცნობასაც Briar შეძლებდა.\n\nშეამოწმეთ, ხომ ნამდვილად სწორი ფაილია არჩეული.\n\nთუ 28 დღეზე მეტია გასული, რაც გამომგზავნმა პირმა შექმნა ეგ ფაილი, მაშინ Briar ვერ მოახერხებს მის ამოცნობას.</string>
|
||||
<!--Screenshots-->
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_alice">ანა</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_bob">ბექა</string>
|
||||
<!--This is a name to be used in screenshots. Feel free to change it to a local name.-->
|
||||
<string name="screenshot_carol">სალომე</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Bob!-->
|
||||
<string name="screenshot_message_1">ჰეი, ბექა!</string>
|
||||
<!--This is a message to be used in screenshots. Please use the same translation for Alice!-->
|
||||
<string name="screenshot_message_2">ჰეი, ანა! გმადლობ, რომ მაცნობე Briar-ის შესახებ!</string>
|
||||
<!--This is a message to be used in screenshots.-->
|
||||
<string name="screenshot_message_3">არაფრის, იმედია მოგეწონება 😀</string>
|
||||
</resources>
|
||||
@@ -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>
|
||||
@@ -615,14 +616,31 @@
|
||||
<string name="tor_offline_button_check">Tikrinti ryšio nustatymus</string>
|
||||
<string name="mailbox_status_title">Pašto dėžutės būsena</string>
|
||||
<string name="mailbox_status_connected_title">Pašto dėžutė veikia</string>
|
||||
<string name="mailbox_status_failure_title">Pašto dėžutė yra neprieinama</string>
|
||||
<string name="mailbox_status_app_too_old_title">Briar yra per sena</string>
|
||||
<string name="mailbox_status_app_too_old_message">Atnaujinkite Briar programėlę iki naujausios versijos ir bandykite dar kartą.</string>
|
||||
<string name="mailbox_status_mailbox_too_old_title">Pašto dėžutė yra per sena</string>
|
||||
<string name="mailbox_status_check_button">Tikrinti ryšį</string>
|
||||
<!--Example for string substitution: Last connection: 3min ago-->
|
||||
<string name="mailbox_status_connected_info">Paskutinis prisijungimas: %s</string>
|
||||
<!--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>
|
||||
<string name="mailbox_status_unlink_dialog_title">Atsieti pašto dėžutę?</string>
|
||||
<string name="mailbox_status_unlink_dialog_question">Ar tikrai norite atsieti pašto dėžutę?</string>
|
||||
<string name="mailbox_status_unlink_no_wipe_title">Jūsų pašto dėžutė atsieta</string>
|
||||
<string name="mailbox_status_unlink_success">Jūsų pašto dėžutė atsieta</string>
|
||||
<string name="mailbox_error_wizard_title">Pašto dėžutės nesklandumų šalinimo vediklis</string>
|
||||
<!--About-->
|
||||
<string name="about_title">Apie</string>
|
||||
<string name="briar_version">Briar versija: %s</string>
|
||||
<string name="tor_version">Tor versija: %s</string>
|
||||
<string name="links">Nuorodos</string>
|
||||
<string name="briar_website">\u2022 <a href="">Internetinė svetainė</a></string>
|
||||
<string name="briar_source_code">\u2022 <a href="">Pradinis kodas</a></string>
|
||||
<string name="briar_changelog">\u2022 <a href="">Keitinių žurnalas</a></string>
|
||||
<string name="briar_privacy_policy">\u2022 <a href="">Privatumo politika</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")-->
|
||||
<!--Conversation Settings-->
|
||||
<string name="disappearing_messages_title">Išnykstančios žinutės</string>
|
||||
<string name="disappearing_messages_explanation_long">Įjungus šį nustatymą, naujos žinutės
|
||||
@@ -654,6 +672,7 @@
|
||||
<string name="describe_crash">Aprašykite kas nutiko (nebūtina)</string>
|
||||
<string name="enter_feedback">Įveskite savo atsiliepimą</string>
|
||||
<string name="optional_contact_email">Jūsų el. pašto adresas (nebūtina)</string>
|
||||
<string name="privacy_policy">Siųsdami mums savo duomenis, sutinkate su mūsų <a href="">privatumo politika</a></string>
|
||||
<string name="include_debug_report_crash">Įtraukti anoniminius duomenis apie strigtį</string>
|
||||
<string name="include_debug_report_feedback">Įtraukti anoniminius duomenis apie šį įrenginį</string>
|
||||
<string name="dev_report_user_info">Naudotojo informacija</string>
|
||||
@@ -740,6 +759,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">၎င်းအပြင်အဆင်ကို ဖွင့်ထားလျှင်
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user