Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
3c290a320e Remove local author from contacts. 2019-04-23 15:50:04 +01:00
216 changed files with 2051 additions and 4877 deletions

View File

@@ -39,6 +39,31 @@
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>
<Objective-C-extensions>
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" fileNamingConvention="NONE" />
<pair source="c" header="h" fileNamingConvention="NONE" />
</extensions>
</Objective-C-extensions>
<XML> <XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" /> <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML> </XML>

View File

@@ -33,7 +33,7 @@ dependencies {
tor 'org.briarproject:tor-android:0.3.5.8@zip' tor 'org.briarproject:tor-android:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.9@zip' tor 'org.briarproject:obfs4proxy-android:0.0.9@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'

View File

@@ -1,45 +1,45 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.4.0:protos-26.4.0.jar:ad760915586797d39319f402837b378bff3bb4ed583e3e0c48c965631fb2135f', 'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0',
'com.android.tools.analytics-library:shared:26.4.0:shared-26.4.0.jar:1332106a905d48909c81268c9e414946de3e83487db394c6073b0a9b5c3d0ed2', 'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71',
'com.android.tools.analytics-library:tracker:26.4.0:tracker-26.4.0.jar:d0020cfbfd4cd75935f2972d6a24089840d4a10df6f3ef2a796093217dd37796', 'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad',
'com.android.tools.build:apksig:3.4.0:apksig-3.4.0.jar:91d5a1866139c69756280355a6f61b4d619d0516841580114f45a10f2177327e', 'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd',
'com.android.tools.build:apkzlib:3.4.0:apkzlib-3.4.0.jar:8653c85f5fdf1dde840e8b8af7396aeb79c34b66e541b5860059616006535592', 'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac',
'com.android.tools.build:builder-model:3.4.0:builder-model-3.4.0.jar:a88f138124a9f016a70bcb4760359a502f65c7deed56507ee4014f4dd9ea853b', 'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46',
'com.android.tools.build:builder-test-api:3.4.0:builder-test-api-3.4.0.jar:31089ab1ec19ca7687a010867d2f3807513c805b8226979706f4247b5d4df26f', 'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618',
'com.android.tools.build:builder:3.4.0:builder-3.4.0.jar:476221b5203a7f50089bf185ed95000a34b6f5020ef0a17815afd58606922679', 'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5',
'com.android.tools.build:gradle-api:3.4.0:gradle-api-3.4.0.jar:215eca38f6719213c2f492b4d622cdd11676c66c9871f8a2aed0c66d00175628', 'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040',
'com.android.tools.build:manifest-merger:26.4.0:manifest-merger-26.4.0.jar:29e45e690dedd165035e97c21c2ca94d0bd4ec16b6b210daa26669a582b6f220', 'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33',
'com.android.tools.ddms:ddmlib:26.4.0:ddmlib-26.4.0.jar:93f56fe4630c3166adbd6c51d7bb602d96abb91b07ba5b1165fdcd071e88c940', 'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85',
'com.android.tools.external.com-intellij:intellij-core:26.4.0:intellij-core-26.4.0.jar:30cb0e879d4424de9677a50b537fb628636b4a50f5470af5e52437980c41421f', 'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3',
'com.android.tools.external.com-intellij:kotlin-compiler:26.4.0:kotlin-compiler-26.4.0.jar:dd1fe225c31a0e012dc025336363a5b783e2c5c20ffb69e77f8f57e89420d998', 'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe',
'com.android.tools.external.org-jetbrains:uast:26.4.0:uast-26.4.0.jar:f25f3285b775a983327583ff6584dea54e447813ef69e0ce08b05a45b5f4aab0', 'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317',
'com.android.tools.layoutlib:layoutlib-api:26.4.0:layoutlib-api-26.4.0.jar:52128f5cf293b224072be361919bfd416e59480ab7264ddcdbbf046b0d7a12e3', 'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8',
'com.android.tools.lint:lint-api:26.4.0:lint-api-26.4.0.jar:fdb8fca8ae4c254f438338d03d72605e00ed106f2d5550405af41ca1c8509401', 'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033',
'com.android.tools.lint:lint-checks:26.4.0:lint-checks-26.4.0.jar:4ff52d40488cd3e22b9c6b2eb67784e0c3269d0b42ef9d17689cd75a7b2bceb4', 'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517',
'com.android.tools.lint:lint-gradle-api:26.4.0:lint-gradle-api-26.4.0.jar:714b7a85c7d2aa10daeab16e969fe7530c659d0728a7f24021da456870418d0f', 'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f',
'com.android.tools.lint:lint-gradle:26.4.0:lint-gradle-26.4.0.jar:b8c130d273f522388734457e1b96790f41528fcec6fda9e8eaa4e4d95a07cfbb', 'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013',
'com.android.tools.lint:lint:26.4.0:lint-26.4.0.jar:83aa062fb0405b60ed358d858c8c2955e1bae44a455b498068c6a60988755f00', 'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e',
'com.android.tools:annotations:26.4.0:annotations-26.4.0.jar:a7955b8e19c3a2a861d6faa43a58b7c0d46ea9112188ee3e235c6f9f439ecc1a', 'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1',
'com.android.tools:common:26.4.0:common-26.4.0.jar:ea40b94b3c1284ea7700f011388e2906a8363a66abd902891722b3c557984852', 'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac',
'com.android.tools:dvlib:26.4.0:dvlib-26.4.0.jar:23af89c535b01ba36ceed1b6b309b672814eba624e643cd7dedf0519edad50cc', 'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811',
'com.android.tools:repository:26.4.0:repository-26.4.0.jar:3d1763ab46199374dc6d94129bba11c70f1d5857e2c81a3ac4898abca40b176b', 'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40',
'com.android.tools:sdk-common:26.4.0:sdk-common-26.4.0.jar:78a522525b30ffc6b7bf1299c831d24ce385f68a9f4878f8f752e9baefa31b0f', 'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c',
'com.android.tools:sdklib:26.4.0:sdklib-26.4.0.jar:b854c23892013a326d761cf071c72cf3e038ed0469d10f4a356829fa56e4c132', 'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825', 'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3', 'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35', 'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d', 'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c', 'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439', 'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439',
@@ -48,7 +48,7 @@ dependencyVerification {
'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce', 'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce',
'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4', 'com.sun.istack:istack-commons-runtime:2.21:istack-commons-runtime-2.21.jar:c33e67a0807095f02a0e2da139412dd7c4f9cc1a4c054b3e434f96831ba950f4',
'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038', 'com.sun.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
'commons-codec:commons-codec:1.10:commons-codec-1.10.jar:4241dfa94e711d435f29a4604a3e2de5c4aa3c165e23bd066be6fc1fc4309569', 'commons-codec:commons-codec:1.9:commons-codec-1.9.jar:ad19d2601c3abf0b946b5c3a4113e226a8c1e3305e395b90013b78dd94a723ce',
'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636', 'commons-logging:commons-logging:1.2:commons-logging-1.2.jar:daddea1ea0be0f56978ab3006b8ac92834afeefbd9b7e4e6316fca57df0fa636',
'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c', 'it.unimi.dsi:fastutil:7.2.0:fastutil-7.2.0.jar:74fa208043740642f7e6eb09faba15965218ad2f50ce3020efb100136e4b591c',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
@@ -60,9 +60,9 @@ dependencyVerification {
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6', 'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7', 'org.apache.httpcomponents:httpclient:4.5.2:httpclient-4.5.2.jar:0dffc621400d6c632f55787d996b8aeca36b30746a716e079a985f24d8074057',
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd', 'org.apache.httpcomponents:httpcore:4.4.5:httpcore-4.4.5.jar:64d5453874cab7e40a7065cb01a9a9ca1053845a9786b478878b679e0580cec3',
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e', 'org.apache.httpcomponents:httpmime:4.5.2:httpmime-4.5.2.jar:231a3f7e4962053db2be8461d5422e68fc458a3a7dd7d8ada803a348e21f8f07',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', '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.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',

View File

@@ -7,7 +7,7 @@ apply plugin: 'witness'
apply from: 'witness.gradle' apply from: 'witness.gradle'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.22.1" implementation "com.google.dagger:dagger:2.19"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.api;
/**
* Thrown when data being parsed uses a protocol or format version that is not
* supported.
*/
public class UnsupportedVersionException extends FormatException {
private final boolean tooOld;
public UnsupportedVersionException(boolean tooOld) {
this.tooOld = tooOld;
}
public boolean isTooOld() {
return tooOld;
}
}

View File

@@ -33,8 +33,7 @@ public abstract class BdfIncomingMessageHook implements IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* *
* @param txn A read-write transaction * @return whether or not this message should be shared
* @return Whether or not this message should be shared
* @throws DbException Should only be used for real database errors. * @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup, * If this is thrown, delivery will be attempted again at next startup,
* whereas if a FormatException is thrown, the message will be permanently * whereas if a FormatException is thrown, the message will be permanently

View File

@@ -18,11 +18,10 @@ public interface ContactGroupFactory {
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact); Contact contact, AuthorId local);
/** /**
* Creates a group for the given client to share between the given authors * Creates a group for the given client to share between the given authors.
* identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId1, AuthorId authorId2);

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -17,16 +16,14 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final Author author; private final Author author;
private final AuthorId localAuthorId;
@Nullable @Nullable
private final String alias; private final String alias;
@Nullable @Nullable
private final byte[] handshakePublicKey; private final byte[] handshakePublicKey;
private final boolean verified; private final boolean verified;
public Contact(ContactId id, Author author, AuthorId localAuthorId, public Contact(ContactId id, Author author, @Nullable String alias,
@Nullable String alias, @Nullable byte[] handshakePublicKey, @Nullable byte[] handshakePublicKey, boolean verified) {
boolean verified) {
if (alias != null) { if (alias != null) {
int aliasLength = toUtf8(alias).length; int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH) if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
@@ -38,7 +35,6 @@ public class Contact {
} }
this.id = id; this.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId;
this.alias = alias; this.alias = alias;
this.handshakePublicKey = handshakePublicKey; this.handshakePublicKey = handshakePublicKey;
this.verified = verified; this.verified = verified;
@@ -52,10 +48,6 @@ public class Contact {
return author; return author;
} }
public AuthorId getLocalAuthorId() {
return localAuthorId;
}
@Nullable @Nullable
public String getAlias() { public String getAlias() {
return alias; return alias;

View File

@@ -1,10 +1,7 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -27,63 +24,60 @@ public interface ContactManager {
void registerContactHook(ContactHook hook); void registerContactHook(ContactHook hook);
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym, derives and stores transport
* derives and stores transport keys for each transport, and returns an ID * keys for each transport, and returns an ID for the contact.
* for the contact.
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author remote, AuthorId local, SecretKey rootKey,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException; throws DbException;
/** /**
* Returns the handshake link that needs to be sent to a contact we want * Stores a contact with the given pseudonym and returns an ID for the
* to add. * contact.
*/ */
String getHandshakeLink() throws DbException; ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException;
/** /**
* Creates a {@link PendingContact} from the given handshake link and * Stores a contact with the given pseudonym, derives and stores transport
* alias, adds it to the database and returns it. * keys for each transport, and returns an ID for the contact.
* *
* @param link The handshake link received from the contact we want to add * @param alice true if the local party is Alice
* @param alias The alias the user has given this contact
* @return A PendingContact representing the contact to be added
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
*/ */
PendingContact addPendingContact(String link, String alias) ContactId addContact(Author a, SecretKey rootKey, long timestamp,
throws DbException, FormatException; boolean alice, boolean verified, boolean active) throws DbException;
/**
* Returns the static link that needs to be sent to the contact to be added.
*/
String getRemoteContactLink();
/**
* Returns true if the given link is syntactically valid.
*/
boolean isValidRemoteContactLink(String link);
/**
* Requests a new contact to be added via the given {@code link}.
*
* @param link The link received from the contact we want to add.
* @param alias The alias the user has given this contact.
* @return A PendingContact representing the contact to be added.
*/
PendingContact addRemoteContactRequest(String link, String alias);
/** /**
* Returns a list of {@link PendingContact}s. * Returns a list of {@link PendingContact}s.
*/ */
Collection<PendingContact> getPendingContacts() throws DbException; Collection<PendingContact> getPendingContacts();
/** /**
* Removes a {@link PendingContact}. * Removes a {@link PendingContact} that is in state
* {@link PendingContactState FAILED}.
*/ */
void removePendingContact(PendingContactId p) throws DbException; void removePendingContact(PendingContact pendingContact);
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
@@ -91,22 +85,14 @@ public interface ContactManager {
Contact getContact(ContactId c) throws DbException; Contact getContact(ContactId c) throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given ID.
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws NoSuchContactException If the contact is not in the database
*/ */
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId) Contact getContact(AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns the contact with the given remoteAuthorId * Returns the contact with the given ID.
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws NoSuchContactException If the contact is not in the database
*/ */
Contact getContact(Transaction txn, AuthorId remoteAuthorId, Contact getContact(Transaction txn, AuthorId a) throws DbException;
AuthorId localAuthorId) throws DbException;
/** /**
* Returns all active contacts. * Returns all active contacts.
@@ -136,16 +122,14 @@ public interface ContactManager {
throws DbException; throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this pseudonym already exists.
*/ */
boolean contactExists(Transaction txn, AuthorId remoteAuthorId, boolean contactExists(Transaction txn, AuthorId a) throws DbException;
AuthorId localAuthorId) throws DbException;
/** /**
* Return true if a contact with this name and public key already exists * Returns true if a contact with this pseudonym already exists.
*/ */
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId) boolean contactExists(AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns the {@link AuthorInfo} for the given author. * Returns the {@link AuthorInfo} for the given author.
@@ -159,20 +143,8 @@ public interface ContactManager {
interface ContactHook { interface ContactHook {
/**
* Called when a contact is being added.
*
* @param txn A read-write transaction
* @param c The contact that is being added
*/
void addingContact(Transaction txn, Contact c) throws DbException; void addingContact(Transaction txn, Contact c) throws DbException;
/**
* Called when a contact is being removed
*
* @param txn A read-write transaction
* @param c The contact that is being removed
*/
void removingContact(Transaction txn, Contact c) throws DbException; void removingContact(Transaction txn, Contact c) throws DbException;
} }
} }

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.api.contact;
import java.util.regex.Pattern;
public interface HandshakeLinkConstants {
/**
* The current version of the handshake link format.
*/
int FORMAT_VERSION = 0;
/**
* The length of a base32-encoded handshake link in bytes, excluding the
* 'briar://' prefix.
*/
int BASE32_LINK_BYTES = 53;
/**
* The length of a raw handshake link in bytes, before base32 encoding.
*/
int RAW_LINK_BYTES = 33;
/**
* Regular expression for matching handshake links, including or excluding
* the 'briar://' prefix.
*/
Pattern LINK_REGEX =
Pattern.compile("(briar://)?([a-z2-7]{" + BASE32_LINK_BYTES + "})");
/**
* Label for hashing handshake public keys to calculate their identifiers.
*/
String ID_LABEL = "org.briarproject.bramble/HANDSHAKE_KEY_ID";
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a pending contact is removed.
*/
@Immutable
@NotNullByDefault
public class PendingContactRemovedEvent extends Event {
private final PendingContactId id;
public PendingContactRemovedEvent(PendingContactId id) {
this.id = id;
}
public PendingContactId getId() {
return id;
}
}

View File

@@ -7,7 +7,7 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -102,11 +102,11 @@ public interface DatabaseComponent {
NullableDbCallable<R, E> task) throws DbException, E; NullableDbCallable<R, E> task) throws DbException, E;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym and returns an ID for the
* and returns an ID for the contact. * contact.
*/ */
ContactId addContact(Transaction txn, Author remote, AuthorId local, ContactId addContact(Transaction txn, Author a, boolean verified)
boolean verified) throws DbException; throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -128,9 +128,9 @@ public interface DatabaseComponent {
HandshakeKeys k) throws DbException; HandshakeKeys k) throws DbException;
/** /**
* Stores an identity. * Stores a local pseudonym.
*/ */
void addIdentity(Transaction txn, Identity i) throws DbException; void addLocalAuthor(Transaction txn, LocalAuthor a) throws DbException;
/** /**
* Stores a local message. * Stores a local message.
@@ -158,13 +158,11 @@ public interface DatabaseComponent {
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact.
* local pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local) boolean containsContact(Transaction txn, AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns true if the database contains the given group. * Returns true if the database contains the given group.
@@ -174,12 +172,12 @@ public interface DatabaseComponent {
boolean containsGroup(Transaction txn, GroupId g) throws DbException; boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains an identity for the given * Returns true if the database contains the given local author.
* pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsIdentity(Transaction txn, AuthorId a) throws DbException; boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException;
/** /**
* Returns true if the database contains the given pending contact. * Returns true if the database contains the given pending contact.
@@ -254,6 +252,13 @@ public interface DatabaseComponent {
*/ */
Contact getContact(Transaction txn, ContactId c) throws DbException; Contact getContact(Transaction txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -261,22 +266,6 @@ public interface DatabaseComponent {
*/ */
Collection<Contact> getContacts(Transaction txn) throws DbException; Collection<Contact> getContacts(Transaction txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(Transaction txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -317,18 +306,18 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Returns the identity for the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Identity getIdentity(Transaction txn, AuthorId a) throws DbException; LocalAuthor getLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns the identities for all local pseudonyms. * Returns all local pseudonyms.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<Identity> getIdentities(Transaction txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/** /**
* Returns the message with the given ID. * Returns the message with the given ID.
@@ -559,9 +548,9 @@ public interface DatabaseComponent {
HandshakeKeySetId k) throws DbException; HandshakeKeySetId k) throws DbException;
/** /**
* Removes an identity (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
void removeIdentity(Transaction txn, AuthorId a) throws DbException; void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/** /**
* Removes a message (and all associated state) from the database. * Removes a message (and all associated state) from the database.
@@ -619,12 +608,6 @@ public interface DatabaseComponent {
void addMessageDependencies(Transaction txn, Message dependent, void addMessageDependencies(Transaction txn, Message dependent,
Collection<MessageId> dependencies) throws DbException; Collection<MessageId> dependencies) throws DbException;
/**
* Sets the handshake key pair for the identity with the given ID.
*/
void setHandshakeKeyPair(Transaction txn, AuthorId local, byte[] publicKey,
byte[] privateKey) throws DbException;
/** /**
* Sets the reordering window for the given transport key set in the given * Sets the reordering window for the given transport key set in the given
* time period. * time period.

View File

@@ -1,9 +1,9 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
/** /**
* Thrown when a database operation is attempted for an identity that is not in * Thrown when a database operation is attempted for a pseudonym that is not in
* the database. This exception may occur due to concurrent updates and does * the database. This exception may occur due to concurrent updates and does
* not indicate a database error. * not indicate a database error.
*/ */
public class NoSuchIdentityException extends DbException { public class NoSuchLocalAuthorException extends DbException {
} }

View File

@@ -18,7 +18,14 @@ public interface AuthorFactory {
/** /**
* Creates a local author with the current format version and the given * Creates a local author with the current format version and the given
* name. * name and keys.
*/ */
LocalAuthor createLocalAuthor(String name); LocalAuthor createLocalAuthor(String name, byte[] publicKey,
byte[] privateKey);
/**
* Creates a local author with the given format version, name and keys.
*/
LocalAuthor createLocalAuthor(int formatVersion, String name,
byte[] publicKey, byte[] privateKey);
} }

View File

@@ -1,96 +0,0 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Arrays;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
@Immutable
@NotNullByDefault
public class Identity {
private final LocalAuthor localAuthor;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created;
public Identity(LocalAuthor localAuthor,
@Nullable byte[] handshakePublicKey,
@Nullable byte[] handshakePrivateKey, long created) {
if (handshakePublicKey != null) {
int keyLength = handshakePublicKey.length;
if (keyLength == 0 || keyLength > MAX_AGREEMENT_PUBLIC_KEY_BYTES)
throw new IllegalArgumentException();
}
this.localAuthor = localAuthor;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
}
/**
* Returns the ID of the user's pseudonym.
*/
public AuthorId getId() {
return localAuthor.getId();
}
/**
* Returns the user's pseudonym.
*/
public LocalAuthor getLocalAuthor() {
return localAuthor;
}
/**
* Returns true if the identity has a handshake key pair.
*/
public boolean hasHandshakeKeyPair() {
return handshakePublicKey != null && handshakePrivateKey != null;
}
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/**
* Returns the time the identity was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
@Override
public int hashCode() {
return localAuthor.getId().hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof Identity) {
Identity i = (Identity) o;
return created == i.created &&
localAuthor.equals(i.localAuthor) &&
Arrays.equals(handshakePublicKey, i.handshakePublicKey) &&
Arrays.equals(handshakePrivateKey, i.handshakePrivateKey);
}
return false;
}
}

View File

@@ -1,29 +1,30 @@
package org.briarproject.bramble.api.identity; package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface IdentityManager { public interface IdentityManager {
/** /**
* Creates an identity with the given name. The identity includes a * Creates a local identity with the given name.
* handshake key pair.
*/ */
@CryptoExecutor @CryptoExecutor
Identity createIdentity(String name); LocalAuthor createLocalAuthor(String name);
/** /**
* Registers the given identity with the manager. This method should be * Registers the given local identity with the manager. The identity is
* called before {@link LifecycleManager#startServices(SecretKey)}. The * not stored until {@link #storeLocalAuthor()} is called.
* identity is stored when {@link LifecycleManager#startServices(SecretKey)}
* is called. The identity must include a handshake key pair.
*/ */
void registerIdentity(Identity i); void registerLocalAuthor(LocalAuthor a);
/**
* Stores the local identity registered with
* {@link #registerLocalAuthor(LocalAuthor)}, if any.
*/
void storeLocalAuthor() throws DbException;
/** /**
* Returns the cached local identity or loads it from the database. * Returns the cached local identity or loads it from the database.
@@ -32,18 +33,7 @@ public interface IdentityManager {
/** /**
* Returns the cached local identity or loads it from the database. * Returns the cached local identity or loads it from the database.
* <p/>
* Read-only.
*/ */
LocalAuthor getLocalAuthor(Transaction txn) throws DbException; LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/**
* Returns the cached handshake keys or loads them from the database.
* <p/>
* Read-only.
*
* @return A two-element array containing the public key in the first
* element and the private key in the second
*/
byte[][] getHandshakeKeys(Transaction txn) throws DbException;
} }

View File

@@ -2,8 +2,11 @@ package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/** /**
* A pseudonym for the local user. * A pseudonym for the local user.
*/ */
@@ -12,11 +15,31 @@ import javax.annotation.concurrent.Immutable;
public class LocalAuthor extends Author { public class LocalAuthor extends Author {
private final byte[] privateKey; private final byte[] privateKey;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey) { byte[] publicKey, byte[] privateKey, long created) {
super(id, formatVersion, name, publicKey); super(id, formatVersion, name, publicKey);
this.privateKey = privateKey; this.privateKey = privateKey;
this.created = created;
handshakePublicKey = null;
handshakePrivateKey = null;
}
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, byte[] handshakePublicKey,
byte[] handshakePrivateKey, long created) {
super(id, formatVersion, name, publicKey);
if (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH) {
throw new IllegalArgumentException();
}
this.privateKey = privateKey;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
} }
/** /**
@@ -25,4 +48,28 @@ public class LocalAuthor extends Author {
public byte[] getPrivateKey() { public byte[] getPrivateKey() {
return privateKey; return privateKey;
} }
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/**
* Returns the time the pseudonym was created, in milliseconds since the
* Unix epoch.
*/
public long getTimeCreated() {
return created;
}
} }

View File

@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when an identity is removed. * An event that is broadcast when a local pseudonym is added.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class IdentityRemovedEvent extends Event { public class LocalAuthorAddedEvent extends Event {
private final AuthorId authorId; private final AuthorId authorId;
public IdentityRemovedEvent(AuthorId authorId) { public LocalAuthorAddedEvent(AuthorId authorId) {
this.authorId = authorId; this.authorId = authorId;
} }

View File

@@ -7,15 +7,15 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* An event that is broadcast when an identity is added. * An event that is broadcast when a local pseudonym is removed.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class IdentityAddedEvent extends Event { public class LocalAuthorRemovedEvent extends Event {
private final AuthorId authorId; private final AuthorId authorId;
public IdentityAddedEvent(AuthorId authorId) { public LocalAuthorRemovedEvent(AuthorId authorId) {
this.authorId = authorId; this.authorId = authorId;
} }

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.keyagreement;
import java.io.IOException;
/**
* Thrown when a QR code that has been scanned uses a protocol version that is
* not supported.
*/
public class UnsupportedVersionException extends IOException {
private final boolean tooOld;
public UnsupportedVersionException(boolean tooOld) {
this.tooOld = tooOld;
}
public boolean isTooOld() {
return tooOld;
}
}

View File

@@ -2,16 +2,16 @@ package org.briarproject.bramble.api.lifecycle;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
/** /**
* Manages the lifecycle of the app: opening and closing the * Manages the lifecycle of the app, starting {@link Client Clients}, starting
* {@link DatabaseComponent} starting and stopping {@link Service Services}, * and stopping {@link Service Services}, shutting down
* and shutting down {@link ExecutorService ExecutorServices}. * {@link ExecutorService ExecutorServices}, and opening and closing the
* {@link DatabaseComponent}.
*/ */
@NotNullByDefault @NotNullByDefault
public interface LifecycleManager { public interface LifecycleManager {
@@ -42,19 +42,18 @@ public interface LifecycleManager {
} }
} }
/**
* Registers a hook to be called after the database is opened and before
* {@link Service services} are started. This method should be called
* before {@link #startServices(SecretKey)}.
*/
void registerOpenDatabaseHook(OpenDatabaseHook hook);
/** /**
* Registers a {@link Service} to be started and stopped. This method * Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(SecretKey)}. * should be called before {@link #startServices(SecretKey)}.
*/ */
void registerService(Service s); void registerService(Service s);
/**
* Registers a {@link Client} to be started. This method should be called
* before {@link #startServices(SecretKey)}.
*/
void registerClient(Client c);
/** /**
* Registers an {@link ExecutorService} to be shut down. This method * Registers an {@link ExecutorService} to be shut down. This method
* should be called before {@link #startServices(SecretKey)}. * should be called before {@link #startServices(SecretKey)}.
@@ -63,7 +62,7 @@ public interface LifecycleManager {
/** /**
* Opens the {@link DatabaseComponent} using the given key and starts any * Opens the {@link DatabaseComponent} using the given key and starts any
* registered {@link Service Services}. * registered {@link Client Clients} and {@link Service Services}.
*/ */
StartResult startServices(SecretKey dbKey); StartResult startServices(SecretKey dbKey);
@@ -81,7 +80,8 @@ public interface LifecycleManager {
/** /**
* Waits for the {@link DatabaseComponent} to be opened and all registered * Waits for the {@link DatabaseComponent} to be opened and all registered
* {@link Service Services} to start before returning. * {@link Client Clients} and {@link Service Services} to start before
* returning.
*/ */
void waitForStartup() throws InterruptedException; void waitForStartup() throws InterruptedException;
@@ -97,13 +97,4 @@ public interface LifecycleManager {
*/ */
LifecycleState getLifecycleState(); LifecycleState getLifecycleState();
interface OpenDatabaseHook {
/**
* Called when the database is being opened, before
* {@link #waitForDatabase()} returns.
*
* @param txn A read-write transaction
*/
void onDatabaseOpened(Transaction txn) throws DbException;
}
} }

View File

@@ -0,0 +1,14 @@
package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface Client {
/**
* Called at startup to create any local state needed by the client.
*/
void createLocalState(Transaction txn) throws DbException;
}

View File

@@ -11,8 +11,7 @@ public interface IncomingMessageHook {
/** /**
* Called once for each incoming message that passes validation. * Called once for each incoming message that passes validation.
* *
* @param txn A read-write transaction * @return whether or not this message should be shared
* @return Whether or not this message should be shared
* @throws DbException Should only be used for real database errors. * @throws DbException Should only be used for real database errors.
* If this is thrown, delivery will be attempted again at next startup, * If this is thrown, delivery will be attempted again at next startup,
* whereas if an InvalidMessageException is thrown, * whereas if an InvalidMessageException is thrown,

View File

@@ -46,14 +46,7 @@ public interface ClientVersioningManager {
ClientId clientId, int majorVersion) throws DbException; ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook { interface ClientVersioningHook {
/**
* Called when the visibility of a client with respect to a contact is
* changing.
*
* @param txn A read-write transaction
* @param c The contact affected by the visibility change
* @param v The new visibility of the client
*/
void onClientVisibilityChanging(Transaction txn, Contact c, void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException; Visibility v) throws DbException;
} }

View File

@@ -1,75 +0,0 @@
package org.briarproject.bramble.util;
import java.io.ByteArrayOutputStream;
public class Base32 {
private static final char[] DIGITS = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
'Y', 'Z', '2', '3', '4', '5', '6', '7'
};
public static String encode(byte[] b) {
StringBuilder s = new StringBuilder();
int byteIndex = 0, currentCode = 0x00;
int byteMask = 0x80, codeMask = 0x10;
while (byteIndex < b.length) {
if ((b[byteIndex] & byteMask) != 0) currentCode |= codeMask;
// After every 8 bits, move on to the next byte
if (byteMask == 0x01) {
byteMask = 0x80;
byteIndex++;
} else {
byteMask >>>= 1;
}
// After every 5 bits, move on to the next digit
if (codeMask == 0x01) {
s.append(DIGITS[currentCode]);
codeMask = 0x10;
currentCode = 0x00;
} else {
codeMask >>>= 1;
}
}
// If we're part-way through a digit, output it
if (codeMask != 0x10) s.append(DIGITS[currentCode]);
return s.toString();
}
public static byte[] decode(String s, boolean strict) {
ByteArrayOutputStream b = new ByteArrayOutputStream();
int digitIndex = 0, digitCount = s.length(), currentByte = 0x00;
int byteMask = 0x80, codeMask = 0x10;
while (digitIndex < digitCount) {
int code = decodeDigit(s.charAt(digitIndex));
if ((code & codeMask) != 0) currentByte |= byteMask;
// After every 8 bits, move on to the next byte
if (byteMask == 0x01) {
b.write(currentByte);
byteMask = 0x80;
currentByte = 0x00;
} else {
byteMask >>>= 1;
}
// After every 5 bits, move on to the next digit
if (codeMask == 0x01) {
codeMask = 0x10;
digitIndex++;
} else {
codeMask >>>= 1;
}
}
// If any extra bits were used for encoding, they should all be zero
if (strict && byteMask != 0x80 && currentByte != 0x00)
throw new IllegalArgumentException();
return b.toByteArray();
}
private static int decodeDigit(char c) {
if (c >= 'A' && c <= 'Z') return c - 'A';
if (c >= 'a' && c <= 'z') return c - 'a';
if (c >= '2' && c <= '7') return c - '2' + 26;
throw new IllegalArgumentException("Not a base32 digit: " + c);
}
}

View File

@@ -153,13 +153,4 @@ public class StringUtils {
return new String(c); return new String(c);
} }
public static String getRandomBase32String(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++) {
int character = random.nextInt(32);
if (character < 26) c[i] = (char) ('a' + character);
else c[i] = (char) ('2' + (character - 26));
}
return new String(c);
}
} }

View File

@@ -8,7 +8,6 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -31,7 +30,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION; import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -101,26 +99,25 @@ public class TestUtils {
return new SecretKey(getRandomBytes(SecretKey.LENGTH)); return new SecretKey(getRandomBytes(SecretKey.LENGTH));
} }
public static Identity getIdentity() { public static LocalAuthor getLocalAuthor() {
LocalAuthor localAuthor = getLocalAuthor(); return getLocalAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
byte[] handshakePub = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] handshakePriv = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
return new Identity(localAuthor, handshakePub, handshakePriv,
timestamp);
} }
public static LocalAuthor getLocalAuthor() { public static LocalAuthor getLocalAuthor(int nameLength) {
AuthorId id = new AuthorId(getRandomId()); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength); String name = getRandomString(nameLength);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); byte[] privateKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey); return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey,
timestamp);
} }
public static Author getAuthor() { public static Author getAuthor() {
return getAuthor(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static Author getAuthor(int nameLength) {
AuthorId id = new AuthorId(getRandomId()); AuthorId id = new AuthorId(getRandomId());
int nameLength = 1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH);
String name = getRandomString(nameLength); String name = getRandomString(nameLength);
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
return new Author(id, FORMAT_VERSION, name, publicKey); return new Author(id, FORMAT_VERSION, name, publicKey);
@@ -166,19 +163,15 @@ public class TestUtils {
} }
public static Contact getContact() { public static Contact getContact() {
return getContact(getAuthor(), new AuthorId(getRandomId()), return getContact(getAuthor(), random.nextBoolean());
random.nextBoolean());
} }
public static Contact getContact(Author remote, AuthorId local, public static Contact getContact(Author a, boolean verified) {
boolean verified) { return getContact(getContactId(), a, verified);
return getContact(getContactId(), remote, local, verified);
} }
public static Contact getContact(ContactId c, Author remote, AuthorId local, public static Contact getContact(ContactId c, Author a, boolean verified) {
boolean verified) { return new Contact(c, a, getRandomString(MAX_AUTHOR_NAME_LENGTH),
return new Contact(c, remote, local,
getRandomString(MAX_AUTHOR_NAME_LENGTH),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified); getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
} }

View File

@@ -2,7 +2,7 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',

View File

@@ -17,7 +17,7 @@ dependencies {
implementation 'org.whispersystems:curve25519-java:0.5.0' implementation 'org.whispersystems:curve25519-java:0.5.0'
implementation 'org.briarproject:jtorctl:0.3' implementation 'org.briarproject:jtorctl:0.3'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
@@ -26,7 +26,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.19'
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -4,8 +4,8 @@ import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
@@ -161,8 +161,8 @@ class AccountManagerImpl implements AccountManager {
synchronized (stateChangeLock) { synchronized (stateChangeLock) {
if (hasDatabaseKey()) if (hasDatabaseKey())
throw new AssertionError("Already have a database key"); throw new AssertionError("Already have a database key");
Identity identity = identityManager.createIdentity(name); LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
identityManager.registerIdentity(identity); identityManager.registerLocalAuthor(localAuthor);
SecretKey key = crypto.generateSecretKey(); SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false; if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key; databaseKey = key;

View File

@@ -39,8 +39,7 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
@Override @Override
public Group createContactGroup(ClientId clientId, int majorVersion, public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact) { Contact contact, AuthorId local) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId(); AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote); byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, majorVersion, descriptor); return groupFactory.createGroup(clientId, majorVersion, descriptor);

View File

@@ -293,8 +293,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
throws DbException { throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
ContactId contactId = contactManager.addContact(txn, remoteAuthor, ContactId contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterKey, timestamp, alice, masterKey, timestamp, alice, true, true);
true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);
return contactId; return contactId;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
@@ -9,7 +8,6 @@ import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -21,42 +19,45 @@ import org.briarproject.bramble.api.transport.KeyManager;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES; import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.util.StringUtils.toUtf8;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class ContactManagerImpl implements ContactManager { class ContactManagerImpl implements ContactManager {
private static final int LINK_LENGTH = 64;
private static final String REMOTE_CONTACT_LINK = private static final String REMOTE_CONTACT_LINK =
"briar://" + getRandomBase32String(BASE32_LINK_BYTES); "briar://" + getRandomBase32String(LINK_LENGTH);
private static final Pattern LINK_REGEX =
Pattern.compile("(briar://)?([a-z2-7]{" + LINK_LENGTH + "})");
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final PendingContactFactory pendingContactFactory;
private final List<ContactHook> hooks; private final List<ContactHook> hooks;
@Inject @Inject
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager, ContactManagerImpl(DatabaseComponent db, KeyManager keyManager,
IdentityManager identityManager, IdentityManager identityManager) {
PendingContactFactory pendingContactFactory) {
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
this.identityManager = identityManager; this.identityManager = identityManager;
this.pendingContactFactory = pendingContactFactory;
hooks = new CopyOnWriteArrayList<>(); hooks = new CopyOnWriteArrayList<>();
} }
@@ -66,10 +67,10 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
SecretKey rootKey, long timestamp, boolean alice, boolean verified, long timestamp, boolean alice, boolean verified, boolean active)
boolean active) throws DbException { throws DbException {
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, a, verified);
keyManager.addContact(txn, c, rootKey, timestamp, alice, active); keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
@@ -77,46 +78,63 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local, public ContactId addContact(Transaction txn, Author a, boolean verified)
boolean verified) throws DbException { throws DbException {
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, a, verified);
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact); for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c; return c;
} }
@Override @Override
public ContactId addContact(Author remote, AuthorId local, public ContactId addContact(Author a, SecretKey rootKey, long timestamp,
SecretKey rootKey, long timestamp, boolean alice, boolean verified, boolean alice, boolean verified, boolean active)
boolean active) throws DbException { throws DbException {
return db.transactionWithResult(false, txn -> return db.transactionWithResult(false, txn ->
addContact(txn, remote, local, rootKey, timestamp, alice, addContact(txn, a, rootKey, timestamp, alice,
verified, active)); verified, active));
} }
@Override @Override
public String getHandshakeLink() { public String getRemoteContactLink() {
// TODO replace with real implementation // TODO replace with real implementation
return REMOTE_CONTACT_LINK; return REMOTE_CONTACT_LINK;
} }
@Override @SuppressWarnings("SameParameterValue")
public PendingContact addPendingContact(String link, String alias) private static String getRandomBase32String(int length) {
throws DbException, FormatException { Random random = new Random();
PendingContact p = char[] c = new char[length];
pendingContactFactory.createPendingContact(link, alias); for (int i = 0; i < length; i++) {
db.transaction(false, txn -> db.addPendingContact(txn, p)); int character = random.nextInt(32);
return p; if (character < 26) c[i] = (char) ('a' + character);
else c[i] = (char) ('2' + (character - 26));
}
return new String(c);
} }
@Override @Override
public Collection<PendingContact> getPendingContacts() throws DbException { public boolean isValidRemoteContactLink(String link) {
return db.transactionWithResult(true, db::getPendingContacts); return LINK_REGEX.matcher(link).matches();
} }
@Override @Override
public void removePendingContact(PendingContactId p) throws DbException { public PendingContact addRemoteContactRequest(String link, String alias) {
db.transaction(false, txn -> db.removePendingContact(txn, p)); // TODO replace with real implementation
PendingContactId id = new PendingContactId(link.getBytes());
return new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH], alias,
WAITING_FOR_CONNECTION, System.currentTimeMillis());
}
@Override
public Collection<PendingContact> getPendingContacts() {
// TODO replace with real implementation
return emptyList();
}
@Override
public void removePendingContact(PendingContact pendingContact) {
// TODO replace with real implementation
} }
@Override @Override
@@ -125,23 +143,13 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId) public Contact getContact(AuthorId a) throws DbException {
throws DbException { return db.transactionWithResult(true, txn -> getContact(txn, a));
return db.transactionWithResult(true, txn ->
getContact(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
public Contact getContact(Transaction txn, AuthorId remoteAuthorId, public Contact getContact(Transaction txn, AuthorId a) throws DbException {
AuthorId localAuthorId) throws DbException { return db.getContact(txn, a);
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, remoteAuthorId);
for (Contact c : contacts) {
if (c.getLocalAuthorId().equals(localAuthorId)) {
return c;
}
}
throw new NoSuchContactException();
} }
@Override @Override
@@ -172,16 +180,14 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId, public boolean contactExists(Transaction txn, AuthorId a)
AuthorId localAuthorId) throws DbException { throws DbException {
return db.containsContact(txn, remoteAuthorId, localAuthorId); return db.containsContact(txn, a);
} }
@Override @Override
public boolean contactExists(AuthorId remoteAuthorId, public boolean contactExists(AuthorId a) throws DbException {
AuthorId localAuthorId) throws DbException { return db.transactionWithResult(true, txn -> contactExists(txn, a));
return db.transactionWithResult(true, txn ->
contactExists(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
@@ -203,12 +209,12 @@ class ContactManagerImpl implements ContactManager {
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.getId().equals(authorId)) if (localAuthor.getId().equals(authorId))
return new AuthorInfo(OURSELVES); return new AuthorInfo(OURSELVES);
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId); if (db.containsContact(txn, authorId)) {
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN); Contact c = db.getContact(txn, authorId);
if (contacts.size() > 1) throw new AssertionError(); if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
Contact c = contacts.iterator().next(); else return new AuthorInfo(UNVERIFIED, c.getAlias());
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias()); }
else return new AuthorInfo(UNVERIFIED, c.getAlias()); return new AuthorInfo(UNKNOWN);
} }
} }

View File

@@ -28,10 +28,4 @@ public class ContactModule {
ContactExchangeTaskImpl contactExchangeTask) { ContactExchangeTaskImpl contactExchangeTask) {
return contactExchangeTask; return contactExchangeTask;
} }
@Provides
PendingContactFactory providePendingContactFactory(
PendingContactFactoryImpl pendingContactFactory) {
return pendingContactFactory;
}
} }

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.contact.PendingContact;
interface PendingContactFactory {
/**
* Creates a {@link PendingContact} from the given handshake link and alias.
*
* @throws UnsupportedVersionException If the link uses a format version
* that is not supported
* @throws FormatException If the link is invalid
*/
PendingContact createPendingContact(String link, String alias)
throws FormatException;
}

View File

@@ -1,70 +0,0 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.Base32;
import java.security.GeneralSecurityException;
import java.util.regex.Matcher;
import javax.inject.Inject;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
class PendingContactFactoryImpl implements PendingContactFactory {
private final CryptoComponent crypto;
private final Clock clock;
@Inject
PendingContactFactoryImpl(CryptoComponent crypto, Clock clock) {
this.crypto = crypto;
this.clock = clock;
}
@Override
public PendingContact createPendingContact(String link, String alias)
throws FormatException {
PublicKey publicKey = parseHandshakeLink(link);
PendingContactId id = getPendingContactId(publicKey);
long timestamp = clock.currentTimeMillis();
return new PendingContact(id, publicKey.getEncoded(), alias,
WAITING_FOR_CONNECTION, timestamp);
}
private PublicKey parseHandshakeLink(String link) throws FormatException {
Matcher matcher = LINK_REGEX.matcher(link);
if (!matcher.find()) throw new FormatException();
// Discard 'briar://' and anything before or after the link
link = matcher.group(2);
byte[] base32 = Base32.decode(link, false);
if (base32.length != RAW_LINK_BYTES) throw new AssertionError();
byte version = base32[0];
if (version != FORMAT_VERSION)
throw new UnsupportedVersionException(version < FORMAT_VERSION);
byte[] publicKeyBytes = new byte[base32.length - 1];
arraycopy(base32, 1, publicKeyBytes, 0, publicKeyBytes.length);
try {
KeyParser parser = crypto.getAgreementKeyParser();
return parser.parsePublicKey(publicKeyBytes);
} catch (GeneralSecurityException e) {
throw new FormatException();
}
}
private PendingContactId getPendingContactId(PublicKey publicKey) {
byte[] hash = crypto.hash(ID_LABEL, publicKey.getEncoded());
return new PendingContactId(hash);
}
}

View File

@@ -15,7 +15,7 @@ import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -87,11 +87,10 @@ interface Database<T> {
void commitTransaction(T txn) throws DbException; void commitTransaction(T txn) throws DbException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact with the given pseudonym and returns an ID for the
* and returns an ID for the contact. * contact.
*/ */
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified) ContactId addContact(T txn, Author a, boolean verified) throws DbException;
throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -120,9 +119,9 @@ interface Database<T> {
HandshakeKeys k) throws DbException; HandshakeKeys k) throws DbException;
/** /**
* Stores an identity. * Stores a local pseudonym.
*/ */
void addIdentity(T txn, Identity i) throws DbException; void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
/** /**
* Stores a message. * Stores a message.
@@ -164,13 +163,11 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Returns true if the database contains the given contact for the given * Returns true if the database contains the given contact.
* local pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsContact(T txn, AuthorId remote, AuthorId local) boolean containsContact(T txn, AuthorId a) throws DbException;
throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact.
@@ -187,12 +184,11 @@ interface Database<T> {
boolean containsGroup(T txn, GroupId g) throws DbException; boolean containsGroup(T txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains an identity for the given * Returns true if the database contains the given local pseudonym.
* pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsIdentity(T txn, AuthorId a) throws DbException; boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
/** /**
* Returns true if the database contains the given message. * Returns true if the database contains the given message.
@@ -253,6 +249,13 @@ interface Database<T> {
*/ */
Contact getContact(T txn, ContactId c) throws DbException; Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(T txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -260,21 +263,6 @@ interface Database<T> {
*/ */
Collection<Contact> getContacts(T txn) throws DbException; Collection<Contact> getContacts(T txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(T txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -324,18 +312,18 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Returns the identity for local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Identity getIdentity(T txn, AuthorId a) throws DbException; LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
/** /**
* Returns the identities for all local pseudonyms. * Returns all local pseudonyms.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<Identity> getIdentities(T txn) throws DbException; Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
/** /**
* Returns the message with the given ID. * Returns the message with the given ID.
@@ -630,9 +618,9 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Removes an identity (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
void removeIdentity(T txn, AuthorId a) throws DbException; void removeLocalAuthor(T txn, AuthorId a) throws DbException;
/** /**
* Removes a message (and all associated state) from the database. * Removes a message (and all associated state) from the database.
@@ -686,12 +674,6 @@ interface Database<T> {
void setGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) void setGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException; throws DbException;
/**
* Sets the handshake key pair for the identity with the given ID.
*/
void setHandshakeKeyPair(T txn, AuthorId local, byte[] publicKey,
byte[] privateKey) throws DbException;
/** /**
* Marks the given message as shared. * Marks the given message as shared.
*/ */

View File

@@ -20,7 +20,7 @@ import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchIdentityException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException; import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
@@ -32,9 +32,9 @@ import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.identity.event.IdentityAddedEvent; import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent;
import org.briarproject.bramble.api.identity.event.IdentityRemovedEvent; import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -232,18 +232,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public ContactId addContact(Transaction transaction, Author remote, public ContactId addContact(Transaction transaction, Author a,
AuthorId local, boolean verified) boolean verified) throws DbException {
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, local)) if (db.containsLocalAuthor(txn, a.getId()))
throw new NoSuchIdentityException();
if (db.containsIdentity(txn, remote.getId()))
throw new ContactExistsException(); throw new ContactExistsException();
if (db.containsContact(txn, remote.getId(), local)) if (db.containsContact(txn, a.getId()))
throw new ContactExistsException(); throw new ContactExistsException();
ContactId c = db.addContact(txn, remote, local, verified); ContactId c = db.addContact(txn, a, verified);
transaction.attach(new ContactAddedEvent(c)); transaction.attach(new ContactAddedEvent(c));
return c; return c;
} }
@@ -283,13 +280,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void addIdentity(Transaction transaction, Identity i) public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, i.getId())) { if (!db.containsLocalAuthor(txn, a.getId())) {
db.addIdentity(txn, i); db.addLocalAuthor(txn, a);
transaction.attach(new IdentityAddedEvent(i.getId())); transaction.attach(new LocalAuthorAddedEvent(a.getId()));
} }
} }
@@ -342,12 +339,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public boolean containsContact(Transaction transaction, AuthorId remote, public boolean containsContact(Transaction transaction, AuthorId a)
AuthorId local) throws DbException { throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, local)) return db.containsContact(txn, a);
throw new NoSuchIdentityException();
return db.containsContact(txn, remote, local);
} }
@Override @Override
@@ -358,10 +353,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public boolean containsIdentity(Transaction transaction, AuthorId a) public boolean containsLocalAuthor(Transaction transaction, AuthorId local)
throws DbException { throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
return db.containsIdentity(txn, a); return db.containsLocalAuthor(txn, local);
} }
@Override @Override
@@ -487,6 +482,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContact(txn, c); return db.getContact(txn, c);
} }
@Override
public Contact getContact(Transaction transaction, AuthorId a)
throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, a))
throw new NoSuchContactException();
return db.getContact(txn, a);
}
@Override @Override
public Collection<Contact> getContacts(Transaction transaction) public Collection<Contact> getContacts(Transaction transaction)
throws DbException { throws DbException {
@@ -494,22 +498,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContacts(txn); return db.getContacts(txn);
} }
@Override
public Collection<Contact> getContactsByAuthorId(Transaction transaction,
AuthorId remote) throws DbException {
T txn = unbox(transaction);
return db.getContactsByAuthorId(txn, remote);
}
@Override
public Collection<ContactId> getContacts(Transaction transaction,
AuthorId a) throws DbException {
T txn = unbox(transaction);
if (!db.containsIdentity(txn, a))
throw new NoSuchIdentityException();
return db.getContacts(txn, a);
}
@Override @Override
public Group getGroup(Transaction transaction, GroupId g) public Group getGroup(Transaction transaction, GroupId g)
throws DbException { throws DbException {
@@ -554,19 +542,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public Identity getIdentity(Transaction transaction, AuthorId a) public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, a)) if (!db.containsLocalAuthor(txn, a))
throw new NoSuchIdentityException(); throw new NoSuchLocalAuthorException();
return db.getIdentity(txn, a); return db.getLocalAuthor(txn, a);
} }
@Override @Override
public Collection<Identity> getIdentities(Transaction transaction) public Collection<LocalAuthor> getLocalAuthors(Transaction transaction)
throws DbException { throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
return db.getIdentities(txn); return db.getLocalAuthors(txn);
} }
@Override @Override
@@ -905,14 +893,14 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void removeIdentity(Transaction transaction, AuthorId a) public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsIdentity(txn, a)) if (!db.containsLocalAuthor(txn, a))
throw new NoSuchIdentityException(); throw new NoSuchLocalAuthorException();
db.removeIdentity(txn, a); db.removeLocalAuthor(txn, a);
transaction.attach(new IdentityRemovedEvent(a)); transaction.attach(new LocalAuthorRemovedEvent(a));
} }
@Override @Override
@@ -1035,16 +1023,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
} }
@Override
public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
byte[] publicKey, byte[] privateKey) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsIdentity(txn, local))
throw new NoSuchIdentityException();
db.setHandshakeKeyPair(txn, local, publicKey, privateKey);
}
@Override @Override
public void setReorderingWindow(Transaction transaction, public void setReorderingWindow(Transaction transaction,
TransportKeySetId k, TransportId t, long timePeriod, long base, TransportKeySetId k, TransportId t, long timePeriod, long base,

View File

@@ -15,7 +15,6 @@ import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -93,7 +92,7 @@ import static org.briarproject.bramble.util.LogUtils.now;
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing // Package access for testing
static final int CODE_SCHEMA_VERSION = 43; static final int CODE_SCHEMA_VERSION = 44;
// Time period offsets for incoming transport keys // Time period offsets for incoming transport keys
private static final int OFFSET_PREV = -1; private static final int OFFSET_PREV = -1;
@@ -128,12 +127,8 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " alias _STRING," // Null if no alias has been set + " alias _STRING," // Null if no alias has been set
+ " publicKey _BINARY NOT NULL," + " publicKey _BINARY NOT NULL,"
+ " handshakePublicKey _BINARY," // Null if key is unknown + " handshakePublicKey _BINARY," // Null if key is unknown
+ " localAuthorId _HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL," + " verified BOOLEAN NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId))";
+ " FOREIGN KEY (localAuthorId)"
+ " REFERENCES localAuthors (authorId)"
+ " ON DELETE CASCADE)";
private static final String CREATE_GROUPS = private static final String CREATE_GROUPS =
"CREATE TABLE groups" "CREATE TABLE groups"
@@ -487,7 +482,8 @@ abstract class JdbcDatabase implements Database<Connection> {
new Migration39_40(), new Migration39_40(),
new Migration40_41(dbTypes), new Migration40_41(dbTypes),
new Migration41_42(dbTypes), new Migration41_42(dbTypes),
new Migration42_43(dbTypes) new Migration42_43(dbTypes),
new Migration43_44()
); );
} }
@@ -663,23 +659,21 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public ContactId addContact(Connection txn, Author remote, AuthorId local, public ContactId addContact(Connection txn, Author a, boolean verified)
boolean verified) throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
// Create a contact row // Create a contact row
String sql = "INSERT INTO contacts" String sql = "INSERT INTO contacts"
+ " (authorId, formatVersion, name, publicKey," + " (authorId, formatVersion, name, publicKey, verified)"
+ " localAuthorId, verified)" + " VALUES (?, ?, ?, ?, ?)";
+ " VALUES (?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getId().getBytes()); ps.setBytes(1, a.getId().getBytes());
ps.setInt(2, remote.getFormatVersion()); ps.setInt(2, a.getFormatVersion());
ps.setString(3, remote.getName()); ps.setString(3, a.getName());
ps.setBytes(4, remote.getPublicKey()); ps.setBytes(4, a.getPublicKey());
ps.setBytes(5, local.getBytes()); ps.setBoolean(5, verified);
ps.setBoolean(6, verified);
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -875,7 +869,8 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void addIdentity(Connection txn, Identity i) throws DbException { public void addLocalAuthor(Connection txn, LocalAuthor a)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "INSERT INTO localAuthors" String sql = "INSERT INTO localAuthors"
@@ -883,17 +878,16 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " handshakePublicKey, handshakePrivateKey, created)" + " handshakePublicKey, handshakePrivateKey, created)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
LocalAuthor local = i.getLocalAuthor(); ps.setBytes(1, a.getId().getBytes());
ps.setBytes(1, local.getId().getBytes()); ps.setInt(2, a.getFormatVersion());
ps.setInt(2, local.getFormatVersion()); ps.setString(3, a.getName());
ps.setString(3, local.getName()); ps.setBytes(4, a.getPublicKey());
ps.setBytes(4, local.getPublicKey()); ps.setBytes(5, a.getPrivateKey());
ps.setBytes(5, local.getPrivateKey()); if (a.getHandshakePublicKey() == null) ps.setNull(6, BINARY);
if (i.getHandshakePublicKey() == null) ps.setNull(6, BINARY); else ps.setBytes(6, a.getHandshakePublicKey());
else ps.setBytes(6, i.getHandshakePublicKey()); if (a.getHandshakePrivateKey() == null) ps.setNull(7, BINARY);
if (i.getHandshakePrivateKey() == null) ps.setNull(7, BINARY); else ps.setBytes(7, a.getHandshakePrivateKey());
else ps.setBytes(7, i.getHandshakePrivateKey()); ps.setLong(8, a.getTimeCreated());
ps.setLong(8, i.getTimeCreated());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException(); if (affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -1181,16 +1175,14 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public boolean containsContact(Connection txn, AuthorId remote, public boolean containsContact(Connection txn, AuthorId a)
AuthorId local) throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT NULL FROM contacts" String sql = "SELECT NULL FROM contacts WHERE authorId = ?";
+ " WHERE authorId = ? AND localAuthorId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getBytes()); ps.setBytes(1, a.getBytes());
ps.setBytes(2, local.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
boolean found = rs.next(); boolean found = rs.next();
if (rs.next()) throw new DbStateException(); if (rs.next()) throw new DbStateException();
@@ -1249,7 +1241,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public boolean containsIdentity(Connection txn, AuthorId a) public boolean containsLocalAuthor(Connection txn, AuthorId a)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
@@ -1433,7 +1425,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT authorId, formatVersion, name, alias," String sql = "SELECT authorId, formatVersion, name, alias,"
+ " publicKey, handshakePublicKey, localAuthorId, verified" + " publicKey, handshakePublicKey, verified"
+ " FROM contacts" + " FROM contacts"
+ " WHERE contactId = ?"; + " WHERE contactId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
@@ -1446,14 +1438,44 @@ abstract class JdbcDatabase implements Database<Connection> {
String alias = rs.getString(4); String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5); byte[] publicKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6); byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7)); boolean verified = rs.getBoolean(7);
boolean verified = rs.getBoolean(8);
rs.close(); rs.close();
ps.close(); ps.close();
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); new Author(authorId, formatVersion, name, publicKey);
return new Contact(c, author, localAuthorId, alias, return new Contact(c, author, alias, handshakePublicKey, verified);
handshakePublicKey, verified); } catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public Contact getContact(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, formatVersion, name, alias,"
+ " publicKey, handshakePublicKey, verified"
+ " FROM contacts"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
ContactId contactId = new ContactId(rs.getInt(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6);
boolean verified = rs.getBoolean(7);
rs.close();
ps.close();
Author author = new Author(a, formatVersion, name, publicKey);
return new Contact(contactId, author, alias, handshakePublicKey,
verified);
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs, LOG, WARNING); tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING); tryToClose(ps, LOG, WARNING);
@@ -1467,8 +1489,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, authorId, formatVersion, name," String sql = "SELECT contactId, authorId, formatVersion, name,"
+ " alias, publicKey, handshakePublicKey, localAuthorId," + " alias, publicKey, handshakePublicKey, verified"
+ " verified"
+ " FROM contacts"; + " FROM contacts";
s = txn.createStatement(); s = txn.createStatement();
rs = s.executeQuery(sql); rs = s.executeQuery(sql);
@@ -1481,12 +1502,11 @@ abstract class JdbcDatabase implements Database<Connection> {
String alias = rs.getString(5); String alias = rs.getString(5);
byte[] publicKey = rs.getBytes(6); byte[] publicKey = rs.getBytes(6);
byte[] handshakePublicKey = rs.getBytes(7); byte[] handshakePublicKey = rs.getBytes(7);
AuthorId localAuthorId = new AuthorId(rs.getBytes(8)); boolean verified = rs.getBoolean(8);
boolean verified = rs.getBoolean(9);
Author author = Author author =
new Author(authorId, formatVersion, name, publicKey); new Author(authorId, formatVersion, name, publicKey);
contacts.add(new Contact(contactId, author, localAuthorId, contacts.add(new Contact(contactId, author, alias,
alias, handshakePublicKey, verified)); handshakePublicKey, verified));
} }
rs.close(); rs.close();
s.close(); s.close();
@@ -1498,67 +1518,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public Collection<ContactId> getContacts(Connection txn, AuthorId local)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId FROM contacts"
+ " WHERE localAuthorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, local.getBytes());
rs = ps.executeQuery();
List<ContactId> ids = new ArrayList<>();
while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
rs.close();
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public Collection<Contact> getContactsByAuthorId(Connection txn,
AuthorId remote) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId, formatVersion, name, alias,"
+ " publicKey, handshakePublicKey, localAuthorId, verified"
+ " FROM contacts"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getBytes());
rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<>();
while (rs.next()) {
ContactId contactId = new ContactId(rs.getInt(1));
int formatVersion = rs.getInt(2);
String name = rs.getString(3);
String alias = rs.getString(4);
byte[] publicKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6);
AuthorId localAuthorId = new AuthorId(rs.getBytes(7));
boolean verified = rs.getBoolean(8);
Author author =
new Author(remote, formatVersion, name, publicKey);
contacts.add(new Contact(contactId, author, localAuthorId,
alias, handshakePublicKey, verified));
}
rs.close();
ps.close();
return contacts;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Group getGroup(Connection txn, GroupId g) throws DbException { public Group getGroup(Connection txn, GroupId g) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1661,6 +1620,41 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public LocalAuthor getLocalAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT formatVersion, name, publicKey, privateKey,"
+ " handshakePublicKey, handshakePrivateKey, created"
+ " FROM localAuthors"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
int formatVersion = rs.getInt(1);
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
byte[] handshakePublicKey = rs.getBytes(5);
byte[] handshakePrivateKey = rs.getBytes(6);
long created = rs.getLong(7);
LocalAuthor localAuthor = new LocalAuthor(a, formatVersion, name,
publicKey, privateKey, handshakePublicKey,
handshakePrivateKey, created);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return localAuthor;
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public Collection<HandshakeKeySet> getHandshakeKeys(Connection txn, public Collection<HandshakeKeySet> getHandshakeKeys(Connection txn,
TransportId t) throws DbException { TransportId t) throws DbException {
@@ -1742,69 +1736,30 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public Identity getIdentity(Connection txn, AuthorId a) throws DbException { public Collection<LocalAuthor> getLocalAuthors(Connection txn)
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT formatVersion, name, publicKey, privateKey,"
+ " handshakePublicKey, handshakePrivateKey, created"
+ " FROM localAuthors"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
int formatVersion = rs.getInt(1);
String name = rs.getString(2);
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
byte[] handshakePublicKey = rs.getBytes(5);
byte[] handshakePrivateKey = rs.getBytes(6);
long created = rs.getLong(7);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
LocalAuthor local = new LocalAuthor(a, formatVersion, name,
publicKey, privateKey);
return new Identity(local, handshakePublicKey, handshakePrivateKey,
created);
} catch (SQLException e) {
tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override
public Collection<Identity> getIdentities(Connection txn)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT authorId, formatVersion, name, publicKey," String sql = "SELECT authorId, formatVersion, name, publicKey,"
+ " privateKey, handshakePublicKey, handshakePrivateKey," + " privateKey, created"
+ " created"
+ " FROM localAuthors"; + " FROM localAuthors";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Identity> identities = new ArrayList<>(); List<LocalAuthor> authors = new ArrayList<>();
while (rs.next()) { while (rs.next()) {
AuthorId authorId = new AuthorId(rs.getBytes(1)); AuthorId authorId = new AuthorId(rs.getBytes(1));
int formatVersion = rs.getInt(2); int formatVersion = rs.getInt(2);
String name = rs.getString(3); String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4); byte[] publicKey = rs.getBytes(4);
byte[] privateKey = rs.getBytes(5); byte[] privateKey = rs.getBytes(5);
byte[] handshakePublicKey = rs.getBytes(6); long created = rs.getLong(6);
byte[] handshakePrivateKey = rs.getBytes(7); authors.add(new LocalAuthor(authorId, formatVersion, name,
long created = rs.getLong(8); publicKey, privateKey, created));
LocalAuthor local = new LocalAuthor(authorId, formatVersion,
name, publicKey, privateKey);
identities.add(new Identity(local, handshakePublicKey,
handshakePrivateKey, created));
} }
rs.close(); rs.close();
ps.close(); ps.close();
return identities; return authors;
} catch (SQLException e) { } catch (SQLException e) {
tryToClose(rs, LOG, WARNING); tryToClose(rs, LOG, WARNING);
tryToClose(ps, LOG, WARNING); tryToClose(ps, LOG, WARNING);
@@ -2963,7 +2918,8 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
@Override @Override
public void removeIdentity(Connection txn, AuthorId a) throws DbException { public void removeLocalAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "DELETE FROM localAuthors WHERE authorId = ?"; String sql = "DELETE FROM localAuthors WHERE authorId = ?";
@@ -3180,27 +3136,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void setHandshakeKeyPair(Connection txn, AuthorId local,
byte[] publicKey, byte[] privateKey) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE localAuthors"
+ " SET handshakePublicKey = ?, handshakePrivateKey = ?"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, publicKey);
ps.setBytes(2, privateKey);
ps.setBytes(3, local.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void setMessageShared(Connection txn, MessageId m) public void setMessageShared(Connection txn, MessageId m)
throws DbException { throws DbException {

View File

@@ -0,0 +1,40 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration43_44 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration43_44.class.getName());
@Override
public int getStartVersion() {
return 43;
}
@Override
public int getEndVersion() {
return 44;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE contacts"
+ " DROP COLUMN localAuthorId");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,12 +1,12 @@
package org.briarproject.bramble.identity; package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
@@ -22,10 +22,12 @@ import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
class AuthorFactoryImpl implements AuthorFactory { class AuthorFactoryImpl implements AuthorFactory {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final Clock clock;
@Inject @Inject
AuthorFactoryImpl(CryptoComponent crypto) { AuthorFactoryImpl(CryptoComponent crypto, Clock clock) {
this.crypto = crypto; this.crypto = crypto;
this.clock = clock;
} }
@Override @Override
@@ -41,12 +43,17 @@ class AuthorFactoryImpl implements AuthorFactory {
} }
@Override @Override
public LocalAuthor createLocalAuthor(String name) { public LocalAuthor createLocalAuthor(String name, byte[] publicKey,
KeyPair signatureKeyPair = crypto.generateSignatureKeyPair(); byte[] privateKey) {
byte[] publicKey = signatureKeyPair.getPublic().getEncoded(); return createLocalAuthor(FORMAT_VERSION, name, publicKey, privateKey);
byte[] privateKey = signatureKeyPair.getPrivate().getEncoded(); }
AuthorId id = getId(FORMAT_VERSION, name, publicKey);
return new LocalAuthor(id, FORMAT_VERSION, name, publicKey, privateKey); @Override
public LocalAuthor createLocalAuthor(int formatVersion, String name,
byte[] publicKey, byte[] privateKey) {
AuthorId id = getId(formatVersion, name, publicKey);
return new LocalAuthor(id, formatVersion, name, publicKey, privateKey,
clock.currentTimeMillis());
} }
private AuthorId getId(int formatVersion, String name, byte[] publicKey) { private AuthorId getId(int formatVersion, String name, byte[] publicKey) {

View File

@@ -6,164 +6,97 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook { class IdentityManagerImpl implements IdentityManager {
private static final Logger LOG = private static final Logger LOG =
getLogger(IdentityManagerImpl.class.getName()); Logger.getLogger(IdentityManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final AuthorFactory authorFactory; private final AuthorFactory authorFactory;
private final Clock clock;
/** // The local author is immutable so we can cache it
* The user's identity, or null if no identity has been registered or
* loaded. If non-null, this identity always has handshake keys.
*/
@Nullable @Nullable
private volatile Identity cachedIdentity = null; private volatile LocalAuthor cachedAuthor;
/**
* True if {@code cachedIdentity} was registered via
* {@link #registerIdentity(Identity)} and should be stored when
* {@link #onDatabaseOpened(Transaction)} is called.
*/
private volatile boolean shouldStoreIdentity = false;
/**
* True if the handshake keys in {@code cachedIdentity} were generated
* when the identity was loaded and should be stored when
* {@link #onDatabaseOpened(Transaction)} is called.
*/
private volatile boolean shouldStoreKeys = false;
@Inject @Inject
IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto, IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto,
AuthorFactory authorFactory, Clock clock) { AuthorFactory authorFactory) {
this.db = db; this.db = db;
this.crypto = crypto; this.crypto = crypto;
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.clock = clock;
} }
@Override @Override
public Identity createIdentity(String name) { public LocalAuthor createLocalAuthor(String name) {
long start = now(); long start = now();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name); KeyPair keyPair = crypto.generateSignatureKeyPair();
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair(); byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded();
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded(); LocalAuthor localAuthor = authorFactory.createLocalAuthor(name,
logDuration(LOG, "Creating identity", start); publicKey, privateKey);
return new Identity(localAuthor, handshakePub, handshakePriv, logDuration(LOG, "Creating local author", start);
clock.currentTimeMillis()); return localAuthor;
} }
@Override @Override
public void registerIdentity(Identity i) { public void registerLocalAuthor(LocalAuthor a) {
if (!i.hasHandshakeKeyPair()) throw new IllegalArgumentException(); cachedAuthor = a;
cachedIdentity = i; LOG.info("Local author registered");
shouldStoreIdentity = true;
LOG.info("Identity registered");
} }
@Override @Override
public void onDatabaseOpened(Transaction txn) throws DbException { public void storeLocalAuthor() throws DbException {
Identity cached = getCachedIdentity(txn); LocalAuthor cached = cachedAuthor;
if (shouldStoreIdentity) { if (cached == null) {
// The identity was registered at startup - store it LOG.info("No local author to store");
db.addIdentity(txn, cached); return;
LOG.info("Identity stored");
} else if (shouldStoreKeys) {
// Handshake keys were generated when loading the identity -
// store them
byte[] handshakePub =
requireNonNull(cached.getHandshakePublicKey());
byte[] handshakePriv =
requireNonNull(cached.getHandshakePrivateKey());
db.setHandshakeKeyPair(txn, cached.getId(), handshakePub,
handshakePriv);
LOG.info("Handshake key pair stored");
} }
db.transaction(false, txn -> db.addLocalAuthor(txn, cached));
LOG.info("Local author stored");
} }
@Override @Override
public LocalAuthor getLocalAuthor() throws DbException { public LocalAuthor getLocalAuthor() throws DbException {
Identity cached = cachedIdentity; if (cachedAuthor == null) {
if (cached == null) cachedAuthor =
cached = db.transactionWithResult(true, this::getCachedIdentity); db.transactionWithResult(true, this::loadLocalAuthor);
return cached.getLocalAuthor(); LOG.info("Local author loaded");
} }
LocalAuthor cached = cachedAuthor;
@Override if (cached == null) throw new AssertionError();
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
return getCachedIdentity(txn).getLocalAuthor();
}
@Override
public byte[][] getHandshakeKeys(Transaction txn) throws DbException {
Identity cached = getCachedIdentity(txn);
return new byte[][] {
cached.getHandshakePublicKey(),
cached.getHandshakePrivateKey()
};
}
/**
* Loads the identity if necessary and returns it. If
* {@code cachedIdentity} was not already set by calling
* {@link #registerIdentity(Identity)}, this method sets it. If
* {@code cachedIdentity} was already set, either by calling
* {@link #registerIdentity(Identity)} or by a previous call to this
* method, then this method returns the cached identity without hitting
* the database.
*/
private Identity getCachedIdentity(Transaction txn) throws DbException {
Identity cached = cachedIdentity;
if (cached == null)
cachedIdentity = cached = loadIdentityWithKeyPair(txn);
return cached; return cached;
} }
/**
* Loads and returns the identity, generating a handshake key pair if @Override
* necessary and setting {@code shouldStoreKeys} if a handshake key pair public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
* was generated. if (cachedAuthor == null) {
*/ cachedAuthor = loadLocalAuthor(txn);
private Identity loadIdentityWithKeyPair(Transaction txn) LOG.info("Local author loaded");
throws DbException { }
Collection<Identity> identities = db.getIdentities(txn); LocalAuthor cached = cachedAuthor;
if (identities.size() != 1) throw new DbException(); if (cached == null) throw new AssertionError();
Identity i = identities.iterator().next(); return cached;
LOG.info("Identity loaded");
if (i.hasHandshakeKeyPair()) return i;
KeyPair handshakeKeyPair = crypto.generateAgreementKeyPair();
byte[] handshakePub = handshakeKeyPair.getPublic().getEncoded();
byte[] handshakePriv = handshakeKeyPair.getPrivate().getEncoded();
LOG.info("Handshake key pair generated");
shouldStoreKeys = true;
return new Identity(i.getLocalAuthor(), handshakePub, handshakePriv,
i.getTimeCreated());
} }
private LocalAuthor loadLocalAuthor(Transaction txn) throws DbException {
return db.getLocalAuthors(txn).iterator().next();
}
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -25,9 +24,8 @@ public class IdentityModule {
@Provides @Provides
@Singleton @Singleton
IdentityManager provideIdentityManager(LifecycleManager lifecycleManager, IdentityManager provideIdentityManager(
IdentityManagerImpl identityManager) { IdentityManagerImpl identityManager) {
lifecycleManager.registerOpenDatabaseHook(identityManager);
return identityManager; return identityManager;
} }
} }

View File

@@ -1,13 +1,13 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.keyagreement.TransportDescriptor; import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants;

View File

@@ -7,11 +7,13 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent; import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -26,7 +28,6 @@ import javax.inject.Inject;
import static java.util.logging.Level.FINE; import static java.util.logging.Level.FINE;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
@@ -48,13 +49,14 @@ import static org.briarproject.bramble.util.LogUtils.now;
class LifecycleManagerImpl implements LifecycleManager, MigrationListener { class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
private static final Logger LOG = private static final Logger LOG =
getLogger(LifecycleManagerImpl.class.getName()); Logger.getLogger(LifecycleManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final EventBus eventBus; private final EventBus eventBus;
private final List<Service> services; private final List<Service> services;
private final List<OpenDatabaseHook> openDatabaseHooks; private final List<Client> clients;
private final List<ExecutorService> executors; private final List<ExecutorService> executors;
private final IdentityManager identityManager;
private final Semaphore startStopSemaphore = new Semaphore(1); private final Semaphore startStopSemaphore = new Semaphore(1);
private final CountDownLatch dbLatch = new CountDownLatch(1); private final CountDownLatch dbLatch = new CountDownLatch(1);
private final CountDownLatch startupLatch = new CountDownLatch(1); private final CountDownLatch startupLatch = new CountDownLatch(1);
@@ -63,11 +65,13 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
private volatile LifecycleState state = STARTING; private volatile LifecycleState state = STARTING;
@Inject @Inject
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus) { LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
IdentityManager identityManager) {
this.db = db; this.db = db;
this.eventBus = eventBus; this.eventBus = eventBus;
this.identityManager = identityManager;
services = new CopyOnWriteArrayList<>(); services = new CopyOnWriteArrayList<>();
openDatabaseHooks = new CopyOnWriteArrayList<>(); clients = new CopyOnWriteArrayList<>();
executors = new CopyOnWriteArrayList<>(); executors = new CopyOnWriteArrayList<>();
} }
@@ -79,12 +83,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
} }
@Override @Override
public void registerOpenDatabaseHook(OpenDatabaseHook hook) { public void registerClient(Client c) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO))
LOG.info("Registering open database hook " LOG.info("Registering client " + c.getClass().getSimpleName());
+ hook.getClass().getSimpleName()); clients.add(c);
}
openDatabaseHooks.add(hook);
} }
@Override @Override
@@ -100,28 +102,28 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
return ALREADY_RUNNING; return ALREADY_RUNNING;
} }
try { try {
LOG.info("Opening database"); LOG.info("Starting services");
long start = now(); long start = now();
boolean reopened = db.open(dbKey, this); boolean reopened = db.open(dbKey, this);
if (reopened) logDuration(LOG, "Reopening database", start); if (reopened) logDuration(LOG, "Reopening database", start);
else logDuration(LOG, "Creating database", start); else logDuration(LOG, "Creating database", start);
identityManager.storeLocalAuthor();
db.transaction(false, txn -> {
for (OpenDatabaseHook hook : openDatabaseHooks) {
long start1 = now();
hook.onDatabaseOpened(txn);
if (LOG.isLoggable(FINE)) {
logDuration(LOG, "Calling open database hook "
+ hook.getClass().getSimpleName(), start1);
}
}
});
LOG.info("Starting services");
state = STARTING_SERVICES; state = STARTING_SERVICES;
dbLatch.countDown(); dbLatch.countDown();
eventBus.broadcast(new LifecycleEvent(STARTING_SERVICES)); eventBus.broadcast(new LifecycleEvent(STARTING_SERVICES));
db.transaction(false, txn -> {
for (Client c : clients) {
long start1 = now();
c.createLocalState(txn);
if (LOG.isLoggable(FINE)) {
logDuration(LOG, "Starting client "
+ c.getClass().getSimpleName(), start1);
}
}
});
for (Service s : services) { for (Service s : services) {
start = now(); start = now();
s.startService(); s.startService();

View File

@@ -8,6 +8,7 @@ import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -29,7 +30,10 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@@ -38,6 +42,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -59,11 +64,15 @@ class PluginManagerImpl implements PluginManager, Service {
Logger.getLogger(PluginManagerImpl.class.getName()); Logger.getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor; private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus; private final EventBus eventBus;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Map<TransportId, Plugin> plugins; private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins; private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins; private final List<DuplexPlugin> duplexPlugins;
@@ -71,25 +80,41 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@Inject @Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus, PluginManagerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager, PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager, SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager) { TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.eventBus = eventBus; this.eventBus = eventBus;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
plugins = new ConcurrentHashMap<>(); plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>(); simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>(); duplexPlugins = new CopyOnWriteArrayList<>();
startLatches = new ConcurrentHashMap<>(); startLatches = new ConcurrentHashMap<>();
} }
@Override @Override
public void startService() { public void startService() {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
// Instantiate the poller
if (pluginConfig.shouldPoll()) {
LOG.info("Starting poller");
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, this, transportPropertyManager, random,
clock);
eventBus.addListener(poller);
}
// Instantiate the simplex plugins and start them asynchronously // Instantiate the simplex plugins and start them asynchronously
LOG.info("Starting simplex plugins"); LOG.info("Starting simplex plugins");
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) { for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {

View File

@@ -1,11 +1,9 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory; import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry; import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
import javax.inject.Inject; import javax.inject.Inject;
@@ -20,8 +18,6 @@ public class PluginModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
PluginManager pluginManager; PluginManager pluginManager;
@Inject
Poller poller;
} }
@Provides @Provides
@@ -50,12 +46,4 @@ public class PluginModule {
lifecycleManager.registerService(pluginManager); lifecycleManager.registerService(pluginManager);
return pluginManager; return pluginManager;
} }
@Provides
@Singleton
Poller providePoller(PluginConfig config, EventBus eventBus,
PollerImpl poller) {
if (config.shouldPoll()) eventBus.addListener(poller);
return poller;
}
} }

View File

@@ -1,7 +1,259 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
/** import org.briarproject.bramble.api.contact.ContactId;
* Empty interface used for injecting the poller. import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
*/ import org.briarproject.bramble.api.db.DbException;
interface Poller { import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class Poller implements EventListener {
private static final Logger LOG = Logger.getLogger(Poller.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Lock lock;
private final Map<TransportId, ScheduledPollTask> tasks; // Locking: lock
Poller(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
lock = new ReentrantLock();
tasks = new HashMap<>();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e;
// Connect to the newly activated contact
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly enabled transport
pollNow(t.getTransportId());
} else if (e instanceof TransportDisabledEvent) {
TransportDisabledEvent t = (TransportDisabledEvent) e;
// Cancel polling for the disabled transport
cancel(t.getTransportId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : pluginManager.getSimplexPlugins())
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : pluginManager.getDuplexPlugins())
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p != null && p.shouldPoll())
schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
if (p != null && p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int delay, boolean randomiseNext) {
// Replace any later scheduled task for this plugin
long due = clock.currentTimeMillis() + delay;
TransportId t = p.getId();
lock.lock();
try {
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false);
PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(
() -> ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future));
}
} finally {
lock.unlock();
}
}
private void cancel(TransportId t) {
lock.lock();
try {
ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false);
} finally {
lock.unlock();
}
}
@IoExecutor
private void poll(Plugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class ScheduledPollTask {
private final PollTask task;
private final Future future;
private ScheduledPollTask(PollTask task, Future future) {
this.task = task;
this.future = future;
}
}
private class PollTask implements Runnable {
private final Plugin plugin;
private final long due;
private final boolean randomiseNext;
private PollTask(Plugin plugin, long due, boolean randomiseNext) {
this.plugin = plugin;
this.due = due;
this.randomiseNext = randomiseNext;
}
@Override
@IoExecutor
public void run() {
lock.lock();
try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this)
return; // Replaced by another task
tasks.remove(t);
} finally {
lock.unlock();
}
int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false);
poll(plugin);
}
}
} }

View File

@@ -1,264 +0,0 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.ConnectionClosedEvent;
import org.briarproject.bramble.api.plugin.event.ConnectionOpenedEvent;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@ThreadSafe
@NotNullByDefault
class PollerImpl implements Poller, EventListener {
private static final Logger LOG = getLogger(PollerImpl.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final PluginManager pluginManager;
private final TransportPropertyManager transportPropertyManager;
private final SecureRandom random;
private final Clock clock;
private final Lock lock;
@GuardedBy("lock")
private final Map<TransportId, ScheduledPollTask> tasks;
@Inject
PollerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
TransportPropertyManager transportPropertyManager,
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.pluginManager = pluginManager;
this.transportPropertyManager = transportPropertyManager;
this.random = random;
this.clock = clock;
lock = new ReentrantLock();
tasks = new HashMap<>();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e;
// Connect to the newly added contact
connectToContact(c.getContactId());
} else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
if (!c.isIncoming()) {
// Connect to the disconnected contact
connectToContact(c.getContactId(), c.getTransportId());
}
} else if (e instanceof ConnectionOpenedEvent) {
ConnectionOpenedEvent c = (ConnectionOpenedEvent) e;
// Reschedule polling, the polling interval may have decreased
reschedule(c.getTransportId());
} else if (e instanceof TransportEnabledEvent) {
TransportEnabledEvent t = (TransportEnabledEvent) e;
// Poll the newly enabled transport
pollNow(t.getTransportId());
} else if (e instanceof TransportDisabledEvent) {
TransportDisabledEvent t = (TransportDisabledEvent) e;
// Cancel polling for the disabled transport
cancel(t.getTransportId());
}
}
private void connectToContact(ContactId c) {
for (SimplexPlugin s : pluginManager.getSimplexPlugins())
if (s.shouldPoll()) connectToContact(c, s);
for (DuplexPlugin d : pluginManager.getDuplexPlugins())
if (d.shouldPoll()) connectToContact(c, d);
}
private void connectToContact(ContactId c, TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p instanceof SimplexPlugin && p.shouldPoll())
connectToContact(c, (SimplexPlugin) p);
else if (p instanceof DuplexPlugin && p.shouldPoll())
connectToContact(c, (DuplexPlugin) p);
}
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
TransportConnectionWriter w = p.createWriter(props);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (connectionRegistry.isConnected(c, t)) return;
try {
TransportProperties props =
transportPropertyManager.getRemoteProperties(c, t);
DuplexTransportConnection d = p.createConnection(props);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
private void reschedule(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
if (p != null && p.shouldPoll())
schedule(p, p.getPollingInterval(), false);
}
private void pollNow(TransportId t) {
Plugin p = pluginManager.getPlugin(t);
// Randomise next polling interval
if (p != null && p.shouldPoll()) schedule(p, 0, true);
}
private void schedule(Plugin p, int delay, boolean randomiseNext) {
// Replace any later scheduled task for this plugin
long due = clock.currentTimeMillis() + delay;
TransportId t = p.getId();
lock.lock();
try {
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.task.due) {
// If a later task exists, cancel it. If it's already started
// it will abort safely when it finds it's been replaced
if (scheduled != null) scheduled.future.cancel(false);
PollTask task = new PollTask(p, due, randomiseNext);
Future future = scheduler.schedule(() ->
ioExecutor.execute(task), delay, MILLISECONDS);
tasks.put(t, new ScheduledPollTask(task, future));
}
} finally {
lock.unlock();
}
}
private void cancel(TransportId t) {
lock.lock();
try {
ScheduledPollTask scheduled = tasks.remove(t);
if (scheduled != null) scheduled.future.cancel(false);
} finally {
lock.unlock();
}
}
@IoExecutor
private void poll(Plugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
try {
Map<ContactId, TransportProperties> remote =
transportPropertyManager.getRemoteProperties(t);
Collection<ContactId> connected =
connectionRegistry.getConnectedContacts(t);
remote = new HashMap<>(remote);
remote.keySet().removeAll(connected);
if (!remote.isEmpty()) p.poll(remote);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
private class ScheduledPollTask {
private final PollTask task;
private final Future future;
private ScheduledPollTask(PollTask task, Future future) {
this.task = task;
this.future = future;
}
}
private class PollTask implements Runnable {
private final Plugin plugin;
private final long due;
private final boolean randomiseNext;
private PollTask(Plugin plugin, long due, boolean randomiseNext) {
this.plugin = plugin;
this.due = due;
this.randomiseNext = randomiseNext;
}
@Override
@IoExecutor
public void run() {
lock.lock();
try {
TransportId t = plugin.getId();
ScheduledPollTask scheduled = tasks.get(t);
if (scheduled != null && scheduled.task != this)
return; // Replaced by another task
tasks.remove(t);
} finally {
lock.unlock();
}
int delay = plugin.getPollingInterval();
if (randomiseNext) delay = (int) (delay * random.nextDouble());
schedule(plugin, delay, false);
poll(plugin);
}
}
}

View File

@@ -48,7 +48,7 @@ public class PropertiesModule {
ValidationManager validationManager, ContactManager contactManager, ValidationManager validationManager, ContactManager contactManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
TransportPropertyManagerImpl transportPropertyManager) { TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerOpenDatabaseHook(transportPropertyManager); lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
transportPropertyManager); transportPropertyManager);
contactManager.registerContactHook(transportPropertyManager); contactManager.registerContactHook(transportPropertyManager);

View File

@@ -13,11 +13,13 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.Group.Visibility;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
@@ -40,11 +42,11 @@ import javax.inject.Inject;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
OpenDatabaseHook, ContactHook, ClientVersioningHook, Client, ContactHook, ClientVersioningHook, IncomingMessageHook {
IncomingMessageHook {
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ClientVersioningManager clientVersioningManager; private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser; private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
@@ -54,11 +56,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, ClientHelper clientHelper,
IdentityManager identityManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.clientVersioningManager = clientVersioningManager; this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
@@ -68,7 +72,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
@Override @Override
public void onDatabaseOpened(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts // Set things up for any pre-existing contacts
@@ -78,7 +82,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.addGroup(txn, g); db.addGroup(txn, g);
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn, Visibility client = clientVersioningManager.getClientVisibility(txn,
@@ -94,14 +98,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
} }
@Override @Override
public void onClientVisibilityChanging(Transaction txn, Contact c, public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException { Visibility v) throws DbException {
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.setGroupVisibility(txn, c.getId(), g.getId(), v); db.setGroupVisibility(txn, c.getId(), g.getId(), v);
} }
@@ -133,7 +137,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addRemoteProperties(Transaction txn, ContactId c, public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException { Map<TransportId, TransportProperties> props) throws DbException {
Group g = getContactGroup(db.getContact(txn, c)); Group g = getContactGroup(db.getContact(txn, c), getLocalAuthorId(txn));
for (Entry<TransportId, TransportProperties> e : props.entrySet()) { for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0, storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0,
false, false); false, false);
@@ -192,15 +196,16 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
TransportId t) throws DbException { TransportId t) throws DbException {
return db.transactionWithResult(true, txn -> { return db.transactionWithResult(true, txn -> {
Map<ContactId, TransportProperties> remote = new HashMap<>(); Map<ContactId, TransportProperties> remote = new HashMap<>();
AuthorId local = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) for (Contact c : db.getContacts(txn))
remote.put(c.getId(), getRemoteProperties(txn, c, t)); remote.put(c.getId(), getRemoteProperties(txn, c, local, t));
return remote; return remote;
}); });
} }
private TransportProperties getRemoteProperties(Transaction txn, Contact c, private TransportProperties getRemoteProperties(Transaction txn, Contact c,
TransportId t) throws DbException { AuthorId local, TransportId t) throws DbException {
Group g = getContactGroup(c); Group g = getContactGroup(c, local);
try { try {
// Find the latest remote update // Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false); LatestUpdate latest = findLatest(txn, g.getId(), t, false);
@@ -217,8 +222,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public TransportProperties getRemoteProperties(ContactId c, TransportId t) public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException { throws DbException {
return db.transactionWithResult(true, txn -> return db.transactionWithResult(true, txn -> {
getRemoteProperties(txn, db.getContact(txn, c), t)); Contact contact = db.getContact(txn ,c);
AuthorId local = getLocalAuthorId(txn);
return getRemoteProperties(txn, contact, local, t);
});
} }
@Override @Override
@@ -251,8 +259,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
if (latest != null) if (latest != null)
db.removeMessage(txn, latest.messageId); db.removeMessage(txn, latest.messageId);
// Store the merged properties in each contact's group // Store the merged properties in each contact's group
AuthorId localAuthorId = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c); Group g = getContactGroup(c, localAuthorId);
latest = findLatest(txn, g.getId(), t, true); latest = findLatest(txn, g.getId(), t, true);
version = latest == null ? 1 : latest.version + 1; version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version, storeMessage(txn, g.getId(), t, merged, version,
@@ -268,9 +277,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
private Group getContactGroup(Contact c) { private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c); MAJOR_VERSION, c, local);
} }
private void storeMessage(Transaction txn, GroupId g, TransportId t, private void storeMessage(Transaction txn, GroupId g, TransportId t,

View File

@@ -12,10 +12,12 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.Group.Visibility;
@@ -53,11 +55,12 @@ import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_
import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION; import static org.briarproject.bramble.versioning.ClientVersioningConstants.MSG_KEY_UPDATE_VERSION;
@NotNullByDefault @NotNullByDefault
class ClientVersioningManagerImpl implements ClientVersioningManager, class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
Service, OpenDatabaseHook, ContactHook, IncomingMessageHook { Service, ContactHook, IncomingMessageHook {
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final Clock clock; private final Clock clock;
private final Group localGroup; private final Group localGroup;
@@ -68,9 +71,11 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Inject @Inject
ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper, ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
IdentityManager identityManager,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
@@ -124,7 +129,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
@Override @Override
public void onDatabaseOpened(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
// Set things up for any pre-existing contacts // Set things up for any pre-existing contacts
@@ -154,7 +159,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group and share it with the contact // Create a group and share it with the contact
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
@@ -173,7 +178,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
} }
@Override @Override
@@ -308,7 +313,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
List<ClientVersion> versions) throws DbException { List<ClientVersion> versions) throws DbException {
try { try {
// Find the latest local and remote updates // Find the latest local and remote updates
Group g = getContactGroup(c); Group g = getContactGroup(c, getLocalAuthorId(txn));
LatestUpdates latest = findLatestUpdates(txn, g.getId()); LatestUpdates latest = findLatestUpdates(txn, g.getId());
// Load and parse the latest local update // Load and parse the latest local update
if (latest.local == null) throw new DbException(); if (latest.local == null) throw new DbException();
@@ -344,16 +349,20 @@ class ClientVersioningManagerImpl implements ClientVersioningManager,
} }
} }
private Group getContactGroup(Contact c) { private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c); MAJOR_VERSION, c, local);
} }
@Nullable @Nullable
private LatestUpdates findLatestUpdates(Transaction txn, ContactId c) private LatestUpdates findLatestUpdates(Transaction txn, ContactId c)
throws DbException, FormatException { throws DbException, FormatException {
Contact contact = db.getContact(txn, c); Contact contact = db.getContact(txn, c);
Group g = getContactGroup(contact); Group g = getContactGroup(contact, getLocalAuthorId(txn));
// Contact may be in the process of being added or removed, so // Contact may be in the process of being added or removed, so
// contact group may not exist // contact group may not exist
if (!db.containsGroup(txn, g.getId())) return null; if (!db.containsGroup(txn, g.getId())) return null;

View File

@@ -34,7 +34,7 @@ public class VersioningModule {
ClientVersioningManagerImpl clientVersioningManager, ClientVersioningManagerImpl clientVersioningManager,
LifecycleManager lifecycleManager, ContactManager contactManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager) { ValidationManager validationManager) {
lifecycleManager.registerOpenDatabaseHook(clientVersioningManager); lifecycleManager.registerClient(clientVersioningManager);
lifecycleManager.registerService(clientVersioningManager); lifecycleManager.registerService(clientVersioningManager);
contactManager.registerContactHook(clientVersioningManager); contactManager.registerContactHook(clientVersioningManager);
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION, validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.account;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
@@ -25,7 +24,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -48,8 +47,7 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
private final String encryptedKeyHex = toHexString(encryptedKey); private final String encryptedKeyHex = toHexString(encryptedKey);
private final byte[] newEncryptedKey = getRandomBytes(123); private final byte[] newEncryptedKey = getRandomBytes(123);
private final String newEncryptedKeyHex = toHexString(newEncryptedKey); private final String newEncryptedKeyHex = toHexString(newEncryptedKey);
private final Identity identity = getIdentity(); private final LocalAuthor localAuthor = getLocalAuthor();
private final LocalAuthor localAuthor = identity.getLocalAuthor();
private final String authorName = localAuthor.getName(); private final String authorName = localAuthor.getName();
private final String password = getRandomString(10); private final String password = getRandomString(10);
private final String newPassword = getRandomString(10); private final String newPassword = getRandomString(10);
@@ -253,9 +251,9 @@ public class AccountManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testCreateAccountStoresDbKey() throws Exception { public void testCreateAccountStoresDbKey() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(identityManager).createIdentity(authorName); oneOf(identityManager).createLocalAuthor(authorName);
will(returnValue(identity)); will(returnValue(localAuthor));
oneOf(identityManager).registerIdentity(identity); oneOf(identityManager).registerLocalAuthor(localAuthor);
oneOf(crypto).generateSecretKey(); oneOf(crypto).generateSecretKey();
will(returnValue(key)); will(returnValue(key));
oneOf(crypto).encryptWithPassword(key.getBytes(), password); oneOf(crypto).encryptWithPassword(key.getBytes(), password);

View File

@@ -5,25 +5,20 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo; import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import java.util.Collection; import java.util.Collection;
import java.util.Random; import java.util.Random;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
@@ -33,7 +28,6 @@ import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -47,19 +41,16 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final KeyManager keyManager = context.mock(KeyManager.class); private final KeyManager keyManager = context.mock(KeyManager.class);
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
private final PendingContactFactory pendingContactFactory =
context.mock(PendingContactFactory.class);
private final ContactManager contactManager; private final ContactManager contactManager;
private final Author remote = getAuthor(); private final Author author = getAuthor();
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private final AuthorId local = localAuthor.getId();
private final boolean verified = false, active = true; private final boolean verified = false, active = true;
private final Contact contact = getContact(remote, local, verified); private final Contact contact = getContact(author, verified);
private final ContactId contactId = contact.getId(); private final ContactId contactId = contact.getId();
public ContactManagerImplTest() { public ContactManagerImplTest() {
contactManager = new ContactManagerImpl(db, keyManager, contactManager =
identityManager, pendingContactFactory); new ContactManagerImpl(db, keyManager, identityManager);
} }
@Test @Test
@@ -71,7 +62,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, remote, local, verified); oneOf(db).addContact(txn, author, verified);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp, oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp,
alice, active); alice, active);
@@ -79,12 +70,12 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
will(returnValue(contact)); will(returnValue(contact));
}}); }});
assertEquals(contactId, contactManager.addContact(remote, local, assertEquals(contactId, contactManager.addContact(author, rootKey,
rootKey, timestamp, alice, verified, active)); timestamp, alice, verified, active));
} }
@Test @Test
public void testGetContact() throws Exception { public void testGetContactByContactId() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
@@ -96,41 +87,15 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testGetContactByAuthor() throws Exception { public void testGetContactByAuthorId() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
assertEquals(contact, contactManager.getContact(remote.getId(), local));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId()); oneOf(db).getContact(txn, author.getId());
will(returnValue(emptyList())); will(returnValue(contact));
}}); }});
contactManager.getContact(remote.getId(), local); assertEquals(contact, contactManager.getContact(author.getId()));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
contactManager.getContact(remote.getId(), new AuthorId(getRandomId()));
} }
@Test @Test
@@ -184,78 +149,82 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).containsContact(txn, remote.getId(), local); oneOf(db).containsContact(txn, author.getId());
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(contactManager.contactExists(remote.getId(), local)); assertTrue(contactManager.contactExists(author.getId()));
} }
@Test @Test
public void testGetAuthorInfo() throws Exception { public void testGetAuthorInfoOurselves() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(singletonList(contact)));
}}); }});
AuthorInfo authorInfo = AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId()); contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(contact.getAlias(), authorInfo.getAlias());
}
@Test
public void testGetAuthorInfoTransaction() throws DbException {
Transaction txn = new Transaction(null, true);
// check unknown author
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(emptyList()));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
// check unverified contact
checkAuthorInfoContext(txn, remote.getId(), singletonList(contact));
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(contact.getAlias(), authorInfo.getAlias());
// check verified contact
Contact verified = getContact(remote, local, true);
checkAuthorInfoContext(txn, remote.getId(), singletonList(verified));
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(verified.getAlias(), authorInfo.getAlias());
// check ourselves
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
never(db).getContactsByAuthorId(txn, remote.getId());
}});
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(OURSELVES, authorInfo.getStatus()); assertEquals(OURSELVES, authorInfo.getStatus());
assertNull(authorInfo.getAlias()); assertNull(authorInfo.getAlias());
} }
private void checkAuthorInfoContext(Transaction txn, AuthorId authorId, @Test
Collection<Contact> contacts) throws DbException { public void testGetAuthorInfoVerified() throws Exception {
context.checking(new Expectations() {{ Transaction txn = new Transaction(null, true);
Contact verified = getContact(author, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, authorId); oneOf(db).containsContact(txn, author.getId());
will(returnValue(contacts)); will(returnValue(true));
oneOf(db).getContact(txn, author.getId());
will(returnValue(verified));
}}); }});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(verified.getAlias(), authorInfo.getAlias());
} }
@Test
public void testGetAuthorInfoUnverified() throws Exception {
Transaction txn = new Transaction(null, true);
Contact unverified = getContact(author, false);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(true));
oneOf(db).getContact(txn, author.getId());
will(returnValue(unverified));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(unverified.getAlias(), authorInfo.getAlias());
}
@Test
public void testGetAuthorInfoUnknown() throws Exception {
Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(false));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
}
} }

View File

@@ -1,129 +0,0 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.util.Base32;
import org.jmock.Expectations;
import org.junit.Test;
import java.security.GeneralSecurityException;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.BASE32_LINK_BYTES;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.FORMAT_VERSION;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.ID_LABEL;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.RAW_LINK_BYTES;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
public class PendingContactFactoryImplTest extends BrambleMockTestCase {
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final Clock clock = context.mock(Clock.class);
private final KeyParser keyParser = context.mock(KeyParser.class);
private final PublicKey publicKey = context.mock(PublicKey.class);
private final PendingContactFactory pendingContactFactory =
new PendingContactFactoryImpl(crypto, clock);
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final byte[] publicKeyBytes = getRandomBytes(RAW_LINK_BYTES - 1);
private final byte[] idBytes = getRandomId();
private final long timestamp = System.currentTimeMillis();
@Test(expected = FormatException.class)
public void testRejectsSyntacticallyInvalidLink() throws Exception {
pendingContactFactory.createPendingContact("briar://potato", alias);
}
@Test
public void testRejectsLinkWithUnknownFormatVersion() throws Exception {
String link = encodeLink(FORMAT_VERSION + 1);
try {
pendingContactFactory.createPendingContact(link, alias);
fail();
} catch (UnsupportedVersionException e) {
assertFalse(e.isTooOld());
}
}
@Test(expected = FormatException.class)
public void testRejectsLinkWithInvalidPublicKey() throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
will(throwException(new GeneralSecurityException()));
}});
pendingContactFactory.createPendingContact(encodeLink(), alias);
}
@Test
public void testAcceptsValidLinkWithoutPrefix() throws Exception {
testAcceptsValidLink(encodeLink());
}
@Test
public void testAcceptsValidLinkWithPrefix() throws Exception {
testAcceptsValidLink("briar://" + encodeLink());
}
@Test
public void testAcceptsValidLinkWithRubbish() throws Exception {
testAcceptsValidLink("before " + encodeLink() + " after");
}
@Test
public void testAcceptsValidLinkWithPrefixAndRubbish() throws Exception {
testAcceptsValidLink("before briar://" + encodeLink() + " after");
}
private void testAcceptsValidLink(String link) throws Exception {
context.checking(new Expectations() {{
oneOf(crypto).getAgreementKeyParser();
will(returnValue(keyParser));
oneOf(keyParser).parsePublicKey(with(equal(publicKeyBytes)));
will(returnValue(publicKey));
allowing(publicKey).getEncoded();
will(returnValue(publicKeyBytes));
oneOf(crypto).hash(ID_LABEL, publicKeyBytes);
will(returnValue(idBytes));
oneOf(clock).currentTimeMillis();
will(returnValue(timestamp));
}});
PendingContact p =
pendingContactFactory.createPendingContact(link, alias);
assertArrayEquals(idBytes, p.getId().getBytes());
assertArrayEquals(publicKeyBytes, p.getPublicKey());
assertEquals(alias, p.getAlias());
assertEquals(WAITING_FOR_CONNECTION, p.getState());
assertEquals(timestamp, p.getTimestamp());
}
private String encodeLink() {
return encodeLink(FORMAT_VERSION);
}
private String encodeLink(int formatVersion) {
byte[] rawLink = new byte[RAW_LINK_BYTES];
rawLink[0] = (byte) formatVersion;
arraycopy(publicKeyBytes, 0, rawLink, 1, publicKeyBytes.length);
String base32 = Base32.encode(rawLink).toLowerCase();
assertEquals(BASE32_LINK_BYTES, base32.length());
return base32;
}
}

View File

@@ -11,17 +11,16 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchIdentityException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException; import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.identity.event.IdentityAddedEvent; import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent;
import org.briarproject.bramble.api.identity.event.IdentityRemovedEvent; import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -66,7 +65,6 @@ import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap; import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
@@ -79,9 +77,8 @@ import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -107,7 +104,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final GroupId groupId; private final GroupId groupId;
private final Group group; private final Group group;
private final Author author; private final Author author;
private final Identity identity;
private final LocalAuthor localAuthor; private final LocalAuthor localAuthor;
private final String alias; private final String alias;
private final Message message, message1; private final Message message, message1;
@@ -126,8 +122,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
group = getGroup(clientId, majorVersion); group = getGroup(clientId, majorVersion);
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
identity = getIdentity(); localAuthor = getLocalAuthor();
localAuthor = identity.getLocalAuthor();
message = getMessage(groupId); message = getMessage(groupId);
message1 = getMessage(groupId); message1 = getMessage(groupId);
messageId = message.getId(); messageId = message.getId();
@@ -136,7 +131,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
metadata.put("foo", new byte[] {'b', 'a', 'r'}); metadata.put("foo", new byte[] {'b', 'a', 'r'});
transportId = getTransportId(); transportId = getTransportId();
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
contact = getContact(author, localAuthor.getId(), true); contact = getContact(author, true);
contactId = contact.getId(); contactId = contact.getId();
alias = contact.getAlias(); alias = contact.getAlias();
keySetId = new TransportKeySetId(345); keySetId = new TransportKeySetId(345);
@@ -162,20 +157,17 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// startTransaction() // startTransaction()
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
// addIdentity() // registerLocalAuthor()
oneOf(database).containsIdentity(txn, localAuthor.getId()); oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).addIdentity(txn, identity); oneOf(database).addLocalAuthor(txn, localAuthor);
oneOf(eventBus).broadcast(with(any(IdentityAddedEvent.class))); oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class)));
// addContact() // addContact()
oneOf(database).containsIdentity(txn, localAuthor.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(true));
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).containsContact(txn, author.getId(), oneOf(database).containsContact(txn, author.getId());
localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, localAuthor.getId(), true); oneOf(database).addContact(txn, author, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
// getContacts() // getContacts()
@@ -206,11 +198,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(database).removeContact(txn, contactId); oneOf(database).removeContact(txn, contactId);
oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class)));
// removeIdentity() // removeLocalAuthor()
oneOf(database).containsIdentity(txn, localAuthor.getId()); oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(database).removeIdentity(txn, localAuthor.getId()); oneOf(database).removeLocalAuthor(txn, localAuthor.getId());
oneOf(eventBus).broadcast(with(any(IdentityRemovedEvent.class))); oneOf(eventBus).broadcast(with(any(LocalAuthorRemovedEvent.class)));
// endTransaction() // endTransaction()
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// close() // close()
@@ -221,9 +213,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.addIdentity(transaction, identity); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, db.addContact(transaction, author, assertEquals(contactId, db.addContact(transaction, author, true));
localAuthor.getId(), true));
assertEquals(singletonList(contact), assertEquals(singletonList(contact),
db.getContacts(transaction)); db.getContacts(transaction));
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
@@ -232,7 +223,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
db.getGroups(transaction, clientId, majorVersion)); db.getGroups(transaction, clientId, majorVersion));
db.removeGroup(transaction, group); db.removeGroup(transaction, group);
db.removeContact(transaction, contactId); db.removeContact(transaction, contactId);
db.removeIdentity(transaction, localAuthor.getId()); db.removeLocalAuthor(transaction, localAuthor.getId());
}); });
db.close(); db.close();
} }
@@ -284,11 +275,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(17).of(database).startTransaction(); exactly(18).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
exactly(17).of(database).abortTransaction(txn); oneOf(database).containsContact(txn, author.getId());
will(returnValue(false));
exactly(18).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
@@ -351,6 +344,14 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.getContact(transaction, author.getId()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.getMessageStatus(transaction, contactId, groupId)); db.getMessageStatus(transaction, contactId, groupId));
@@ -437,52 +438,33 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testVariousMethodsThrowExceptionIfIdentityIsMissing() public void testVariousMethodsThrowExceptionIfLocalAuthorIsMissing()
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the identity is in the DB (which it's not) // Check whether the pseudonym is in the DB (which it's not)
exactly(4).of(database).startTransaction(); exactly(2).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(4).of(database).containsIdentity(txn, localAuthor.getId()); exactly(2).of(database).containsLocalAuthor(txn,
localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
exactly(4).of(database).abortTransaction(txn); exactly(2).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); eventExecutor, shutdownManager);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.getLocalAuthor(transaction, localAuthor.getId()));
true));
fail(); fail();
} catch (NoSuchIdentityException expected) { } catch (NoSuchLocalAuthorException expected) {
// Expected // Expected
} }
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.getIdentity(transaction, localAuthor.getId())); db.removeLocalAuthor(transaction, localAuthor.getId()));
fail(); fail();
} catch (NoSuchIdentityException expected) { } catch (NoSuchLocalAuthorException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.removeIdentity(transaction, localAuthor.getId()));
fail();
} catch (NoSuchIdentityException expected) {
// Expected
}
try {
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] privateKey = getRandomBytes(123);
db.transaction(false, transaction ->
db.setHandshakeKeyPair(transaction, localAuthor.getId(),
publicKey, privateKey));
fail();
} catch (NoSuchIdentityException expected) {
// Expected // Expected
} }
} }
@@ -1418,10 +1400,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsIdentity(txn, localAuthor.getId());
will(returnValue(true));
// Contact is a local identity // Contact is a local identity
oneOf(database).containsIdentity(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
@@ -1431,8 +1411,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, true));
true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1444,13 +1423,10 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsIdentity(txn, localAuthor.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(true));
oneOf(database).containsIdentity(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
// Contact already exists for this local identity // Contact already exists
oneOf(database).containsContact(txn, author.getId(), oneOf(database).containsContact(txn, author.getId());
localAuthor.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
@@ -1460,8 +1436,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(), db.addContact(transaction, author, true));
true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1469,6 +1444,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
@Test @Test
@SuppressWarnings("unchecked")
public void testMessageDependencies() throws Exception { public void testMessageDependencies() throws Exception {
int shutdownHandle = 12345; int shutdownHandle = 12345;
MessageId messageId2 = new MessageId(getRandomId()); MessageId messageId2 = new MessageId(getRandomId());

View File

@@ -4,8 +4,6 @@ import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -38,7 +36,7 @@ import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERE
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes; import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
@@ -132,11 +130,10 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
@Test @Test
public void testContainsContactByAuthorId() throws Exception { public void testContainsContactByAuthorId() throws Exception {
String name = "containsContact(T, AuthorId, AuthorId)"; String name = "containsContact(T, AuthorId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
AuthorId remote = pickRandom(contacts).getAuthor().getId(); db.containsContact(txn, pickRandom(contacts).getAuthor().getId());
db.containsContact(txn, remote, localAuthor.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@@ -162,11 +159,11 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
@Test @Test
public void testContainsIdentity() throws Exception { public void testContainsLocalAuthor() throws Exception {
String name = "containsIdentity(T, AuthorId)"; String name = "containsLocalAuthor(T, AuthorId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.containsIdentity(txn, localAuthor.getId()); db.containsLocalAuthor(txn, localAuthor.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@@ -203,7 +200,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
@Test @Test
public void testGetContact() throws Exception { public void testGetContactByContactId() throws Exception {
String name = "getContact(T, ContactId)"; String name = "getContact(T, ContactId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
@@ -212,6 +209,16 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
}); });
} }
@Test
public void testGetContactByAuthorId() throws Exception {
String name = "getContact(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
db.getContact(txn, pickRandom(contacts).getAuthor().getId());
db.commitTransaction(txn);
});
}
@Test @Test
public void testGetContacts() throws Exception { public void testGetContacts() throws Exception {
String name = "getContacts(T)"; String name = "getContacts(T)";
@@ -222,27 +229,6 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
}); });
} }
@Test
public void testGetContactsByRemoteAuthorId() throws Exception {
String name = "getContactsByAuthorId(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
AuthorId remote = pickRandom(contacts).getAuthor().getId();
db.getContactsByAuthorId(txn, remote);
db.commitTransaction(txn);
});
}
@Test
public void testGetContactsByLocalAuthorId() throws Exception {
String name = "getContacts(T, AuthorId)";
benchmark(name, db -> {
Connection txn = db.startTransaction();
db.getContacts(txn, localAuthor.getId());
db.commitTransaction(txn);
});
}
@Test @Test
public void testGetGroup() throws Exception { public void testGetGroup() throws Exception {
String name = "getGroup(T, GroupId)"; String name = "getGroup(T, GroupId)";
@@ -296,21 +282,21 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
@Test @Test
public void testGetIdentity() throws Exception { public void testGetLocalAuthor() throws Exception {
String name = "getIdentity(T, AuthorId)"; String name = "getLocalAuthor(T, AuthorId)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.getIdentity(txn, localAuthor.getId()); db.getLocalAuthor(txn, localAuthor.getId());
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@Test @Test
public void testGetIdentities() throws Exception { public void testGetLocalAuthors() throws Exception {
String name = "getIdentities(T)"; String name = "getLocalAuthors(T)";
benchmark(name, db -> { benchmark(name, db -> {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.getIdentities(txn); db.getLocalAuthors(txn);
db.commitTransaction(txn); db.commitTransaction(txn);
}); });
} }
@@ -532,8 +518,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
} }
void populateDatabase(Database<Connection> db) throws DbException { void populateDatabase(Database<Connection> db) throws DbException {
Identity identity = getIdentity(); localAuthor = getLocalAuthor();
localAuthor = identity.getLocalAuthor();
clientIds = new ArrayList<>(); clientIds = new ArrayList<>();
contacts = new ArrayList<>(); contacts = new ArrayList<>();
groups = new ArrayList<>(); groups = new ArrayList<>();
@@ -545,10 +530,9 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
for (int i = 0; i < CLIENTS; i++) clientIds.add(getClientId()); for (int i = 0; i < CLIENTS; i++) clientIds.add(getClientId());
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
db.addIdentity(txn, identity); db.addLocalAuthor(txn, localAuthor);
for (int i = 0; i < CONTACTS; i++) { for (int i = 0; i < CONTACTS; i++) {
ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(), ContactId c = db.addContact(txn, getAuthor(), random.nextBoolean());
random.nextBoolean());
contacts.add(db.getContact(txn, c)); contacts.add(db.getContact(txn, c));
contactGroups.put(c, new ArrayList<>()); contactGroups.put(c, new ArrayList<>());
for (int j = 0; j < GROUPS_PER_CONTACT; j++) { for (int j = 0; j < GROUPS_PER_CONTACT; j++) {

View File

@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException; import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
@@ -58,7 +57,6 @@ import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap; import static java.util.Collections.singletonMap;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED; import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
import static org.briarproject.bramble.api.crypto.CryptoConstants.MAX_AGREEMENT_PUBLIC_KEY_BYTES;
import static org.briarproject.bramble.api.db.Metadata.REMOVE; import static org.briarproject.bramble.api.db.Metadata.REMOVE;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE; import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
@@ -75,10 +73,9 @@ import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getPendingContact; import static org.briarproject.bramble.test.TestUtils.getPendingContact;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory; import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
@@ -87,7 +84,6 @@ import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -106,7 +102,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final int majorVersion; private final int majorVersion;
private final Group group; private final Group group;
private final Author author; private final Author author;
private final Identity identity;
private final LocalAuthor localAuthor; private final LocalAuthor localAuthor;
private final Message message; private final Message message;
private final MessageId messageId; private final MessageId messageId;
@@ -123,8 +118,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
group = getGroup(clientId, majorVersion); group = getGroup(clientId, majorVersion);
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
identity = getIdentity(); localAuthor = getLocalAuthor();
localAuthor = identity.getLocalAuthor();
message = getMessage(groupId); message = getMessage(groupId);
messageId = message.getId(); messageId = message.getId();
transportId = getTransportId(); transportId = getTransportId();
@@ -150,9 +144,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
db.addIdentity(txn, identity); db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, assertEquals(contactId, db.addContact(txn, author, true));
db.addContact(txn, author, localAuthor.getId(), true));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsGroup(txn, groupId)); assertFalse(db.containsGroup(txn, groupId));
db.addGroup(txn, group); db.addGroup(txn, group);
@@ -213,9 +206,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -244,9 +235,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared but unvalidated message // Add a contact, a shared group and a shared but unvalidated message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, UNKNOWN, true, null); db.addMessage(txn, message, UNKNOWN, true, null);
@@ -289,9 +278,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, an invisible group and a shared message // Add a contact, an invisible group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -340,9 +327,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and an unshared message // Add a contact, a shared group and an unshared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, false, null); db.addMessage(txn, message, DELIVERED, false, null);
@@ -371,17 +356,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
// The message is sendable, but too large to send // The message is sendable, but too large to send
Collection<MessageId> ids = Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
db.getMessagesToSend(txn, contactId, message.getRawLength() - 1, message.getRawLength() - 1, MAX_LATENCY);
MAX_LATENCY);
assertTrue(ids.isEmpty()); assertTrue(ids.isEmpty());
// The message is just the right size to send // The message is just the right size to send
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(), ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
@@ -398,9 +380,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a visible group // Add a contact and a visible group
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, false); db.addGroupVisibility(txn, contactId, groupId, false);
@@ -439,9 +419,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -571,9 +549,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a shared group // Add a contact and a shared group
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
@@ -591,9 +567,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
// The group is not in the database // The group is not in the database
assertFalse(db.containsVisibleMessage(txn, contactId, messageId)); assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
@@ -609,9 +583,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, an invisible group and a message // Add a contact, an invisible group and a message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -628,9 +600,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and a group // Add a contact and a group
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
@@ -680,9 +650,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(emptyList(), db.getTransportKeys(txn, transportId)); assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
// Add the contact, the transport and the transport keys // Add the contact, the transport and the transport keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1)); assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
@@ -781,9 +749,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId)); assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
// Add the contact, the transport and the handshake keys // Add the contact, the transport and the handshake keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId, assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys)); db.addHandshakeKeys(txn, contactId, keys));
@@ -934,9 +900,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -978,9 +942,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and handshake keys // Add the contact, transport and handshake keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId, assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys)); db.addHandshakeKeys(txn, contactId, keys));
@@ -1025,9 +987,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and transport keys // Add the contact, transport and transport keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys)); assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
@@ -1072,9 +1032,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add the contact, transport and handshake keys // Add the contact, transport and handshake keys
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author,true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addTransport(txn, transportId, 123); db.addTransport(txn, transportId, 123);
assertEquals(handshakeKeySetId, assertEquals(handshakeKeySetId,
db.addHandshakeKeys(txn, contactId, keys)); db.addHandshakeKeys(txn, contactId, keys));
@@ -1110,56 +1068,42 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
} }
@Test @Test
public void testGetContactsByAuthorId() throws Exception { public void testGetContactByContactId() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add an identity for a local author - no contacts should be // Add a contact
// associated assertEquals(contactId, db.addContact(txn, author, true));
db.addIdentity(txn, identity);
// Add a contact associated with the local author // Check the contact is returned
assertEquals(contactId, Contact c = db.getContact(txn, contactId);
db.addContact(txn, author, localAuthor.getId(), true)); assertEquals(contactId, c.getId());
assertEquals(author.getId(), c.getAuthor().getId());
// Ensure contact is returned from database by author ID assertEquals(author.getFormatVersion(),
Collection<Contact> contacts = c.getAuthor().getFormatVersion());
db.getContactsByAuthorId(txn, author.getId()); assertEquals(author.getName(), c.getAuthor().getName());
assertEquals(1, contacts.size()); assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
assertEquals(contactId, contacts.iterator().next().getId());
// Ensure no contacts are returned after contact was deleted
db.removeContact(txn, contactId);
contacts = db.getContactsByAuthorId(txn, author.getId());
assertEquals(0, contacts.size());
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
} }
@Test @Test
public void testGetContactsByLocalAuthorId() throws Exception { public void testGetContactByAuthorId() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add an identity for a local author - no contacts should be // Add a contact
// associated assertEquals(contactId, db.addContact(txn, author, true));
db.addIdentity(txn, identity);
Collection<ContactId> contacts =
db.getContacts(txn, localAuthor.getId());
assertEquals(emptyList(), contacts);
// Add a contact associated with the local author // Check the contact is returned
assertEquals(contactId, Contact c = db.getContact(txn, author.getId());
db.addContact(txn, author, localAuthor.getId(), true)); assertEquals(contactId, c.getId());
contacts = db.getContacts(txn, localAuthor.getId()); assertEquals(author.getId(), c.getAuthor().getId());
assertEquals(singletonList(contactId), contacts); assertEquals(author.getFormatVersion(),
c.getAuthor().getFormatVersion());
// Remove the identity - the contact should be removed assertEquals(author.getName(), c.getAuthor().getName());
db.removeIdentity(txn, localAuthor.getId()); assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
contacts = db.getContacts(txn, localAuthor.getId());
assertEquals(emptyList(), contacts);
assertFalse(db.containsContact(txn, contactId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -1171,9 +1115,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact - initially there should be no offered messages // Add a contact - initially there should be no offered messages
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
assertEquals(0, db.countOfferedMessages(txn, contactId)); assertEquals(0, db.countOfferedMessages(txn, contactId));
// Add some offered messages and count them // Add some offered messages and count them
@@ -1755,9 +1697,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1854,44 +1794,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testDifferentLocalAuthorsCanHaveTheSameContact()
throws Exception {
Identity identity1 = getIdentity();
LocalAuthor localAuthor1 = identity1.getLocalAuthor();
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add identities for two local authors
db.addIdentity(txn, identity);
db.addIdentity(txn, identity1);
// Add the same contact for each local author
ContactId contactId =
db.addContact(txn, author, localAuthor.getId(), true);
ContactId contactId1 =
db.addContact(txn, author, localAuthor1.getId(), true);
// The contacts should be distinct
assertNotEquals(contactId, contactId1);
assertEquals(2, db.getContacts(txn).size());
assertEquals(1, db.getContacts(txn, localAuthor.getId()).size());
assertEquals(1, db.getContacts(txn, localAuthor1.getId()).size());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testDeleteMessage() throws Exception { public void testDeleteMessage() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -1943,9 +1852,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
// The contact should have no alias // The contact should have no alias
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
@@ -2000,9 +1907,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a group and a message // Add a contact, a group and a message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addMessage(txn, message, UNKNOWN, false, null); db.addMessage(txn, message, UNKNOWN, false, null);
@@ -2084,9 +1989,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -2129,9 +2032,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, a shared group and a shared message // Add a contact, a shared group and a shared message
db.addIdentity(txn, identity); assertEquals(contactId, db.addContact(txn, author, true));
assertEquals(contactId,
db.addContact(txn, author, localAuthor.getId(), true));
db.addGroup(txn, group); db.addGroup(txn, group);
db.addGroupVisibility(txn, contactId, groupId, true); db.addGroupVisibility(txn, contactId, groupId, true);
db.addMessage(txn, message, DELIVERED, true, null); db.addMessage(txn, message, DELIVERED, true, null);
@@ -2245,30 +2146,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
db.close(); db.close();
} }
@Test
public void testSetHandshakeKeyPair() throws Exception {
Identity withoutKeys =
new Identity(localAuthor, null, null, identity.getTimeCreated());
assertFalse(withoutKeys.hasHandshakeKeyPair());
byte[] publicKey = getRandomBytes(MAX_AGREEMENT_PUBLIC_KEY_BYTES);
byte[] privateKey = getRandomBytes(123);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
db.addIdentity(txn, withoutKeys);
Identity retrieved = db.getIdentity(txn, localAuthor.getId());
assertFalse(retrieved.hasHandshakeKeyPair());
db.setHandshakeKeyPair(txn, localAuthor.getId(), publicKey, privateKey);
retrieved = db.getIdentity(txn, localAuthor.getId());
assertTrue(retrieved.hasHandshakeKeyPair());
assertArrayEquals(publicKey, retrieved.getHandshakePublicKey());
assertArrayEquals(privateKey, retrieved.getHandshakePrivateKey());
db.commitTransaction(txn);
db.close();
}
private Database<Connection> open(boolean resume) throws Exception { private Database<Connection> open(boolean resume) throws Exception {
return open(resume, new TestMessageFactory(), new SystemClock()); return open(resume, new TestMessageFactory(), new SystemClock());
} }

View File

@@ -8,17 +8,18 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorFactory; import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.Identity; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static java.util.Collections.singletonList; import java.util.Collection;
import static org.briarproject.bramble.test.TestUtils.getIdentity; import java.util.Collections;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
public class IdentityManagerImplTest extends BrambleMockTestCase { public class IdentityManagerImplTest extends BrambleMockTestCase {
@@ -27,100 +28,67 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class);
private final AuthorFactory authorFactory = private final AuthorFactory authorFactory =
context.mock(AuthorFactory.class); context.mock(AuthorFactory.class);
private final Clock clock = context.mock(Clock.class); private final PublicKey publicKey = context.mock(PublicKey.class);
private final PublicKey handshakePublicKey = context.mock(PublicKey.class); private final PrivateKey privateKey = context.mock(PrivateKey.class);
private final PrivateKey handshakePrivateKey =
context.mock(PrivateKey.class);
private final Transaction txn = new Transaction(null, false); private final Transaction txn = new Transaction(null, false);
private final Identity identityWithKeys = getIdentity(); private final LocalAuthor localAuthor = getLocalAuthor();
private final LocalAuthor localAuthor = identityWithKeys.getLocalAuthor(); private final Collection<LocalAuthor> localAuthors =
private final Identity identityWithoutKeys = new Identity(localAuthor, Collections.singletonList(localAuthor);
null, null, identityWithKeys.getTimeCreated()); private final String authorName = localAuthor.getName();
private final KeyPair handshakeKeyPair = private final KeyPair keyPair = new KeyPair(publicKey, privateKey);
new KeyPair(handshakePublicKey, handshakePrivateKey); private final byte[] publicKeyBytes = localAuthor.getPublicKey();
private final byte[] handshakePublicKeyBytes = private final byte[] privateKeyBytes = localAuthor.getPrivateKey();
identityWithKeys.getHandshakePublicKey(); private IdentityManager identityManager;
private final byte[] handshakePrivateKeyBytes =
identityWithKeys.getHandshakePrivateKey();
private IdentityManagerImpl identityManager;
@Before @Before
public void setUp() { public void setUp() {
identityManager = identityManager = new IdentityManagerImpl(db, crypto, authorFactory);
new IdentityManagerImpl(db, crypto, authorFactory, clock);
} }
@Test @Test
public void testOpenDatabaseIdentityRegistered() throws Exception { public void testCreateLocalAuthor() {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).addIdentity(txn, identityWithKeys); oneOf(crypto).generateSignatureKeyPair();
will(returnValue(keyPair));
oneOf(publicKey).getEncoded();
will(returnValue(publicKeyBytes));
oneOf(privateKey).getEncoded();
will(returnValue(privateKeyBytes));
oneOf(authorFactory).createLocalAuthor(authorName,
publicKeyBytes, privateKeyBytes);
will(returnValue(localAuthor));
}}); }});
identityManager.registerIdentity(identityWithKeys); assertEquals(localAuthor,
identityManager.onDatabaseOpened(txn); identityManager.createLocalAuthor(authorName));
} }
@Test @Test
public void testOpenDatabaseHandshakeKeysGenerated() throws Exception { public void testRegisterAndStoreLocalAuthor() throws Exception {
context.checking(new Expectations() {{ context.checking(new DbExpectations() {{
oneOf(db).getIdentities(txn); oneOf(db).transaction(with(false), withDbRunnable(txn));
will(returnValue(singletonList(identityWithoutKeys))); oneOf(db).addLocalAuthor(txn, localAuthor);
oneOf(crypto).generateAgreementKeyPair();
will(returnValue(handshakeKeyPair));
oneOf(handshakePublicKey).getEncoded();
will(returnValue(handshakePublicKeyBytes));
oneOf(handshakePrivateKey).getEncoded();
will(returnValue(handshakePrivateKeyBytes));
oneOf(db).setHandshakeKeyPair(txn, localAuthor.getId(),
handshakePublicKeyBytes, handshakePrivateKeyBytes);
}}); }});
identityManager.onDatabaseOpened(txn); identityManager.registerLocalAuthor(localAuthor);
assertEquals(localAuthor, identityManager.getLocalAuthor());
identityManager.storeLocalAuthor();
} }
@Test @Test
public void testOpenDatabaseNoHandshakeKeysGenerated() throws Exception { public void testGetLocalAuthor() throws Exception {
context.checking(new Expectations() {{ context.checking(new DbExpectations() {{
oneOf(db).getIdentities(txn); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
will(returnValue(singletonList(identityWithKeys))); oneOf(db).getLocalAuthors(txn);
will(returnValue(localAuthors));
}}); }});
identityManager.onDatabaseOpened(txn);
}
@Test
public void testGetLocalAuthorIdentityRegistered() throws DbException {
identityManager.registerIdentity(identityWithKeys);
assertEquals(localAuthor, identityManager.getLocalAuthor()); assertEquals(localAuthor, identityManager.getLocalAuthor());
} }
@Test @Test
public void testGetLocalAuthorHandshakeKeysGenerated() throws Exception { public void testGetCachedLocalAuthor() throws DbException {
context.checking(new DbExpectations() {{ identityManager.registerLocalAuthor(localAuthor);
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getIdentities(txn);
will(returnValue(singletonList(identityWithoutKeys)));
oneOf(crypto).generateAgreementKeyPair();
will(returnValue(handshakeKeyPair));
oneOf(handshakePublicKey).getEncoded();
will(returnValue(handshakePublicKeyBytes));
oneOf(handshakePrivateKey).getEncoded();
will(returnValue(handshakePrivateKeyBytes));
}});
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
@Test
public void testGetLocalAuthorNoHandshakeKeysGenerated() throws Exception {
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getIdentities(txn);
will(returnValue(singletonList(identityWithKeys)));
}});
assertEquals(localAuthor, identityManager.getLocalAuthor()); assertEquals(localAuthor, identityManager.getLocalAuthor());
} }

View File

@@ -2,11 +2,11 @@ package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader; import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.junit.Test; import org.junit.Test;

View File

@@ -1,53 +0,0 @@
package org.briarproject.bramble.lifecycle;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations;
import org.junit.Before;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicBoolean;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertEquals;
public class LifecycleManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final EventBus eventBus = context.mock(EventBus.class);
private final SecretKey dbKey = getSecretKey();
private LifecycleManagerImpl lifecycleManager;
@Before
public void setUp() {
lifecycleManager = new LifecycleManagerImpl(db, eventBus);
}
@Test
public void testOpenDatabaseHooksAreCalledAtStartup() throws Exception {
Transaction txn = new Transaction(null, false);
AtomicBoolean called = new AtomicBoolean(false);
OpenDatabaseHook hook = transaction -> called.set(true);
context.checking(new DbExpectations() {{
oneOf(db).open(dbKey, lifecycleManager);
will(returnValue(false));
oneOf(db).transaction(with(false), withDbRunnable(txn));
allowing(eventBus).broadcast(with(any(LifecycleEvent.class)));
}});
lifecycleManager.registerOpenDatabaseHook(hook);
assertEquals(SUCCESS, lifecycleManager.startServices(dbKey));
assertTrue(called.get());
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.plugin.ConnectionManager; import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginConfig; import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -13,15 +14,18 @@ import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.properties.TransportPropertyManager; import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.jmock.lib.concurrent.Synchroniser; import org.jmock.lib.concurrent.Synchroniser;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom;
import java.util.Arrays; import java.util.Arrays;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
@@ -33,10 +37,16 @@ public class PluginManagerImplTest extends BrambleTestCase {
setThreadingPolicy(new Synchroniser()); setThreadingPolicy(new Synchroniser());
}}; }};
Executor ioExecutor = Executors.newSingleThreadExecutor(); Executor ioExecutor = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class);
SecureRandom random = new SecureRandom();
Clock clock = context.mock(Clock.class);
EventBus eventBus = context.mock(EventBus.class); EventBus eventBus = context.mock(EventBus.class);
PluginConfig pluginConfig = context.mock(PluginConfig.class); PluginConfig pluginConfig = context.mock(PluginConfig.class);
ConnectionManager connectionManager = ConnectionManager connectionManager =
context.mock(ConnectionManager.class); context.mock(ConnectionManager.class);
ConnectionRegistry connectionRegistry =
context.mock(ConnectionRegistry.class);
SettingsManager settingsManager = SettingsManager settingsManager =
context.mock(SettingsManager.class); context.mock(SettingsManager.class);
TransportPropertyManager transportPropertyManager = TransportPropertyManager transportPropertyManager =
@@ -112,9 +122,9 @@ public class PluginManagerImplTest extends BrambleTestCase {
oneOf(duplexPlugin).stop(); oneOf(duplexPlugin).stop();
}}); }});
PluginManagerImpl p = new PluginManagerImpl(ioExecutor, eventBus, PluginManagerImpl p = new PluginManagerImpl(ioExecutor, scheduler,
pluginConfig, connectionManager, settingsManager, eventBus, pluginConfig, connectionManager, connectionRegistry,
transportPropertyManager); settingsManager, transportPropertyManager, random, clock);
// Two plugins should be started and stopped // Two plugins should be started and stopped
p.startService(); p.startService();

View File

@@ -23,7 +23,6 @@ import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction; import org.briarproject.bramble.test.RunAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -40,7 +39,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.test.TestUtils.getContactId; import static org.briarproject.bramble.test.TestUtils.getContactId;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
public class PollerImplTest extends BrambleMockTestCase { public class PollerTest extends BrambleMockTestCase {
private final ScheduledExecutorService scheduler = private final ScheduledExecutorService scheduler =
context.mock(ScheduledExecutorService.class); context.mock(ScheduledExecutorService.class);
@@ -63,20 +62,11 @@ public class PollerImplTest extends BrambleMockTestCase {
private final int pollingInterval = 60 * 1000; private final int pollingInterval = 60 * 1000;
private final long now = System.currentTimeMillis(); private final long now = System.currentTimeMillis();
private PollerImpl poller; public PollerTest() {
public PollerImplTest() {
context.setImposteriser(ClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
random = context.mock(SecureRandom.class); random = context.mock(SecureRandom.class);
} }
@Before
public void setUp() {
poller = new PollerImpl(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
}
@Test @Test
public void testConnectOnContactAdded() throws Exception { public void testConnectOnContactAdded() throws Exception {
// Two simplex plugins: one supports polling, the other doesn't // Two simplex plugins: one supports polling, the other doesn't
@@ -150,7 +140,11 @@ public class PollerImplTest extends BrambleMockTestCase {
will(returnValue(false)); will(returnValue(false));
}}); }});
poller.eventOccurred(new ContactAddedEvent(contactId)); Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ContactAddedEvent(contactId));
} }
@Test @Test
@@ -200,7 +194,11 @@ public class PollerImplTest extends BrambleMockTestCase {
transportId, duplexConnection); transportId, duplexConnection);
}}); }});
poller.eventOccurred(new ConnectionClosedEvent(contactId, transportId, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionClosedEvent(contactId, transportId,
false)); false));
} }
@@ -227,7 +225,11 @@ public class PollerImplTest extends BrambleMockTestCase {
will(returnValue(future)); will(returnValue(future));
}}); }});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -267,9 +269,13 @@ public class PollerImplTest extends BrambleMockTestCase {
will(returnValue(now + 1)); will(returnValue(now + 1));
}}); }});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -312,9 +318,13 @@ public class PollerImplTest extends BrambleMockTestCase {
with((long) pollingInterval - 2), with(MILLISECONDS)); with((long) pollingInterval - 2), with(MILLISECONDS));
}}); }});
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
poller.eventOccurred(new ConnectionOpenedEvent(contactId, transportId, p.eventOccurred(new ConnectionOpenedEvent(contactId, transportId,
false)); false));
} }
@@ -357,7 +367,11 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(plugin).poll(singletonMap(contactId, properties)); oneOf(plugin).poll(singletonMap(contactId, properties));
}}); }});
poller.eventOccurred(new TransportEnabledEvent(transportId)); Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
@@ -398,7 +412,11 @@ public class PollerImplTest extends BrambleMockTestCase {
// All contacts are connected, so don't poll the plugin // All contacts are connected, so don't poll the plugin
}}); }});
poller.eventOccurred(new TransportEnabledEvent(transportId)); Poller p = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
} }
@Test @Test
@@ -424,7 +442,11 @@ public class PollerImplTest extends BrambleMockTestCase {
oneOf(future).cancel(false); oneOf(future).cancel(false);
}}); }});
poller.eventOccurred(new TransportEnabledEvent(transportId)); Poller p = new Poller(ioExecutor, scheduler, connectionManager,
poller.eventOccurred(new TransportDisabledEvent(transportId)); connectionRegistry, pluginManager, transportPropertyManager,
random, clock);
p.eventOccurred(new TransportEnabledEvent(transportId));
p.eventOccurred(new TransportDisabledEvent(transportId));
} }
} }

View File

@@ -11,6 +11,8 @@ import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
@@ -37,6 +39,7 @@ import static org.briarproject.bramble.api.properties.TransportPropertyManager.M
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED; import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -46,6 +49,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class); private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ClientVersioningManager clientVersioningManager = private final ClientVersioningManager clientVersioningManager =
context.mock(ClientVersioningManager.class); context.mock(ClientVersioningManager.class);
private final MetadataParser metadataParser = private final MetadataParser metadataParser =
@@ -55,6 +60,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final LocalAuthor localAuthor = getLocalAuthor();
private final BdfDictionary fooPropertiesDict = BdfDictionary.of( private final BdfDictionary fooPropertiesDict = BdfDictionary.of(
new BdfEntry("fooKey1", "fooValue1"), new BdfEntry("fooKey1", "fooValue1"),
new BdfEntry("fooKey2", "fooValue2") new BdfEntry("fooKey2", "fooValue2")
@@ -81,8 +87,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
will(returnValue(localGroup)); will(returnValue(localGroup));
}}); }});
return new TransportPropertyManagerImpl(db, clientHelper, return new TransportPropertyManagerImpl(db, clientHelper,
clientVersioningManager, metadataParser, contactGroupFactory, identityManager, clientVersioningManager, metadataParser,
clock); contactGroupFactory, clock);
} }
@Test @Test
@@ -95,10 +101,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
oneOf(db).containsGroup(txn, localGroup.getId()); oneOf(db).containsGroup(txn, localGroup.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(db).addGroup(txn, localGroup); oneOf(db).addGroup(txn, localGroup);
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn,
@@ -115,7 +123,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
1, true, true); 1, true, true);
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
t.onDatabaseOpened(txn); t.createLocalState(txn);
} }
@Test @Test
@@ -129,7 +137,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
}}); }});
TransportPropertyManagerImpl t = createInstance(); TransportPropertyManagerImpl t = createInstance();
t.onDatabaseOpened(txn); t.createLocalState(txn);
} }
@Test @Test
@@ -140,8 +148,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Create the group and share it with the contact // Create the group and share it with the contact
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(clientVersioningManager).getClientVisibility(txn, oneOf(clientVersioningManager).getClientVisibility(txn,
@@ -168,8 +178,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup); oneOf(db).removeGroup(txn, contactGroup);
}}); }});
@@ -307,8 +319,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
}}); }});
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict, expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
@@ -432,18 +446,20 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(contacts)); will(returnValue(contacts));
// First contact: no updates // First contact: no updates
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact1); MAJOR_VERSION, contact1, localAuthor.getId());
will(returnValue(contactGroup1)); will(returnValue(contactGroup1));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup1.getId()); contactGroup1.getId());
will(returnValue(Collections.emptyMap())); will(returnValue(Collections.emptyMap()));
// Second contact: returns an update // Second contact: returns an update
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact2); MAJOR_VERSION, contact2, localAuthor.getId());
will(returnValue(contactGroup2)); will(returnValue(contactGroup2));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup2.getId()); contactGroup2.getId());
@@ -510,10 +526,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
expectStoreMessage(txn, localGroup.getId(), "foo", expectStoreMessage(txn, localGroup.getId(), "foo",
fooPropertiesDict, 1, true, false); fooPropertiesDict, 1, true, false);
// Store the new properties in each contact's group, version 1 // Store the new properties in each contact's group, version 1
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());
@@ -566,10 +584,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
// Delete the previous update // Delete the previous update
oneOf(db).removeMessage(txn, localGroupUpdateId); oneOf(db).removeMessage(txn, localGroupUpdateId);
// Store the merged properties in each contact's group, version 2 // Store the merged properties in each contact's group, version 2
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
contactGroup.getId()); contactGroup.getId());

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@@ -31,7 +32,7 @@ public class TestLifecycleModule {
} }
@Override @Override
public void registerOpenDatabaseHook(OpenDatabaseHook hook) { public void registerClient(Client c) {
} }
@Override @Override

View File

@@ -1,71 +0,0 @@
package org.briarproject.bramble.util;
import org.briarproject.bramble.test.BrambleTestCase;
import org.junit.Test;
import java.util.Random;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
public class Base32Test extends BrambleTestCase {
// Test vectors from RFC 4648
// https://tools.ietf.org/html/rfc4648#section-10
@Test
public void testEncoding() {
assertEquals("", Base32.encode(new byte[0]));
assertEquals("MY", Base32.encode(new byte[] {'f'}));
assertEquals("MZXQ", Base32.encode(new byte[] {'f', 'o'}));
assertEquals("MZXW6", Base32.encode(new byte[] {'f', 'o', 'o'}));
assertEquals("MZXW6YQ", Base32.encode(new byte[] {'f', 'o', 'o', 'b'}));
assertEquals("MZXW6YTB",
Base32.encode(new byte[] {'f', 'o', 'o', 'b', 'a'}));
assertEquals("MZXW6YTBOI",
Base32.encode(new byte[] {'f', 'o', 'o', 'b', 'a', 'r'}));
}
@Test
public void testStrictDecoding() {
testDecoding(true);
}
@Test
public void testNonStrictDecoding() {
testDecoding(false);
}
private void testDecoding(boolean strict) {
assertArrayEquals(new byte[0], Base32.decode("", strict));
assertArrayEquals(new byte[] {'f'}, Base32.decode("MY", strict));
assertArrayEquals(new byte[] {'f', 'o'}, Base32.decode("MZXQ", strict));
assertArrayEquals(new byte[] {'f', 'o', 'o'},
Base32.decode("MZXW6", strict));
assertArrayEquals(new byte[] {'f', 'o', 'o', 'b'},
Base32.decode("MZXW6YQ", strict));
assertArrayEquals(new byte[] {'f', 'o', 'o', 'b', 'a'},
Base32.decode("MZXW6YTB", strict));
assertArrayEquals(new byte[] {'f', 'o', 'o', 'b', 'a', 'r'},
Base32.decode("MZXW6YTBOI", strict));
}
@Test(expected = IllegalArgumentException.class)
public void testStrictDecodingRejectsNonZeroUnusedBits() {
Base32.decode("MZ", true);
}
@Test
public void testNonStrictDecodingAcceptsNonZeroUnusedBits() {
assertArrayEquals(new byte[] {'f'}, Base32.decode("MZ", false));
}
@Test
public void testRoundTrip() {
Random random = new Random();
byte[] data = new byte[100 + random.nextInt(100)];
random.nextBytes(data);
assertArrayEquals(data, Base32.decode(Base32.encode(data), true));
assertArrayEquals(data, Base32.decode(Base32.encode(data), false));
}
}

View File

@@ -10,6 +10,8 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.sync.ClientId; import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.Group.Visibility; import org.briarproject.bramble.api.sync.Group.Visibility;
@@ -36,6 +38,7 @@ import static org.briarproject.bramble.api.versioning.ClientVersioningManager.MA
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact; import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID; import static org.briarproject.bramble.versioning.ClientVersioningConstants.GROUP_KEY_CONTACT_ID;
@@ -48,12 +51,15 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
private final DatabaseComponent db = context.mock(DatabaseComponent.class); private final DatabaseComponent db = context.mock(DatabaseComponent.class);
private final ClientHelper clientHelper = context.mock(ClientHelper.class); private final ClientHelper clientHelper = context.mock(ClientHelper.class);
private final IdentityManager identityManager =
context.mock(IdentityManager.class);
private final ContactGroupFactory contactGroupFactory = private final ContactGroupFactory contactGroupFactory =
context.mock(ContactGroupFactory.class); context.mock(ContactGroupFactory.class);
private final Clock clock = context.mock(Clock.class); private final Clock clock = context.mock(Clock.class);
private final ClientVersioningHook hook = private final ClientVersioningHook hook =
context.mock(ClientVersioningHook.class); context.mock(ClientVersioningHook.class);
private final LocalAuthor localAuthor = getLocalAuthor();
private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group localGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION); private final Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
private final Contact contact = getContact(); private final Contact contact = getContact();
@@ -68,7 +74,7 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
will(returnValue(localGroup)); will(returnValue(localGroup));
}}); }});
return new ClientVersioningManagerImpl(db, clientHelper, return new ClientVersioningManagerImpl(db, clientHelper,
contactGroupFactory, clock); identityManager, contactGroupFactory, clock);
} }
@Test @Test
@@ -83,7 +89,7 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
expectAddingContact(); expectAddingContact();
ClientVersioningManagerImpl c = createInstance(); ClientVersioningManagerImpl c = createInstance();
c.onDatabaseOpened(txn); c.createLocalState(txn);
} }
@Test @Test
@@ -95,7 +101,7 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
}}); }});
ClientVersioningManagerImpl c = createInstance(); ClientVersioningManagerImpl c = createInstance();
c.onDatabaseOpened(txn); c.createLocalState(txn);
} }
@Test @Test
@@ -117,8 +123,10 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
new BdfEntry(MSG_KEY_LOCAL, true)); new BdfEntry(MSG_KEY_LOCAL, true));
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).addGroup(txn, contactGroup); oneOf(db).addGroup(txn, contactGroup);
oneOf(db).setGroupVisibility(txn, contact.getId(), oneOf(db).setGroupVisibility(txn, contact.getId(),
@@ -138,8 +146,10 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testRemovesGroupWhenRemovingContact() throws Exception { public void testRemovesGroupWhenRemovingContact() throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).removeGroup(txn, contactGroup); oneOf(db).removeGroup(txn, contactGroup);
}}); }});
@@ -174,10 +184,12 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(db).addLocalMessage(txn, localVersions, new Metadata(), oneOf(db).addLocalMessage(txn, localVersions, new Metadata(),
false); false);
// Inform contacts that client versions have changed // Inform contacts that client versions have changed
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
// Find the latest local and remote updates (no remote update) // Find the latest local and remote updates (no remote update)
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
@@ -261,10 +273,12 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(), oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(),
false); false);
// Inform contacts that client versions have changed // Inform contacts that client versions have changed
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
// Find the latest local and remote updates (no remote update) // Find the latest local and remote updates (no remote update)
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
@@ -357,10 +371,12 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(), oneOf(db).addLocalMessage(txn, newLocalVersions, new Metadata(),
false); false);
// Inform contacts that client versions have changed // Inform contacts that client versions have changed
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContacts(txn); oneOf(db).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
// Find the latest local and remote updates // Find the latest local and remote updates
oneOf(clientHelper).getMessageMetadataAsDictionary(txn, oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
@@ -970,8 +986,10 @@ public class ClientVersioningManagerImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).getContact(txn, contact.getId()); oneOf(db).getContact(txn, contact.getId());
will(returnValue(contact)); will(returnValue(contact));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID, oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
MAJOR_VERSION, contact); MAJOR_VERSION, contact, localAuthor.getId());
will(returnValue(contactGroup)); will(returnValue(contactGroup));
oneOf(db).containsGroup(txn, contactGroup.getId()); oneOf(db).containsGroup(txn, contactGroup.getId());
will(returnValue(exists)); will(returnValue(exists));

View File

@@ -2,15 +2,15 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3', 'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35', 'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d', 'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c', 'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728', 'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90', 'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',

View File

@@ -19,7 +19,7 @@ dependencies {
tor 'org.briarproject:tor:0.3.5.8@zip' tor 'org.briarproject:tor:0.3.5.8@zip'
tor 'org.briarproject:obfs4proxy:0.0.7@zip' tor 'org.briarproject:obfs4proxy:0.0.7@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
@@ -28,7 +28,7 @@ dependencies {
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.19'
} }
def torBinariesDir = 'src/main/resources' def torBinariesDir = 'src/main/resources'

View File

@@ -2,15 +2,15 @@ dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.dagger:dagger-compiler:2.22.1:dagger-compiler-2.22.1.jar:e5f28302cbe70a79d3620cddebfb8ec0736814f3980ffe1e673bfe3342f507d3', 'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
'com.google.dagger:dagger-producers:2.22.1:dagger-producers-2.22.1.jar:f834a0082014213a68ff06a0f048d750178d02196c58b0b15beb367d32b97e35', 'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
'com.google.dagger:dagger-spi:2.22.1:dagger-spi-2.22.1.jar:4b0b922793b3bcb91b99fabb75dba77c68afd7ae4c5f0c4fd6ba681f0a291c7d', 'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'com.google.dagger:dagger:2.22.1:dagger-2.22.1.jar:329d4340f24c4f5717af016c097e90668bfea2a5376e6aa9964b01cef3fd241a', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:2994a7eb78f2710bd3d3bfb639b2c94e219cedac0d4d084d516e78c16dddecf6', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90', 'com.squareup:javapoet:1.11.1:javapoet-1.11.1.jar:9cbf2107be499ec6e95afd36b58e3ca122a24166cdd375732e51267d64058e90',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',

View File

@@ -1,57 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="24"
height="24"
viewBox="0 0 24 24"
version="1.1"
id="svg12"
sodipodi:docname="nearby.svg"
style="fill:none"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata18">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs16" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview14"
showgrid="false"
inkscape:zoom="15.170655"
inkscape:cx="9.6488139"
inkscape:cy="11.430963"
inkscape:window-x="1440"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg12" />
<path
style="opacity:1;fill:#110000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.37658679px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 11.173062,2.4030645 C 9.9685672,2.4471125 8.7753453,2.7130268 7.6640583,3.2033016 8.187015,3.7262611 8.3967456,4.1446273 8.6059301,4.5629964 11.639055,3.412486 15.091396,4.0393524 17.497058,6.4449696 c 0.732151,0.7321412 1.359335,1.5691523 1.673062,2.5104781 0.418424,-0.2091845 0.941213,-0.3133673 1.464151,-0.3133672 h 0.104456 C 20.215788,7.3869735 19.482978,6.3401088 18.541616,5.2941863 16.528265,3.2807917 13.82295,2.3061587 11.173062,2.4030645 Z M 5.1695142,3.2316286 A 2.7193895,2.7193895 0 0 0 2.4501247,5.9510181 2.7193895,2.7193895 0 0 0 5.1695142,8.6704075 2.7193895,2.7193895 0 0 0 7.8889037,5.9510181 2.7193895,2.7193895 0 0 0 5.1695142,3.2316286 Z M 2.5404169,8.5358543 C 0.97153856,12.196552 1.7027262,16.484811 4.6313017,19.413413 c 1.8826514,1.987239 4.497171,2.930071 7.0073853,2.930071 2.510177,0 5.126431,-0.942832 7.009154,-2.930071 0.941272,-0.941362 1.672402,-2.091035 2.195341,-3.346124 h -0.104455 c -0.522939,0 -1.045818,-0.104155 -1.464151,-0.313367 -0.418423,0.941362 -0.940911,1.778328 -1.673062,2.510478 -3.242327,3.242419 -8.5770829,3.242419 -11.819429,0 C 3.27188,15.858828 2.6451458,12.406442 3.795656,9.3732707 3.377287,9.164086 2.9587814,8.9542233 2.5404169,8.5358543 Z m 9.0540081,0.8444984 a 2.9913288,2.9913284 0 0 0 -2.9902654,2.9902663 2.9913288,2.9913284 0 0 0 2.9902654,2.992036 2.9913288,2.9913284 0 0 0 2.992037,-2.992036 2.9913288,2.9913284 0 0 0 -2.992037,-2.9902663 z m 9.138991,0.423134 a 2.7193895,2.7193895 0 0 0 -2.71939,2.7193903 2.7193895,2.7193895 0 0 0 2.71939,2.719389 2.7193895,2.7193895 0 0 0 2.719389,-2.719389 2.7193895,2.7193895 0 0 0 -2.719389,-2.7193903 z"
id="path832-3-6"
inkscape:connector-curvature="0" />
</svg>

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -1,176 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="150"
height="178"
viewBox="0 0 150 178"
fill="none"
version="1.1"
id="svg129"
sodipodi:docname="nickname.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata133">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview131"
showgrid="false"
inkscape:zoom="3.7500494"
inkscape:cx="84.461975"
inkscape:cy="96.344913"
inkscape:window-x="1440"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg129" />
<path
d="M 85.1905,0.127869 3.35693,0.328805 C 2.46521,0.330621 1.6107,0.690255 0.981217,1.32866 0.351735,1.96706 -0.0011965,2.83198 3.04782e-6,3.73331 L 0.372743,156.201 c 0.001185,0.446 0.089367,0.888 0.259505,1.3 0.170138,0.412 0.418902,0.786 0.732092,1.101 0.31319,0.315 0.68466,0.564 1.09322,0.734 0.40856,0.17 0.84619,0.257 1.28792,0.256 l 81.83362,-0.201 c 0.8917,-0.003 1.746,-0.363 2.375,-1.002 0.6291,-0.639 0.9814,-1.504 0.9796,-2.405 L 88.5542,3.51639 C 88.5512,2.61665 88.1955,1.75479 87.565,1.11965 86.9345,0.484503 86.0807,0.127864 85.1905,0.127869 Z"
id="path2"
inkscape:connector-curvature="0"
style="display:inline;opacity:0.25;fill:url(#paint0_linear)" />
<path
d="M 84.2815,-0.0019907 4.27572,0.195606 C 2.57395,0.199809 1.19777,1.59763 1.20193,3.31772 L 1.56691,154.298 c 0.00416,1.72 1.38709,3.111 3.08885,3.107 l 80.00584,-0.198 c 1.7017,-0.004 3.0779,-1.402 3.0737,-3.122 L 87.3704,3.1049 C 87.3662,1.38481 85.9833,-0.0061937 84.2815,-0.0019907 Z"
id="path4"
inkscape:connector-curvature="0"
style="display:inline;opacity:0.25;fill:#6d6d6d;fill-opacity:0.7" />
<path
style="display:inline;opacity:0.25;fill:#646464"
d="M 81.939453 3.9570312 L 65.933594 3.9960938 C 65.688694 5.6638138 64.861562 7.1881588 63.601562 8.2929688 C 62.341563 9.3977688 60.732453 10.010031 59.064453 10.019531 L 29.302734 10.091797 C 27.634734 10.090597 26.021259 9.4871919 24.755859 8.3886719 C 23.490459 7.2901519 22.655444 5.7700256 22.402344 4.1035156 L 6.6367188 4.1425781 C 5.7689587 4.1449981 4.9382819 4.4954875 4.3261719 5.1171875 C 3.7140519 5.7388975 3.3712469 6.5799313 3.3730469 7.4570312 L 3.4609375 44.068359 L 3.7167969 150.16797 C 3.7209769 151.04497 4.0691969 151.88495 4.6855469 152.50195 C 5.3018969 153.11895 6.1361462 153.46394 7.0039062 153.46094 L 82.306641 153.27539 C 83.171641 153.27039 83.999475 152.92078 84.609375 152.30078 C 85.219375 151.68078 85.560547 150.8408 85.560547 149.9668 L 85.306641 43.5625 L 85.220703 7.2558594 C 85.218303 6.3787594 84.870959 5.5386319 84.255859 4.9199219 C 83.640859 4.3012219 82.807253 3.9552112 81.939453 3.9570312 z M 52.652344 5.7910156 L 34.357422 6.0683594 C 34.107922 6.0721494 33.908409 6.27906 33.912109 6.53125 L 33.916016 6.8300781 C 33.919716 7.0822681 34.1255 7.28504 34.375 7.28125 L 52.671875 7.0019531 C 52.921375 6.9981731 53.118934 6.7912525 53.115234 6.5390625 L 53.111328 6.2402344 C 53.107628 5.9880444 52.901844 5.7872256 52.652344 5.7910156 z "
id="path6" />
<path
d="M 57.2036,6.934 C 57.6016,6.92797 57.9193,6.59699 57.9133,6.19476 57.9074,5.79252 57.5799,5.47134 57.182,5.47738 c -0.398,0.00604 -0.7157,0.33701 -0.7098,0.73924 0.006,0.40224 0.3335,0.72342 0.7314,0.71738 z"
id="path10"
inkscape:connector-curvature="0"
style="opacity:0.25;fill:#dbdbdb" />
<path
style="display:inline;opacity:0.25;fill:#e0e0e0"
d="m 17.640625,19.458984 c -4.1846,0 -7.576172,3.445513 -7.576172,7.695313 0,4.2498 3.391572,7.695312 7.576172,7.695312 4.1845,0 7.576172,-3.445512 7.576172,-7.695312 0,-4.2498 -3.391672,-7.695313 -7.576172,-7.695313 z m 19.882813,4.177735 v 3.234375 h 40.117187 v -3.234375 z m 0,6.466797 v 3.234375 H 77.640625 V 30.103516 Z M 17.640625,44.087891 c -4.1846,0 -7.576172,3.443559 -7.576172,7.693359 0,4.2498 3.391572,7.695313 7.576172,7.695312 4.1845,0 7.576172,-3.445512 7.576172,-7.695312 0,-4.2498 -3.391672,-7.693359 -7.576172,-7.693359 z m 19.882813,4.177734 V 51.5 h 40.117187 v -3.234375 z m 0,6.466797 v 3.232422 H 77.640625 V 54.732422 Z M 17.640625,68.714844 c -4.1846,0 -7.576172,3.445512 -7.576172,7.695312 0,4.2498 3.391572,7.695313 7.576172,7.695313 4.1845,0 7.576172,-3.445513 7.576172,-7.695313 0,-4.2498 -3.391672,-7.695312 -7.576172,-7.695312 z m 19.882813,4.179687 v 3.232422 h 40.117187 v -3.232422 z m 0,6.466797 V 82.59375 H 77.640625 V 79.361328 Z M 17.640625,93.34375 c -4.1846,0 -7.576172,3.445613 -7.576172,7.69531 0,4.25 3.391572,7.69532 7.576172,7.69532 4.1845,0 7.576172,-3.44532 7.576172,-7.69532 0,-4.249698 -3.391672,-7.69531 -7.576172,-7.69531 z m 19.882813,4.179688 v 3.232422 h 40.117187 v -3.232422 z m 0,6.464842 v 3.23438 h 40.117187 v -3.23438 z m -19.882813,13.98438 c -4.1846,0 -7.576172,3.44631 -7.576172,7.69531 0,4.25 3.391572,7.69336 7.576172,7.69336 4.1845,0 7.576172,-3.44336 7.576172,-7.69336 0,-4.249 -3.391672,-7.69531 -7.576172,-7.69531 z m 19.882813,4.17773 v 3.23438 h 40.117187 v -3.23438 z m 0,6.4668 v 3.23437 h 40.117187 v -3.23437 z"
id="path12"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#313131"
inkscape:connector-curvature="0"
id="path44"
d="m 136.95,18.4066 -80.0056,0.1976 c -1.7018,0.0042 -3.078,1.402 -3.0738,3.1221 l 0.365,150.9807 c 0.0041,1.72 1.3871,3.111 3.0888,3.107 l 80.0056,-0.198 c 1.702,-0.004 3.078,-1.402 3.074,-3.122 L 140.039,21.5135 c -0.004,-1.7201 -1.387,-3.1111 -3.089,-3.1069 z" />
<path
style="display:inline;fill:#3e3e3e"
d="M 134.60938 22.357422 L 118.59961 22.394531 C 118.35561 24.062231 117.52758 25.586606 116.26758 26.691406 C 115.00758 27.796206 113.39847 28.408469 111.73047 28.417969 L 81.96875 28.492188 C 80.30065 28.490987 78.689228 27.885609 77.423828 26.787109 C 76.158428 25.688609 75.323313 24.168453 75.070312 22.501953 L 59.304688 22.541016 C 58.436887 22.543416 57.604288 22.893925 56.992188 23.515625 C 56.380088 24.137325 56.037262 24.980322 56.039062 25.857422 L 56.128906 62.46875 L 56.748047 62.466797 L 56.128906 62.470703 L 56.382812 168.57031 C 56.385213 169.44731 56.732656 170.28725 57.347656 170.90625 C 57.962756 171.52425 58.796362 171.87114 59.664062 171.86914 L 134.9668 171.68359 C 135.8348 171.68159 136.66534 171.33098 137.27734 170.70898 C 137.88934 170.08798 138.23247 169.24614 138.23047 168.36914 L 137.97266 62.21875 L 137.97461 62.21875 L 137.88867 25.65625 C 137.88667 24.77915 137.54078 23.939012 136.92578 23.320312 C 136.31078 22.701612 135.47738 22.355522 134.60938 22.357422 z "
id="path46" />
<path
style="display:inline;fill:#e0e0e0"
d="M 109.84961 23.890625 C 109.45161 23.896725 109.13462 24.228559 109.14062 24.630859 C 109.14663 25.033059 109.47309 25.353756 109.87109 25.347656 C 110.26909 25.341656 110.58608 25.009622 110.58008 24.607422 C 110.57408 24.205222 110.24761 23.884625 109.84961 23.890625 z M 105.32031 24.201172 L 87.025391 24.478516 C 86.775891 24.482316 86.576378 24.691159 86.580078 24.943359 L 86.583984 25.242188 C 86.587684 25.494387 86.793469 25.695206 87.042969 25.691406 L 105.33984 25.414062 C 105.58884 25.410363 105.7872 25.203372 105.7832 24.951172 L 105.7793 24.650391 C 105.7753 24.398191 105.56931 24.197372 105.32031 24.201172 z M 63.365234 37.568359 L 63.365234 53.738281 L 79.365234 53.738281 L 79.365234 37.568359 L 63.365234 37.568359 z M 86.748047 37.568359 L 86.748047 40.800781 L 126.86523 40.800781 L 126.86523 37.568359 L 86.748047 37.568359 z M 86.748047 44.037109 L 86.748047 47.269531 L 126.86523 47.269531 L 126.86523 44.037109 L 86.748047 44.037109 z M 71.787109 112.99609 C 67.602609 112.99609 64.210937 116.44141 64.210938 120.69141 C 64.210938 124.94141 67.602609 128.38672 71.787109 128.38672 C 75.971709 128.38672 79.365234 124.94141 79.365234 120.69141 C 79.365234 116.44141 75.971709 112.99609 71.787109 112.99609 z M 86.748047 116.92773 L 86.748047 120.16016 L 126.86523 120.16016 L 126.86523 116.92773 L 86.748047 116.92773 z M 86.748047 123.39258 L 86.748047 126.62695 L 126.86523 126.62695 L 126.86523 123.39258 L 86.748047 123.39258 z M 71.787109 142.60156 C 67.602609 142.60156 64.210937 146.04687 64.210938 150.29688 C 64.210938 154.54688 67.602609 157.99023 71.787109 157.99023 C 75.971709 157.99023 79.365234 154.54688 79.365234 150.29688 C 79.365234 146.04688 75.971709 142.60156 71.787109 142.60156 z M 86.748047 145.53516 L 86.748047 148.76758 L 126.86523 148.76758 L 126.86523 145.53516 L 86.748047 145.53516 z M 86.748047 152.00195 L 86.748047 155.23438 L 126.86523 155.23438 L 126.86523 152.00195 L 86.748047 152.00195 z "
id="path50" />
<path
style="display:inline;fill:#d5d6d7"
inkscape:connector-curvature="0"
id="path56"
d="M 148.744,68.6655 H 45.619 v 33.5835 h 103.125 z" />
<path
style="display:inline;fill:#87c214"
d="m 97.822266,50.503906 v 5.722656 h 17.966794 v -5.722656 z m -11.074219,30.59961 v 3.232422 h 40.117183 v -3.232422 z m 0,6.46875 v 3.232422 h 40.117183 v -3.232422 z"
id="path58"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#f5f5f5"
inkscape:connector-curvature="0"
id="path68"
d="m 66.8652,92.3143 c 4.1846,0 7.5768,-3.4451 7.5768,-7.695 0,-4.2498 -3.3922,-7.6949 -7.5768,-7.6949 -4.1846,0 -7.5768,3.4451 -7.5768,7.6949 0,4.2499 3.3922,7.695 7.5768,7.695 z" />
<path
style="display:inline;fill:#87c214"
inkscape:connector-curvature="0"
id="path70"
d="m 70.1296,90.0058 h -6.5738 c -0.3153,0.0024 -0.6253,0.0818 -0.9038,0.2312 -0.2784,0.1495 -0.5171,0.3647 -0.6956,0.6274 1.4766,0.8563 3.1474,1.3114 4.8498,1.321 1.7023,0.0096 3.3781,-0.4266 4.864,-1.2662 -0.1666,-0.265 -0.3932,-0.486 -0.6612,-0.6448 -0.2679,-0.1588 -0.5693,-0.2509 -0.8794,-0.2686 z" />
<path
style="display:inline;fill:#333333"
d="m 66.861328,80.746094 c -2.0798,0 -3.765625,1.70444 -3.765625,3.80664 0,0.393506 0.07586,0.766 0.185547,1.123047 -0.327549,0.812667 -0.821888,1.878261 -1.158203,1.708985 0,0 5.069822,4.4252 9.544922,0 -0.355198,-0.613452 -0.773279,-1.18389 -1.21875,-1.732422 0.10502,-0.350063 0.177734,-0.714879 0.177734,-1.09961 0,-2.1022 -1.685825,-3.80664 -3.765625,-3.80664 z"
id="path72"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#fda57d"
d="m 66.861328,81.570312 c -1.800651,0 -3.302121,1.279176 -3.673828,2.986329 -0.0049,-3.79e-4 -0.0088,-0.0059 -0.01367,-0.0059 -0.1933,0 -0.349609,0.297063 -0.349609,0.664063 0,0.347156 0.141531,0.622551 0.320312,0.652344 0.180942,1.407599 1.108928,2.5691 2.38086,3.058593 v 0.732422 c 0,0.3234 0.127315,0.634681 0.353515,0.863281 0.2263,0.2287 0.533616,0.357422 0.853516,0.357422 h 0.224609 c 0.1588,3e-4 0.316091,-0.03245 0.462891,-0.09375 0.1467,-0.0613 0.280278,-0.150172 0.392578,-0.263672 0.1123,-0.1135 0.201119,-0.248084 0.261719,-0.396484 0.0606,-0.1483 0.0921,-0.30825 0.0918,-0.46875 v -0.720703 c 1.288752,-0.483775 2.232282,-1.654559 2.412109,-3.076172 0.168226,-0.0485 0.298828,-0.311757 0.298828,-0.644531 0,-0.363137 -0.153161,-0.655938 -0.34375,-0.66211 -0.373249,-1.705035 -1.87271,-2.982422 -3.671875,-2.982422 z"
id="path78"
inkscape:connector-curvature="0" />
<path
style="display:inline;fill:#333333"
inkscape:connector-curvature="0"
id="path84"
d="m 63.2529,83.9344 h 7.1792 c 0,0 -0.6122,-2.9296 -3.3275,-2.7401 -2.7154,0.1895 -3.8517,2.7401 -3.8517,2.7401 z" />
<defs
id="defs127">
<linearGradient
id="paint0_linear"
x1="10.0511"
y1="162.566"
x2="80.1467"
y2="-2.31086"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#808080"
stop-opacity="0.25"
id="stop110" />
<stop
offset="0.54"
stop-color="#808080"
stop-opacity="0.12"
id="stop112" />
<stop
offset="1"
stop-color="#808080"
stop-opacity="0.1"
id="stop114" />
</linearGradient>
<linearGradient
id="paint1_linear"
x1="45488.3"
y1="16529.6"
x2="45488.3"
y2="10752.2"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#808080"
stop-opacity="0.25"
id="stop117" />
<stop
offset="0.54"
stop-color="#808080"
stop-opacity="0.12"
id="stop119" />
<stop
offset="1"
stop-color="#808080"
stop-opacity="0.1"
id="stop121" />
</linearGradient>
<clipPath
id="clip0">
<rect
width="150"
height="178"
fill="white"
id="rect124" />
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -118,15 +118,14 @@ dependencies {
implementation 'com.google.zxing:core:3.3.3' implementation 'com.google.zxing:core:3.3.3'
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.14.0' implementation 'uk.co.samuelwall:material-tap-target-prompt:2.14.0'
implementation 'com.vanniktech:emoji-google:0.5.1' implementation 'com.vanniktech:emoji-google:0.5.1'
implementation 'com.github.kobakei:MaterialFabSpeedDial:1.2.1' // later versions already use androidx def glideVersion = '4.8.0'
def glideVersion = '4.9.0'
implementation("com.github.bumptech.glide:glide:$glideVersion") { implementation("com.github.bumptech.glide:glide:$glideVersion") {
exclude group: 'com.android.support' exclude group: 'com.android.support'
exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it
} }
implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx implementation 'com.github.chrisbanes:PhotoView:2.1.4' // later versions already use androidx
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
@@ -135,7 +134,7 @@ dependencies {
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
testImplementation 'org.robolectric:robolectric:4.0.1' testImplementation 'org.robolectric:robolectric:4.0.1'
testImplementation 'org.robolectric:shadows-support-v4:3.3.2' testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
testImplementation 'org.mockito:mockito-core:2.25.0' testImplementation 'org.mockito:mockito-core:2.19.0'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:2.8.2" testImplementation "org.jmock:jmock-junit4:2.8.2"
@@ -145,7 +144,7 @@ dependencies {
androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion" androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion" androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion"
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.22.1" androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.19"
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0' androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
androidTestImplementation 'junit:junit:4.12' androidTestImplementation 'junit:junit:4.12'
androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0" androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0"

View File

@@ -425,31 +425,5 @@
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"/> android:theme="@style/BriarTheme.NoActionBar"/>
<activity
android:name=".android.contact.add.remote.AddContactActivity"
android:label="@string/add_contact_remotely_title_case"
android:launchMode="singleTask"
android:theme="@style/BriarTheme"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="briar"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
<activity
android:name=".android.contact.add.remote.PendingContactListActivity"
android:label="@string/pending_contact_requests"
android:theme="@style/BriarTheme"/>
</application> </application>
</manifest> </manifest>

View File

@@ -42,7 +42,7 @@ import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent; import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent; import org.briarproject.briar.api.conversation.event.ConversationMessageReceivedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent; import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent; import org.briarproject.briar.api.introduction.event.IntroductionSucceededEvent;
import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent; import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent;
import java.util.Set; import java.util.Set;
@@ -99,7 +99,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
private final Multiset<GroupId> groupCounts = new Multiset<>(); private final Multiset<GroupId> groupCounts = new Multiset<>();
private final Multiset<GroupId> forumCounts = new Multiset<>(); private final Multiset<GroupId> forumCounts = new Multiset<>();
private final Multiset<GroupId> blogCounts = new Multiset<>(); private final Multiset<GroupId> blogCounts = new Multiset<>();
private int contactAddedTotal = 0; private int introductionTotal = 0;
private int nextRequestId = 0; private int nextRequestId = 0;
private ContactId blockedContact = null; private ContactId blockedContact = null;
private GroupId blockedGroup = null; private GroupId blockedGroup = null;
@@ -171,7 +171,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
clearGroupMessageNotification(); clearGroupMessageNotification();
clearForumPostNotification(); clearForumPostNotification();
clearBlogPostNotification(); clearBlogPostNotification();
clearContactAddedNotification(); clearIntroductionSuccessNotification();
return null; return null;
}); });
try { try {
@@ -206,9 +206,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
@UiThread @UiThread
private void clearContactAddedNotification() { private void clearIntroductionSuccessNotification() {
contactAddedTotal = 0; introductionTotal = 0;
notificationManager.cancel(CONTACT_ADDED_NOTIFICATION_ID); notificationManager.cancel(INTRODUCTION_SUCCESS_NOTIFICATION_ID);
} }
@Override @Override
@@ -230,8 +230,8 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} else if (e instanceof BlogPostAddedEvent) { } else if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e; BlogPostAddedEvent b = (BlogPostAddedEvent) e;
if (!b.isLocal()) showBlogPostNotification(b.getGroupId()); if (!b.isLocal()) showBlogPostNotification(b.getGroupId());
} else if (e instanceof ContactAddedRemotelyEvent) { } else if (e instanceof IntroductionSucceededEvent) {
showContactAddedNotification(); showIntroductionNotification();
} }
} }
@@ -563,24 +563,24 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
@UiThread @UiThread
private void showContactAddedNotification() { private void showIntroductionNotification() {
contactAddedTotal++; introductionTotal++;
updateContactAddedNotification(); updateIntroductionNotification();
} }
@UiThread @UiThread
private void updateContactAddedNotification() { private void updateIntroductionNotification() {
BriarNotificationBuilder b = BriarNotificationBuilder b =
new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID); new BriarNotificationBuilder(appContext, CONTACT_CHANNEL_ID);
b.setSmallIcon(R.drawable.notification_contact_added); b.setSmallIcon(R.drawable.notification_introduction);
b.setColorRes(R.color.briar_primary); b.setColorRes(R.color.briar_primary);
b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentTitle(appContext.getText(R.string.app_name));
b.setContentText(appContext.getResources().getQuantityString( b.setContentText(appContext.getResources().getQuantityString(
R.plurals.contact_added_notification_text, contactAddedTotal, R.plurals.introduction_notification_text, introductionTotal,
contactAddedTotal)); introductionTotal));
b.setNotificationCategory(CATEGORY_MESSAGE); b.setNotificationCategory(CATEGORY_MESSAGE);
setAlertProperties(b); setAlertProperties(b);
setDeleteIntent(b, CONTACT_ADDED_URI); setDeleteIntent(b, INTRODUCTION_URI);
// Touching the notification shows the contact list // Touching the notification shows the contact list
Intent i = new Intent(appContext, NavDrawerActivity.class); Intent i = new Intent(appContext, NavDrawerActivity.class);
i.putExtra(INTENT_CONTACTS, true); i.putExtra(INTENT_CONTACTS, true);
@@ -591,13 +591,14 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
t.addNextIntent(i); t.addNextIntent(i);
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
notificationManager.notify(CONTACT_ADDED_NOTIFICATION_ID, notificationManager.notify(INTRODUCTION_SUCCESS_NOTIFICATION_ID,
b.build()); b.build());
} }
@Override @Override
public void clearAllContactAddedNotifications() { public void clearAllIntroductionNotifications() {
androidExecutor.runOnUiThread(this::clearContactAddedNotification); androidExecutor.runOnUiThread(
this::clearIntroductionSuccessNotification);
} }
@Override @Override

View File

@@ -226,7 +226,7 @@ public class AppModule {
@Singleton @Singleton
RecentEmoji provideRecentEmoji(LifecycleManager lifecycleManager, RecentEmoji provideRecentEmoji(LifecycleManager lifecycleManager,
RecentEmojiImpl recentEmoji) { RecentEmojiImpl recentEmoji) {
lifecycleManager.registerOpenDatabaseHook(recentEmoji); lifecycleManager.registerClient(recentEmoji);
return recentEmoji; return recentEmoji;
} }
} }

View File

@@ -12,7 +12,7 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.BLOG
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_URI; import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_URI; import static org.briarproject.briar.api.android.AndroidNotificationManager.FORUM_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_URI; import static org.briarproject.briar.api.android.AndroidNotificationManager.GROUP_URI;
import static org.briarproject.briar.api.android.AndroidNotificationManager.CONTACT_ADDED_URI; import static org.briarproject.briar.api.android.AndroidNotificationManager.INTRODUCTION_URI;
public class NotificationCleanupService extends IntentService { public class NotificationCleanupService extends IntentService {
@@ -46,8 +46,8 @@ public class NotificationCleanupService extends IntentService {
notificationManager.clearAllForumPostNotifications(); notificationManager.clearAllForumPostNotifications();
} else if (uri.equals(BLOG_URI)) { } else if (uri.equals(BLOG_URI)) {
notificationManager.clearAllBlogPostNotifications(); notificationManager.clearAllBlogPostNotifications();
} else if (uri.equals(CONTACT_ADDED_URI)) { } else if (uri.equals(INTRODUCTION_URI)) {
notificationManager.clearAllContactAddedNotifications(); notificationManager.clearAllIntroductionNotifications();
} }
} }
} }

View File

@@ -8,11 +8,11 @@ import com.vanniktech.emoji.emoji.Emoji;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.OpenDatabaseHook;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
@@ -30,7 +30,7 @@ import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class RecentEmojiImpl implements RecentEmoji, OpenDatabaseHook { class RecentEmojiImpl implements RecentEmoji, Client {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(RecentEmojiImpl.class.getName()); Logger.getLogger(RecentEmojiImpl.class.getName());
@@ -72,7 +72,7 @@ class RecentEmojiImpl implements RecentEmoji, OpenDatabaseHook {
} }
@Override @Override
public void onDatabaseOpened(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
Settings settings = Settings settings =
settingsManager.getSettings(txn, SETTINGS_NAMESPACE); settingsManager.getSettings(txn, SETTINGS_NAMESPACE);
String serialized = settings.get(EMOJI_LRU_PREFERENCE); String serialized = settings.get(EMOJI_LRU_PREFERENCE);

View File

@@ -36,9 +36,4 @@ public interface TestingConstants {
*/ */
boolean FEATURE_FLAG_IMAGE_ATTACHMENTS = IS_DEBUG_BUILD; boolean FEATURE_FLAG_IMAGE_ATTACHMENTS = IS_DEBUG_BUILD;
/**
* Feature flag for enabling adding contacts at a distance.
*/
boolean FEATURE_FLAG_REMOTE_CONTACTS = IS_DEBUG_BUILD;
} }

View File

@@ -15,12 +15,8 @@ import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity; import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity; import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity; import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
import org.briarproject.briar.android.contact.add.remote.LinkExchangeFragment;
import org.briarproject.briar.android.contact.ContactListFragment; import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.ContactModule; import org.briarproject.briar.android.contact.ContactModule;
import org.briarproject.briar.android.contact.add.remote.NicknameFragment;
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
import org.briarproject.briar.android.conversation.AliasDialogFragment; import org.briarproject.briar.android.conversation.AliasDialogFragment;
import org.briarproject.briar.android.conversation.ConversationActivity; import org.briarproject.briar.android.conversation.ConversationActivity;
import org.briarproject.briar.android.conversation.ImageActivity; import org.briarproject.briar.android.conversation.ImageActivity;
@@ -172,10 +168,6 @@ public interface ActivityComponent {
void inject(UnlockActivity activity); void inject(UnlockActivity activity);
void inject(AddContactActivity activity);
void inject(PendingContactListActivity activity);
// Fragments // Fragments
void inject(AuthorNameFragment fragment); void inject(AuthorNameFragment fragment);
@@ -199,10 +191,6 @@ public interface ActivityComponent {
void inject(KeyAgreementFragment fragment); void inject(KeyAgreementFragment fragment);
void inject(LinkExchangeFragment fragment);
void inject(NicknameFragment fragment);
void inject(ContactChooserFragment fragment); void inject(ContactChooserFragment fragment);
void inject(ShareForumFragment fragment); void inject(ShareForumFragment fragment);

View File

@@ -5,6 +5,8 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
@@ -32,7 +34,6 @@ import org.briarproject.briar.android.controller.handler.UiResultExceptionHandle
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity; import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
import org.briarproject.briar.android.sharing.ShareBlogActivity; import org.briarproject.briar.android.sharing.ShareBlogActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -43,7 +44,6 @@ import javax.inject.Inject;
import static android.app.Activity.RESULT_OK; import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.design.widget.Snackbar.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@@ -356,12 +356,17 @@ public class BlogFragment extends BaseFragment
} }
private void displaySnackbar(int stringId, boolean scroll) { private void displaySnackbar(int stringId, boolean scroll) {
BriarSnackbarBuilder sb = new BriarSnackbarBuilder(); Snackbar snackbar =
Snackbar.make(list, stringId, Snackbar.LENGTH_LONG);
snackbar.getView().setBackgroundResource(R.color.briar_primary);
if (scroll) { if (scroll) {
sb.setAction(R.string.blogs_blog_post_scroll_to, View.OnClickListener onClick = v -> list.smoothScrollToPosition(0);
v -> list.smoothScrollToPosition(0)); snackbar.setActionTextColor(ContextCompat
.getColor(getContext(),
R.color.briar_button_text_positive));
snackbar.setAction(R.string.blogs_blog_post_scroll_to, onClick);
} }
sb.make(list, stringId, LENGTH_LONG).show(); snackbar.show();
} }
private void showDeleteDialog() { private void showDeleteDialog() {

View File

@@ -4,12 +4,15 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -20,7 +23,6 @@ import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.FeedController.FeedListener; import org.briarproject.briar.android.blog.FeedController.FeedListener;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -268,12 +270,16 @@ public class FeedFragment extends BaseFragment implements
int count = adapter.getItemCount(); int count = adapter.getItemCount();
boolean scroll = count > (lastVisible - firstVisible + 1); boolean scroll = count > (lastVisible - firstVisible + 1);
BriarSnackbarBuilder sb = new BriarSnackbarBuilder(); Snackbar s = Snackbar.make(list, stringRes, LENGTH_LONG);
s.getView().setBackgroundResource(R.color.briar_primary);
if (scroll) { if (scroll) {
sb.setAction(R.string.blogs_blog_post_scroll_to, OnClickListener onClick = v -> list.smoothScrollToPosition(0);
v -> list.smoothScrollToPosition(0)); s.setActionTextColor(ContextCompat
.getColor(getContext(),
R.color.briar_button_text_positive));
s.setAction(R.string.blogs_blog_post_scroll_to, onClick);
} }
sb.make(list, stringRes, LENGTH_LONG).show(); s.show();
} }
@Override @Override

View File

@@ -3,25 +3,22 @@ package org.briarproject.briar.android.contact;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.util.Pair; import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.TextView;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
@@ -35,12 +32,9 @@ import org.briarproject.bramble.api.plugin.event.ContactDisconnectedEvent;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity;
import org.briarproject.briar.android.conversation.ConversationActivity; import org.briarproject.briar.android.conversation.ConversationActivity;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.keyagreement.ContactExchangeActivity; import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@@ -55,28 +49,20 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
import io.github.kobakei.materialfabspeeddial.FabSpeedDialMenu;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation; import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.getTransitionName; import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_REMOTE_CONTACTS;
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID; import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
import static org.briarproject.briar.android.util.UiUtils.isSamsung7; import static org.briarproject.briar.android.util.UiUtils.isSamsung7;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class ContactListFragment extends BaseFragment implements EventListener, public class ContactListFragment extends BaseFragment implements EventListener {
OnMenuItemClickListener {
public static final String TAG = ContactListFragment.class.getName(); public static final String TAG = ContactListFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG); private static final Logger LOG = Logger.getLogger(TAG);
@@ -90,7 +76,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
private ContactListAdapter adapter; private ContactListAdapter adapter;
private BriarRecyclerView list; private BriarRecyclerView list;
private Snackbar snackbar;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
@@ -122,23 +107,7 @@ public class ContactListFragment extends BaseFragment implements EventListener,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
requireNonNull(getActivity()).setTitle(R.string.contact_list_button); requireNonNull(getActivity()).setTitle(R.string.contact_list_button);
View contentView = inflater.inflate(R.layout.fragment_contact_list, View contentView = inflater.inflate(R.layout.list, container, false);
container, false);
FabSpeedDial speedDial = contentView.findViewById(R.id.speedDial);
if (FEATURE_FLAG_REMOTE_CONTACTS) {
speedDial.addOnMenuItemClickListener(this);
} else {
speedDial.setMenu(new FabSpeedDialMenu(contentView.getContext()));
speedDial.addOnStateChangeListener(open -> {
if (open) {
Intent intent = new Intent(getContext(),
ContactExchangeActivity.class);
startActivity(intent);
speedDial.closeMenu();
}
});
}
OnContactClickListener<ContactListItem> onContactClickListener = OnContactClickListener<ContactListItem> onContactClickListener =
(view, item) -> { (view, item) -> {
@@ -177,28 +146,26 @@ public class ContactListFragment extends BaseFragment implements EventListener,
list.setEmptyText(getString(R.string.no_contacts)); list.setEmptyText(getString(R.string.no_contacts));
list.setEmptyAction(getString(R.string.no_contacts_action)); list.setEmptyAction(getString(R.string.no_contacts_action));
snackbar = new BriarSnackbarBuilder()
.setAction(R.string.show, v ->
startActivity(new Intent(getContext(),
PendingContactListActivity.class)))
.make(contentView, R.string.pending_contact_requests_snackbar,
LENGTH_INDEFINITE);
return contentView; return contentView;
} }
@Override @Override
public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v, public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
int itemId) { inflater.inflate(R.menu.contact_list_actions, menu);
switch (itemId) { super.onCreateOptionsMenu(menu, inflater);
case R.id.action_add_contact_nearby: }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_add_contact:
Intent intent = Intent intent =
new Intent(getContext(), ContactExchangeActivity.class); new Intent(getContext(), ContactExchangeActivity.class);
startActivity(intent); startActivity(intent);
return; return true;
case R.id.action_add_contact_remotely: default:
startActivity( return super.onOptionsItemSelected(item);
new Intent(getContext(), AddContactActivity.class));
} }
} }
@@ -207,26 +174,11 @@ public class ContactListFragment extends BaseFragment implements EventListener,
super.onStart(); super.onStart();
eventBus.addListener(this); eventBus.addListener(this);
notificationManager.clearAllContactNotifications(); notificationManager.clearAllContactNotifications();
notificationManager.clearAllContactAddedNotifications(); notificationManager.clearAllIntroductionNotifications();
loadContacts(); loadContacts();
checkForPendingContacts();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
} }
private void checkForPendingContacts() {
listener.runOnDbThread(() -> {
try {
if (contactManager.getPendingContacts().isEmpty()) {
runOnUiThreadUnlessDestroyed(() -> snackbar.dismiss());
} else {
runOnUiThreadUnlessDestroyed(() -> snackbar.show());
}
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
@@ -293,16 +245,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
(ConversationMessageReceivedEvent) e; (ConversationMessageReceivedEvent) e;
ConversationMessageHeader h = p.getMessageHeader(); ConversationMessageHeader h = p.getMessageHeader();
updateItem(p.getContactId(), h); updateItem(p.getContactId(), h);
} else if (e instanceof PendingContactStateChangedEvent) {
PendingContactStateChangedEvent pe =
(PendingContactStateChangedEvent) e;
// only re-check pending contacts for initial state
if (pe.getPendingContactState() == WAITING_FOR_CONNECTION) {
checkForPendingContacts();
}
} else if (e instanceof PendingContactRemovedEvent ||
e instanceof ContactAddedRemotelyEvent) {
checkForPendingContacts();
} }
} }

View File

@@ -1,105 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.MenuItem;
import android.widget.Toast;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.ACTION_VIEW;
import static android.content.Intent.EXTRA_TEXT;
import static android.widget.Toast.LENGTH_LONG;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class AddContactActivity extends BriarActivity implements
BaseFragmentListener {
@Inject
ViewModelProvider.Factory viewModelFactory;
private AddContactViewModel viewModel;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_fragment_container);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(AddContactViewModel.class);
viewModel.getRemoteLinkEntered().observeEvent(this, entered -> {
if (entered) {
NicknameFragment f = new NicknameFragment();
showNextFragment(f);
}
});
Intent i = getIntent();
if (i != null) {
onNewIntent(i);
setIntent(null); // don't keep the intent for configuration changes
}
if (state == null) {
showInitialFragment(new LinkExchangeFragment());
}
}
@Override
protected void onNewIntent(Intent i) {
super.onNewIntent(i);
String action = i.getAction();
if (ACTION_SEND.equals(action) || ACTION_VIEW.equals(action)) {
String text = i.getStringExtra(EXTRA_TEXT);
String uri = i.getDataString();
if (text != null) handleIncomingLink(text);
else if (uri != null) handleIncomingLink(uri);
}
}
private void handleIncomingLink(String link) {
if (link.equals(viewModel.getHandshakeLink().getValue())) {
Toast.makeText(this, R.string.intent_own_link, LENGTH_LONG)
.show();
} else if (viewModel.isValidRemoteContactLink(link)) {
viewModel.setRemoteHandshakeLink(link);
} else {
Toast.makeText(this, R.string.invalid_link, LENGTH_LONG)
.show();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

View File

@@ -1,112 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
public class AddContactViewModel extends AndroidViewModel {
private final static Logger LOG =
getLogger(AddContactViewModel.class.getName());
private final ContactManager contactManager;
@DatabaseExecutor
private final Executor dbExecutor;
private final MutableLiveData<String> handshakeLink =
new MutableLiveData<>();
private final MutableLiveEvent<Boolean> remoteLinkEntered =
new MutableLiveEvent<>();
private final MutableLiveData<Boolean> addContactResult =
new MutableLiveData<>();
@Nullable
private String remoteHandshakeLink;
@Inject
public AddContactViewModel(@NonNull Application application,
ContactManager contactManager,
@DatabaseExecutor Executor dbExecutor) {
super(application);
this.contactManager = contactManager;
this.dbExecutor = dbExecutor;
loadHandshakeLink();
}
private void loadHandshakeLink() {
dbExecutor.execute(() -> {
try {
handshakeLink.postValue(contactManager.getHandshakeLink());
} catch (DbException e) {
logException(LOG, WARNING, e);
// the UI should stay disable in this case,
// leaving the user unable to proceed
}
});
}
LiveData<String> getHandshakeLink() {
return handshakeLink;
}
@Nullable
String getRemoteHandshakeLink() {
return remoteHandshakeLink;
}
void setRemoteHandshakeLink(String link) {
remoteHandshakeLink = link;
}
boolean isValidRemoteContactLink(@Nullable CharSequence link) {
return link != null && LINK_REGEX.matcher(link).find();
}
LiveEvent<Boolean> getRemoteLinkEntered() {
return remoteLinkEntered;
}
void onRemoteLinkEntered() {
if (remoteHandshakeLink == null) throw new IllegalStateException();
remoteLinkEntered.setEvent(true);
}
void addContact(String nickname) {
if (remoteHandshakeLink == null) throw new IllegalStateException();
dbExecutor.execute(() -> {
try {
contactManager.addPendingContact(remoteHandshakeLink, nickname);
addContactResult.postValue(true);
} catch (DbException | FormatException e) {
logException(LOG, WARNING, e);
addContactResult.postValue(false);
}
});
}
LiveData<Boolean> getAddContactResult() {
return addContactResult;
}
}

View File

@@ -1,164 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.os.Bundle;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.ShareCompat.IntentBuilder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import java.util.regex.Matcher;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.content.Context.CLIPBOARD_SERVICE;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Objects.requireNonNull;
import static org.briarproject.bramble.api.contact.HandshakeLinkConstants.LINK_REGEX;
import static org.briarproject.briar.android.util.UiUtils.observeOnce;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class LinkExchangeFragment extends BaseFragment {
private static final String TAG = LinkExchangeFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private AddContactViewModel viewModel;
private ClipboardManager clipboard;
private TextInputLayout linkInputLayout;
private TextInputEditText linkInput;
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (getActivity() == null || getContext() == null) return null;
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
.get(AddContactViewModel.class);
View v = inflater.inflate(R.layout.fragment_link_exchange,
container, false);
linkInputLayout = v.findViewById(R.id.linkInputLayout);
linkInput = v.findViewById(R.id.linkInput);
if (viewModel.getRemoteHandshakeLink() != null) {
// This can happen if the link was set via an incoming Intent
linkInput.setText(viewModel.getRemoteHandshakeLink());
}
clipboard = (ClipboardManager) requireNonNull(
getContext().getSystemService(CLIPBOARD_SERVICE));
Button pasteButton = v.findViewById(R.id.pasteButton);
pasteButton.setOnClickListener(view -> {
ClipData clipData = clipboard.getPrimaryClip();
if (clipData != null && clipData.getItemCount() > 0)
linkInput.setText(clipData.getItemAt(0).getText());
});
observeOnce(viewModel.getHandshakeLink(), this,
this::onHandshakeLinkLoaded);
return v;
}
private void onHandshakeLinkLoaded(String link) {
View v = requireNonNull(getView());
TextView linkView = v.findViewById(R.id.linkView);
linkView.setText(link);
Button copyButton = v.findViewById(R.id.copyButton);
ClipData clip = ClipData.newPlainText(
getString(R.string.link_clip_label), link);
copyButton.setOnClickListener(view -> {
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), R.string.link_copied_toast,
LENGTH_SHORT).show();
});
copyButton.setEnabled(true);
Button shareButton = v.findViewById(R.id.shareButton);
shareButton.setOnClickListener(view ->
IntentBuilder.from(requireActivity())
.setText(link)
.setType("text/plain")
.startChooser());
shareButton.setEnabled(true);
Button continueButton = v.findViewById(R.id.addButton);
continueButton.setOnClickListener(view -> onContinueButtonClicked());
continueButton.setEnabled(true);
}
/**
* Requires {@link AddContactViewModel#getHandshakeLink()} to be loaded.
*/
@Nullable
private String getRemoteHandshakeLinkOrNull() {
CharSequence link = linkInput.getText();
if (link == null || link.length() == 0) {
linkInputLayout.setError(getString(R.string.missing_link));
linkInput.requestFocus();
return null;
}
Matcher matcher = LINK_REGEX.matcher(link);
if (matcher.find()) {
String linkWithoutSchema = matcher.group(2);
// Check also if this is our own link. This was loaded already,
// because it enables the Continue button which is the only caller.
if (("briar://" + linkWithoutSchema)
.equals(viewModel.getHandshakeLink().getValue())) {
linkInputLayout.setError(getString(R.string.own_link_error));
linkInput.requestFocus();
return null;
}
linkInputLayout.setError(null);
return linkWithoutSchema;
}
linkInputLayout.setError(getString(R.string.invalid_link));
linkInput.requestFocus();
return null;
}
private void onContinueButtonClicked() {
String link = getRemoteHandshakeLinkOrNull();
if (link == null) return; // invalid link
viewModel.setRemoteHandshakeLink(link);
viewModel.onRemoteLinkEntered();
}
}

View File

@@ -1,121 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_LONG;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.util.StringUtils.utf8IsTooLong;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class NicknameFragment extends BaseFragment {
private static final String TAG = NicknameFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private AddContactViewModel viewModel;
private TextInputLayout contactNameLayout;
private TextInputEditText contactNameInput;
private Button addButton;
private ProgressBar progressBar;
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (getActivity() == null || getContext() == null) return null;
View v = inflater.inflate(R.layout.fragment_nickname,
container, false);
viewModel = ViewModelProviders.of(getActivity(), viewModelFactory)
.get(AddContactViewModel.class);
contactNameLayout = v.findViewById(R.id.contactNameLayout);
contactNameInput = v.findViewById(R.id.contactNameInput);
addButton = v.findViewById(R.id.addButton);
addButton.setOnClickListener(view -> onAddButtonClicked());
progressBar = v.findViewById(R.id.progressBar);
return v;
}
@Nullable
private String getNicknameOrNull() {
Editable name = contactNameInput.getText();
if (name == null || name.toString().trim().length() == 0) {
contactNameLayout.setError(getString(R.string.nickname_missing));
contactNameInput.requestFocus();
return null;
}
if (utf8IsTooLong(name.toString(), MAX_AUTHOR_NAME_LENGTH)) {
contactNameLayout.setError(getString(R.string.name_too_long));
contactNameInput.requestFocus();
return null;
}
contactNameLayout.setError(null);
return name.toString().trim();
}
private void onAddButtonClicked() {
String name = getNicknameOrNull();
if (name == null) return; // invalid nickname
addButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
viewModel.getAddContactResult().observe(this, success -> {
if (success == null) return;
if (success) {
Intent intent = new Intent(getActivity(),
PendingContactListActivity.class);
startActivity(intent);
} else {
Toast.makeText(getContext(), R.string.adding_contact_error,
LENGTH_LONG).show();
}
finish();
});
viewModel.addContact(name);
}
}

View File

@@ -1,105 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.LinearLayoutManager;
import android.view.MenuItem;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.view.BriarRecyclerView;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class PendingContactListActivity extends BriarActivity
implements PendingContactListener {
@Inject
ViewModelProvider.Factory viewModelFactory;
private PendingContactListViewModel viewModel;
private PendingContactListAdapter adapter;
private BriarRecyclerView list;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.list);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(PendingContactListViewModel.class);
viewModel.getPendingContacts()
.observe(this, this::onPendingContactsChanged);
adapter = new PendingContactListAdapter(this, this, PendingContact.class);
list = findViewById(R.id.list);
list.setEmptyText(R.string.no_pending_contacts);
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
list.showProgressBar();
}
@Override
public void onStart() {
super.onStart();
list.startPeriodicUpdate();
}
@Override
protected void onStop() {
super.onStop();
list.stopPeriodicUpdate();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void onFailedPendingContactRemoved(PendingContact pendingContact) {
viewModel.removePendingContact(pendingContact.getId());
}
private void onPendingContactsChanged(Collection<PendingContact> contacts) {
if (contacts.isEmpty()) {
if (adapter.isEmpty()) {
list.showData(); // hides progress bar, shows empty text
} else {
// all previous contacts have been removed, so we can finish
supportFinishAfterTransition();
}
} else {
adapter.setItems(contacts);
}
}
}

View File

@@ -1,59 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter;
@NotNullByDefault
class PendingContactListAdapter extends
BriarAdapter<PendingContact, PendingContactViewHolder> {
private final PendingContactListener listener;
PendingContactListAdapter(Context ctx, PendingContactListener listener,
Class<PendingContact> c) {
super(ctx, c);
this.listener = listener;
}
@Override
public PendingContactViewHolder onCreateViewHolder(ViewGroup viewGroup,
int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_pending_contact, viewGroup, false);
return new PendingContactViewHolder(v, listener);
}
@Override
public void onBindViewHolder(
PendingContactViewHolder pendingContactViewHolder, int i) {
pendingContactViewHolder.bind(items.get(i));
}
@Override
public int compare(PendingContact item1, PendingContact item2) {
return (int) (item1.getTimestamp() - item2.getTimestamp());
}
@Override
public boolean areContentsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getId().equals(item2.getId()) &&
item1.getAlias().equals(item2.getAlias()) &&
item1.getTimestamp() == item2.getTimestamp() &&
item1.getState() == item2.getState();
}
@Override
public boolean areItemsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getId().equals(item2.getId());
}
}

View File

@@ -1,97 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedRemotelyEvent;
import org.briarproject.bramble.api.contact.event.PendingContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
public class PendingContactListViewModel extends AndroidViewModel
implements EventListener {
private final Logger LOG =
getLogger(PendingContactListViewModel.class.getName());
@DatabaseExecutor
private final Executor dbExecutor;
private final ContactManager contactManager;
private final EventBus eventBus;
private final MutableLiveData<Collection<PendingContact>> pendingContacts =
new MutableLiveData<>();
@Inject
public PendingContactListViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
ContactManager contactManager, EventBus eventBus) {
super(application);
this.dbExecutor = dbExecutor;
this.contactManager = contactManager;
this.eventBus = eventBus;
this.eventBus.addListener(this);
loadPendingContacts();
}
@Override
protected void onCleared() {
super.onCleared();
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedRemotelyEvent ||
e instanceof PendingContactStateChangedEvent ||
e instanceof PendingContactRemovedEvent) {
loadPendingContacts();
}
}
private void loadPendingContacts() {
dbExecutor.execute(() -> {
try {
pendingContacts.postValue(contactManager.getPendingContacts());
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
LiveData<Collection<PendingContact>> getPendingContacts() {
return pendingContacts;
}
void removePendingContact(PendingContactId id) {
dbExecutor.execute(() -> {
try {
contactManager.removePendingContact(id);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
});
}
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import org.briarproject.bramble.api.contact.PendingContact;
interface PendingContactListener {
void onFailedPendingContactRemoved(PendingContact pendingContact);
}

View File

@@ -1,77 +0,0 @@
package org.briarproject.briar.android.contact.add.remote;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.TextAvatarView;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.util.UiUtils.formatDate;
@NotNullByDefault
class PendingContactViewHolder extends ViewHolder {
private final PendingContactListener listener;
private final TextAvatarView avatar;
private final TextView name;
private final TextView time;
private final TextView status;
private final Button removeButton;
PendingContactViewHolder(View v, PendingContactListener listener) {
super(v);
avatar = v.findViewById(R.id.avatar);
name = v.findViewById(R.id.name);
time = v.findViewById(R.id.time);
status = v.findViewById(R.id.status);
removeButton = v.findViewById(R.id.removeButton);
this.listener = listener;
}
public void bind(PendingContact item) {
avatar.setText(item.getAlias());
avatar.setBackgroundBytes(item.getId().getBytes());
name.setText(item.getAlias());
time.setText(formatDate(time.getContext(), item.getTimestamp()));
removeButton.setOnClickListener(v -> {
listener.onFailedPendingContactRemoved(item);
removeButton.setEnabled(false);
});
int color = ContextCompat
.getColor(status.getContext(), R.color.briar_green);
int buttonVisibility = GONE;
switch (item.getState()) {
case WAITING_FOR_CONNECTION:
color = ContextCompat
.getColor(status.getContext(), R.color.briar_yellow);
status.setText(R.string.waiting_for_contact_to_come_online);
break;
case CONNECTED:
status.setText(R.string.connecting);
break;
case ADDING_CONTACT:
status.setText(R.string.adding_contact);
break;
case FAILED:
color = ContextCompat
.getColor(status.getContext(), R.color.briar_red);
status.setText(R.string.adding_contact_failed);
buttonVisibility = VISIBLE;
break;
default:
throw new IllegalStateException();
}
status.setTextColor(color);
removeButton.setVisibility(buttonVisibility);
removeButton.setEnabled(true);
}
}

View File

@@ -59,7 +59,6 @@ import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache
import org.briarproject.briar.android.forum.ForumActivity; import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity; import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.view.ImagePreview; import org.briarproject.briar.android.view.ImagePreview;
import org.briarproject.briar.android.view.TextAttachmentController; import org.briarproject.briar.android.view.TextAttachmentController;
@@ -297,10 +296,10 @@ public class ConversationActivity extends BriarActivity
super.onActivityResult(request, result, data); super.onActivityResult(request, result, data);
if (request == REQUEST_INTRODUCTION && result == RESULT_OK) { if (request == REQUEST_INTRODUCTION && result == RESULT_OK) {
new BriarSnackbarBuilder() Snackbar snackbar = Snackbar.make(list, R.string.introduction_sent,
.make(list, R.string.introduction_sent, Snackbar.LENGTH_SHORT);
Snackbar.LENGTH_SHORT) snackbar.getView().setBackgroundResource(R.color.briar_primary);
.show(); snackbar.show();
} else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) { } else if (request == REQUEST_ATTACH_IMAGE && result == RESULT_OK) {
// remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS // remove cast when removing FEATURE_FLAG_IMAGE_ATTACHMENTS
((TextAttachmentController) sendController).onImageReceived(data); ((TextAttachmentController) sendController).onImageReceived(data);
@@ -363,7 +362,7 @@ public class ConversationActivity extends BriarActivity
if (enable != null && enable) { if (enable != null && enable) {
menu.findItem(R.id.action_introduction).setEnabled(true); menu.findItem(R.id.action_introduction).setEnabled(true);
// show introduction onboarding, if needed // show introduction onboarding, if needed
viewModel.showIntroductionOnboarding().observeEvent(this, observeOnce(viewModel.showIntroductionOnboarding(), this,
this::showIntroductionOnboarding); this::showIntroductionOnboarding);
} }
}); });
@@ -474,8 +473,9 @@ public class ConversationActivity extends BriarActivity
if (revision == adapter.getRevision()) { if (revision == adapter.getRevision()) {
adapter.incrementRevision(); adapter.incrementRevision();
textInputView.setReady(true); textInputView.setReady(true);
// start observing onboarding after enabling // start observing onboarding after enabling (only once, because
viewModel.showImageOnboarding().observeEvent(this, // we only update this when an onboarding should be shown)
observeOnce(viewModel.showImageOnboarding(), this,
this::showImageOnboarding); this::showImageOnboarding);
List<ConversationItem> items = createItems(headers); List<ConversationItem> items = createItems(headers);
adapter.addAll(items); adapter.addAll(items);
@@ -498,6 +498,7 @@ public class ConversationActivity extends BriarActivity
* <p> * <p>
* Attention: Call this only after contactName has been initialized. * Attention: Call this only after contactName has been initialized.
*/ */
@SuppressWarnings("ConstantConditions")
private List<ConversationItem> createItems( private List<ConversationItem> createItems(
Collection<ConversationMessageHeader> headers) { Collection<ConversationMessageHeader> headers) {
List<ConversationItem> items = new ArrayList<>(headers.size()); List<ConversationItem> items = new ArrayList<>(headers.size());
@@ -713,8 +714,8 @@ public class ConversationActivity extends BriarActivity
}); });
} }
private void showImageOnboarding(Boolean show) { private void showImageOnboarding(@Nullable Boolean show) {
if (!show) return; if (show == null || !show) return;
if (SDK_INT >= 21) { if (SDK_INT >= 21) {
// show onboarding only after the enter transition has ended // show onboarding only after the enter transition has ended
// otherwise the tap target animation won't play // otherwise the tap target animation won't play

View File

@@ -28,8 +28,6 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.messaging.Attachment; import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.AttachmentHeader; import org.briarproject.briar.api.messaging.AttachmentHeader;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
@@ -89,10 +87,10 @@ public class ConversationViewModel extends AndroidViewModel {
Transformations.map(contact, UiUtils::getContactDisplayName); Transformations.map(contact, UiUtils::getContactDisplayName);
private final MutableLiveData<Boolean> imageSupport = private final MutableLiveData<Boolean> imageSupport =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveEvent<Boolean> showImageOnboarding = private final MutableLiveData<Boolean> showImageOnboarding =
new MutableLiveEvent<>(); new MutableLiveData<>();
private final MutableLiveEvent<Boolean> showIntroductionOnboarding = private final MutableLiveData<Boolean> showIntroductionOnboarding =
new MutableLiveEvent<>(); new MutableLiveData<>();
private final MutableLiveData<Boolean> showIntroductionAction = private final MutableLiveData<Boolean> showIntroductionAction =
new MutableLiveData<>(); new MutableLiveData<>();
private final MutableLiveData<Boolean> contactDeleted = private final MutableLiveData<Boolean> contactDeleted =
@@ -214,30 +212,32 @@ public class ConversationViewModel extends AndroidViewModel {
if (imagesSupported && if (imagesSupported &&
settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) { settings.getBoolean(SHOW_ONBOARDING_IMAGE, true)) {
// check if we should show onboarding, only if images are supported // check if we should show onboarding, only if images are supported
showImageOnboarding.postEvent(true); showImageOnboarding.postValue(true);
// allow observer to stop listening for changes // allow observer to stop listening for changes
showIntroductionOnboarding.postEvent(false); showIntroductionOnboarding.postValue(false);
} else { } else {
// allow observer to stop listening for changes // allow observer to stop listening for changes
showImageOnboarding.postEvent(false); showImageOnboarding.postValue(false);
// we only show one onboarding dialog at a time // we only show one onboarding dialog at a time
if (introductionSupported && if (introductionSupported &&
settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION, true)) { settings.getBoolean(SHOW_ONBOARDING_INTRODUCTION, true)) {
showIntroductionOnboarding.postEvent(true); showIntroductionOnboarding.postValue(true);
} else { } else {
// allow observer to stop listening for changes // allow observer to stop listening for changes
showIntroductionOnboarding.postEvent(false); showIntroductionOnboarding.postValue(false);
} }
} }
} }
@UiThread @UiThread
void onImageOnboardingSeen() { void onImageOnboardingSeen() {
showImageOnboarding.setValue(false);
onOnboardingSeen(SHOW_ONBOARDING_IMAGE); onOnboardingSeen(SHOW_ONBOARDING_IMAGE);
} }
@UiThread @UiThread
void onIntroductionOnboardingSeen() { void onIntroductionOnboardingSeen() {
showIntroductionOnboarding.setValue(false);
onOnboardingSeen(SHOW_ONBOARDING_INTRODUCTION); onOnboardingSeen(SHOW_ONBOARDING_INTRODUCTION);
} }
@@ -365,11 +365,11 @@ public class ConversationViewModel extends AndroidViewModel {
return imageSupport; return imageSupport;
} }
LiveEvent<Boolean> showImageOnboarding() { LiveData<Boolean> showImageOnboarding() {
return showImageOnboarding; return showImageOnboarding;
} }
LiveEvent<Boolean> showIntroductionOnboarding() { LiveData<Boolean> showIntroductionOnboarding() {
return showIntroductionOnboarding; return showIntroductionOnboarding;
} }

View File

@@ -9,6 +9,7 @@ import android.os.Bundle;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi; import android.support.annotation.RequiresApi;
import android.support.design.widget.AppBarLayout; import android.support.design.widget.AppBarLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter; import android.support.v4.app.FragmentStatePagerAdapter;
@@ -31,7 +32,6 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.PullDownLayout; import org.briarproject.briar.android.view.PullDownLayout;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@@ -100,8 +100,7 @@ public class ImageActivity extends BriarActivity
// get View Model // get View Model
viewModel = ViewModelProviders.of(this, viewModelFactory) viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(ImageViewModel.class); .get(ImageViewModel.class);
viewModel.getSaveState().observeEvent(this, viewModel.getSaveState().observe(this, this::onImageSaveStateChanged);
this::onImageSaveStateChanged);
// inflate layout // inflate layout
setContentView(R.layout.activity_image); setContentView(R.layout.activity_image);
@@ -142,8 +141,7 @@ public class ImageActivity extends BriarActivity
viewPager.setCurrentItem(position); viewPager.setCurrentItem(position);
if (SDK_INT >= 16) { if (SDK_INT >= 16) {
viewModel.getOnImageClicked() viewModel.getOnImageClicked().observe(this, this::onImageClicked);
.observeEvent(this, this::onImageClicked);
window.getDecorView().setSystemUiVisibility(UI_FLAGS_DEFAULT); window.getDecorView().setSystemUiVisibility(UI_FLAGS_DEFAULT);
} }
} }
@@ -224,6 +222,7 @@ public class ImageActivity extends BriarActivity
private void onImageClicked(@Nullable Boolean clicked) { private void onImageClicked(@Nullable Boolean clicked) {
if (clicked != null && clicked) { if (clicked != null && clicked) {
toggleSystemUi(); toggleSystemUi();
viewModel.onOnImageClickSeen();
} }
} }
@@ -309,10 +308,10 @@ public class ImageActivity extends BriarActivity
R.string.save_image_error : R.string.save_image_success; R.string.save_image_error : R.string.save_image_success;
int colorRes = error ? int colorRes = error ?
R.color.briar_red : R.color.briar_primary; R.color.briar_red : R.color.briar_primary;
new BriarSnackbarBuilder() Snackbar s = Snackbar.make(layout, stringRes, LENGTH_LONG);
.setBackgroundColor(colorRes) s.getView().setBackgroundResource(colorRes);
.make(layout, stringRes, LENGTH_LONG) s.show();
.show(); viewModel.onSaveStateSeen();
} }
AttachmentItem getVisibleAttachment() { AttachmentItem getVisibleAttachment() {

View File

@@ -2,6 +2,8 @@ package org.briarproject.briar.android.conversation;
import android.app.Application; import android.app.Application;
import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -13,8 +15,6 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.messaging.Attachment; import org.briarproject.briar.api.messaging.Attachment;
import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.MessagingManager;
@@ -53,10 +53,9 @@ public class ImageViewModel extends AndroidViewModel {
/** /**
* true means there was an error saving the image, false if image was saved. * true means there was an error saving the image, false if image was saved.
*/ */
private final MutableLiveEvent<Boolean> saveState = private final MutableLiveData<Boolean> saveState = new MutableLiveData<>();
new MutableLiveEvent<>(); private final MutableLiveData<Boolean> imageClicked =
private final MutableLiveEvent<Boolean> imageClicked = new MutableLiveData<>();
new MutableLiveEvent<>();
private int toolbarTop, toolbarBottom; private int toolbarTop, toolbarBottom;
@Inject @Inject
@@ -71,17 +70,24 @@ public class ImageViewModel extends AndroidViewModel {
} }
void clickImage() { void clickImage() {
imageClicked.setEvent(true); imageClicked.setValue(true);
} }
/** /**
* A LiveEvent that is true if the image was clicked, * A LiveData that is true if the image was clicked,
* false if it wasn't. * false if it wasn't.
*
* Call {@link #onOnImageClickSeen()} after consuming an update.
*/ */
LiveEvent<Boolean> getOnImageClicked() { LiveData<Boolean> getOnImageClicked() {
return imageClicked; return imageClicked;
} }
@UiThread
void onOnImageClickSeen() {
imageClicked.setValue(false);
}
void setToolbarPosition(int top, int bottom) { void setToolbarPosition(int top, int bottom) {
toolbarTop = top; toolbarTop = top;
toolbarBottom = bottom; toolbarBottom = bottom;
@@ -105,18 +111,26 @@ public class ImageViewModel extends AndroidViewModel {
/** /**
* A LiveData that is true if there was an error * A LiveData that is true if there was an error
* and false if the image was saved. * and false if the image was saved.
* It can be null otherwise, if no image was saved recently.
*
* Call {@link #onSaveStateSeen()} after consuming an update.
*/ */
LiveEvent<Boolean> getSaveState() { LiveData<Boolean> getSaveState() {
return saveState; return saveState;
} }
@UiThread
void onSaveStateSeen() {
saveState.setValue(null);
}
/** /**
* Saves the attachment to a writeable {@link Uri}. * Saves the attachment to a writeable {@link Uri}.
*/ */
@UiThread @UiThread
void saveImage(AttachmentItem attachment, @Nullable Uri uri) { void saveImage(AttachmentItem attachment, @Nullable Uri uri) {
if (uri == null) { if (uri == null) {
saveState.setEvent(true); saveState.setValue(true);
} else { } else {
saveImage(attachment, () -> getOutputStream(uri), null); saveImage(attachment, () -> getOutputStream(uri), null);
} }
@@ -141,7 +155,7 @@ public class ImageViewModel extends AndroidViewModel {
copyImageFromDb(a, osp, afterCopy); copyImageFromDb(a, osp, afterCopy);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
saveState.postEvent(true); saveState.postValue(true);
} }
}); });
} }
@@ -154,10 +168,10 @@ public class ImageViewModel extends AndroidViewModel {
OutputStream os = osp.getOutputStream(); OutputStream os = osp.getOutputStream();
copyAndClose(is, os); copyAndClose(is, os);
if (afterCopy != null) afterCopy.run(); if (afterCopy != null) afterCopy.run();
saveState.postEvent(false); saveState.postValue(false);
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
saveState.postEvent(true); saveState.postValue(true);
} }
}); });
} }

View File

@@ -4,6 +4,7 @@ import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
@@ -26,7 +27,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseEventFragment; import org.briarproject.briar.android.fragment.BaseEventFragment;
import org.briarproject.briar.android.sharing.ForumInvitationActivity; import org.briarproject.briar.android.sharing.ForumInvitationActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
@@ -105,9 +105,11 @@ public class ForumListFragment extends BaseEventFragment implements
list.setLayoutManager(new LinearLayoutManager(getActivity())); list.setLayoutManager(new LinearLayoutManager(getActivity()));
list.setAdapter(adapter); list.setAdapter(adapter);
snackbar = new BriarSnackbarBuilder() snackbar = Snackbar.make(list, "", LENGTH_INDEFINITE);
.setAction(R.string.show, this) snackbar.getView().setBackgroundResource(R.color.briar_primary);
.make(list, "", LENGTH_INDEFINITE); snackbar.setAction(R.string.show, this);
snackbar.setActionTextColor(ContextCompat
.getColor(getActivity(), R.color.briar_button_text_positive));
return contentView; return contentView;
} }

View File

@@ -15,7 +15,6 @@ import android.widget.Toast;
import com.google.zxing.Result; import com.google.zxing.Result;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult; import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
@@ -23,6 +22,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;

View File

@@ -4,6 +4,7 @@ import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnClickListener;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.StringRes;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
@@ -197,7 +198,7 @@ public class GroupActivity extends
private void setGroupEnabled(boolean enabled) { private void setGroupEnabled(boolean enabled) {
isDissolved = !enabled; isDissolved = !enabled;
sendController.setReady(enabled); textInput.setEnabled(enabled);
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f); list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
if (!enabled) { if (!enabled) {

Some files were not shown because too many files have changed in this diff Show More