Compare commits

..

168 Commits

Author SHA1 Message Date
akwizgran
5cd5fc7e43 Bump version numbers for 1.0.6 release. 2018-06-01 10:20:32 +01:00
akwizgran
abd9db70b9 Update translations, add Polish translation. 2018-06-01 10:18:42 +01:00
akwizgran
5025cf1e40 Merge branch 'remove-removable-drive-plugin' into 'master'
Remove RemovableDrivePlugin, refactor plugin interface

Closes #25

See merge request akwizgran/briar!817
2018-05-31 08:49:32 +00:00
akwizgran
834342fd3a Merge branch 'remove-reblog-scene-transition' into 'master'
Disable reblog scene transition as it even crashes my Android 7.1 device

Closes #785

See merge request akwizgran/briar!821
2018-05-29 15:56:07 +00:00
akwizgran
3028b236e1 Merge branch 'disable-prefetching' into 'master'
Disable pre-fetching in Threaded RecyclerView as a workaround for #1289

See merge request akwizgran/briar!820
2018-05-29 15:47:15 +00:00
Torsten Grote
254422bc02 Disable reblog scene transition as it even crashes my Android 7.1 device
Closes #785
2018-05-29 12:44:41 -03:00
Torsten Grote
c7949d6e00 Disable pre-fetching in Threaded RecyclerView as a workaround for #1289 2018-05-29 12:29:40 -03:00
Torsten Grote
0187264da7 Merge branch '1219-remove-debug-logging' into 'master'
Remove debug logging from setup process

See merge request akwizgran/briar!819
2018-05-28 13:59:22 +00:00
akwizgran
85a18cf53f Remove debug logging from setup process. 2018-05-28 14:34:20 +01:00
akwizgran
3181b695df Remove RemovableDrivePlugin, refactor plugin interface. 2018-05-25 13:57:38 +01:00
akwizgran
b2ac210586 Merge branch 'factor_out_plugin_conf' into 'master'
Make plugins and polling configurable

See merge request akwizgran/briar!814
2018-05-24 16:34:05 +00:00
Torsten Grote
d20340416d Merge branch 'jcenter-tor-binaries' into 'master'
Download Tor binaries from JCenter

See merge request akwizgran/briar!816
2018-05-24 12:21:14 +00:00
akwizgran
9da871718c Download Tor binaries from JCenter. 2018-05-24 10:54:34 +01:00
goapunk
3793cb841b Fix test and poller instantiation 2018-05-23 14:39:01 +02:00
goapunk
c6b88b51f0 Make plugins and polling configurable
* Move PluginConfig out of bramble-android. Projects using bramble now need to provide it.
* Add a PluginConfig#shouldPoll() method which can be used to disable polling altogether.
* Move Poller instantiation to the PluginManager.
2018-05-23 14:39:00 +02:00
Torsten Grote
2f00215a44 Merge branch 'remove-jtorctl-jar' into 'master'
Replace jtorctl jar with JCenter dependency

See merge request akwizgran/briar!815
2018-05-23 11:22:15 +00:00
akwizgran
183f0c5f31 Bump version numbers for 1.0.5 release. 2018-05-22 15:30:33 +01:00
Torsten Grote
34c5aaae0a Update translations (Farsi and Chinese complete now) 2018-05-22 11:17:51 -03:00
Torsten Grote
5531355ebd Merge branch '1219-store-db-key-in-file' into 'master'
Store database key in a file

Closes #1219

See merge request akwizgran/briar!810
2018-05-22 12:24:13 +00:00
akwizgran
b9e607744a Store second copy of DB key in backup file. 2018-05-22 12:07:07 +01:00
akwizgran
def62bce5a Replace jtorctl jar with JCenter dependency. 2018-05-22 11:32:19 +01:00
akwizgran
9dae3d191a Merge branch '1281-introduction-bug' into 'master'
Introduction: Reset session information for removed introducees

Closes #1281

See merge request akwizgran/briar!813
2018-05-22 09:09:13 +00:00
Torsten Grote
20422edf78 Introduction: Reset session information for removed introducees 2018-05-21 16:26:11 -03:00
Torsten Grote
f8bc5f08bf Merge branch 'unicode-escapes-for-test-data' into 'master'
Escape Unicode characters in test data

See merge request akwizgran/briar!812
2018-05-21 16:21:29 +00:00
akwizgran
9434495d70 Escape Unicode characters in test data.
This enables reproducible builds with non-Unicode locales.
2018-05-21 17:02:09 +01:00
Torsten Grote
bf9e91fcf5 Merge branch 'fix-build-timestamp' into 'master'
Make build timestamp command compatible with old versions of Git

See merge request akwizgran/briar!811
2018-05-21 12:47:47 +00:00
akwizgran
d9d86206a6 Make build timestamp command compatible with old versions of Git. 2018-05-21 13:40:14 +01:00
akwizgran
b410b8efcc Don't overwrite the backup if it's our only copy. 2018-05-18 15:17:43 +01:00
akwizgran
39aa2d96b3 Unit tests for DB key storage and retrieval. 2018-05-18 15:11:28 +01:00
akwizgran
21dae824a6 Store database key in a file rather than shared prefs. 2018-05-18 14:47:53 +01:00
akwizgran
cfdbd29cb4 Remove unused logging methods. 2018-05-18 14:47:20 +01:00
akwizgran
4df335ebd3 Merge branch 'own-ci' into 'master'
Switch to our own CI image

See merge request akwizgran/briar!809
2018-05-18 10:01:04 +00:00
Torsten Grote
682bee1486 Switch to our own CI image 2018-05-17 18:52:49 -03:00
akwizgran
f31219d54b Bump version numbers for 1.0.4 release. 2018-05-17 16:22:39 +01:00
Torsten Grote
b0ea32c85f Merge branch 'hide-testing-preferences' into 'master'
Hide testing preferences in release builds

See merge request akwizgran/briar!808
2018-05-17 14:33:06 +00:00
akwizgran
651e0b9859 Hide testing preferences in release builds. 2018-05-17 15:14:59 +01:00
akwizgran
f66244b578 Merge branch 'ci-test' into 'master'
Add CI stage for testing reproducibility

See merge request akwizgran/briar!807
2018-05-17 14:13:40 +00:00
Torsten Grote
3a35effae9 Add CI stage for testing reproducibility 2018-05-17 10:55:56 -03:00
Torsten Grote
97f4cd039a Update translations. New: Farsi and Asturian 2018-05-17 10:55:08 -03:00
akwizgran
20a1474457 Merge branch '1277-bdf-reader' into 'master'
Use lower default limit for BDF strings and raws

Closes #1277

See merge request akwizgran/briar!805
2018-05-17 13:35:57 +00:00
Torsten Grote
f214208b0a Merge branch 'test-data-lan-addresses' into 'master'
Use more realistic LAN addresses for test contacts

See merge request akwizgran/briar!804
2018-05-17 11:53:48 +00:00
akwizgran
d95a5fd58c Use lower default limit for BDF strings and raws. 2018-05-17 12:33:56 +01:00
akwizgran
7d4de21be0 Merge branch 'decouple-report-sender' into 'master'
Decouple DevReportSender from TorPlugin

See merge request akwizgran/briar!803
2018-05-17 09:52:47 +00:00
akwizgran
a65bda04bf Use more realistic LAN addresses for test contacts. 2018-05-17 10:22:29 +01:00
akwizgran
41ae7b0522 Merge branch 'internal-logger' into 'master'
Use internal log handler for crash reports and feedback

Closes #986

See merge request akwizgran/briar!802
2018-05-17 08:22:48 +00:00
akwizgran
c2214f5e61 Merge branch 'message-encrypter' into 'master'
Refactor MessageEncrypter main method

See merge request akwizgran/briar!800
2018-05-17 08:22:22 +00:00
akwizgran
75bd7927ac Decouple DevReportSender from TorPlugin. 2018-05-17 09:20:12 +01:00
akwizgran
ed2c0336ed Merge branch 'crash-button' into 'master'
Add crash button to UI in debug mode

See merge request akwizgran/briar!801
2018-05-16 11:19:15 +00:00
akwizgran
b23baf70b4 Don't send system logs in ACRA reports. 2018-05-16 11:53:00 +01:00
akwizgran
17a7144194 Add internal logger. 2018-05-16 11:52:55 +01:00
akwizgran
0f7d27cd95 Refactor MessageEncrypter main method. 2018-05-16 11:42:00 +01:00
akwizgran
6735e5075b Add crash button to UI in debug mode. 2018-05-16 11:40:50 +01:00
akwizgran
135cf086f6 Merge branch 'silence_strict_mode' into 'master'
Silence known strict mode violations

See merge request akwizgran/briar!795
2018-05-16 09:48:54 +00:00
goapunk
72bac59989 silence known strict mode violations 2018-05-15 17:29:53 +02:00
Torsten Grote
2c99a75b4e Merge branch 'settings-screen' into 'master'
Use lighter text colour for preference categories and fix layout

See merge request akwizgran/briar!799
2018-05-15 14:20:01 +00:00
Torsten Grote
5c068ed07b Merge branch 'remove-unbound-transport-keys' into 'master'
Remove support for unbound transport keys

See merge request akwizgran/briar!791
2018-05-15 14:18:58 +00:00
akwizgran
fec384c200 Add migration for NOT NULL constraint. 2018-05-15 14:37:36 +01:00
akwizgran
89a4d1922b Remove support for unbound transport keys. 2018-05-15 14:37:36 +01:00
akwizgran
6ed16802ce Merge branch 'deterministic-build-timestamp' into 'master'
Use git commit timestamp as build timestamp

See merge request akwizgran/briar!798
2018-05-15 13:24:00 +00:00
akwizgran
7f11d7280f Merge branch 'animal-sniffer' into 'master'
Use Animal Sniffer to check Java 6 API compatibility

See merge request akwizgran/briar!787
2018-05-15 13:23:29 +00:00
akwizgran
597e2a233f Use git commit timestamp as build timestamp. 2018-05-15 12:47:14 +01:00
akwizgran
e83d8bb700 Bump version numbers for 1.0.3 release. 2018-05-14 21:52:16 +01:00
akwizgran
d1ce0d0628 Merge branch '1215-low-memory-hide-ui' into 'master'
Clear the UI when memory is critically low

See merge request akwizgran/briar!786
2018-05-14 14:53:04 +00:00
akwizgran
d73ec3cd88 Merge branch 'disable-expiry' into 'master'
Disable expiry for release builds

See merge request akwizgran/briar!797
2018-05-14 14:51:40 +00:00
akwizgran
71c66c843b Merge branch '1219-commit-shared-prefs' into 'master'
Commit shared preferences, clear instead of deleting

See merge request akwizgran/briar!794
2018-05-14 14:18:49 +00:00
akwizgran
bd19272099 Throw exception if account exists when beginning setup. 2018-05-14 14:20:13 +01:00
akwizgran
b77b885a94 Commit shared preferences, clear instead of deleting. 2018-05-14 14:20:12 +01:00
akwizgran
1fc4f657c7 Merge branch '1219-account-exists' into 'master'
Add logging to debug account creation and deletion

See merge request akwizgran/briar!793
2018-05-14 13:19:20 +00:00
akwizgran
df7d48d54d Fix test expectations. 2018-05-14 12:35:03 +01:00
akwizgran
1987dcb936 Make field that's used on background thread volatile. 2018-05-14 12:34:32 +01:00
akwizgran
f3b69a26f8 Remove unused exception declarations. 2018-05-14 12:31:48 +01:00
akwizgran
5e0ca10dae Add logging to debug account setup. 2018-05-14 12:31:46 +01:00
akwizgran
685496fb15 Extract DatabaseConfig implementation. 2018-05-14 12:30:57 +01:00
akwizgran
1521cdd258 Move expiry date to TestingConstants. 2018-05-14 12:24:37 +01:00
akwizgran
80561910b1 Disable expiry for release builds. 2018-05-14 12:03:30 +01:00
akwizgran
bffb5c94ed Merge branch '1229-setup-crash' into 'master'
Store nickname and password across screen rotations

Closes #1229

See merge request akwizgran/briar!796
2018-05-14 09:57:12 +00:00
akwizgran
dcbb3e76d4 Use lighter text colour for preference categories. 2018-05-11 12:48:43 +01:00
akwizgran
c19f7c27b1 Merge branch 'stream-writer-interface' into 'master'
Send end of stream marker when sync session finishes

See merge request akwizgran/briar!790
2018-05-11 10:55:32 +00:00
akwizgran
9a5a1489ef Remove a redundant method. 2018-05-11 11:41:49 +01:00
akwizgran
648793e092 Add javadoc. 2018-05-11 11:36:49 +01:00
akwizgran
e10742a23d Store nickname and password across screen rotations. 2018-05-11 11:36:04 +01:00
akwizgran
32ada51831 Log transport ID with number of connected contacts. 2018-05-10 12:31:54 +01:00
akwizgran
7734a62c3e Interrupt outgoing session when incoming session ends. 2018-05-10 12:29:45 +01:00
akwizgran
e516c329a1 Bump version numbers for 1.0.2 release. 2018-05-09 16:59:09 +01:00
Torsten Grote
b839041d5a Update translations 2018-05-09 09:56:15 -03:00
Torsten Grote
65de8707b7 Merge branch '1225-improve-setup-ux' into 'master'
Remove circle, make button flat to improve setup UX

Closes #1225

See merge request akwizgran/briar!792
2018-05-09 10:41:17 +00:00
akwizgran
dc5bd39ce4 Remove circle, make button flat to improve setup UX. 2018-05-09 10:50:23 +01:00
akwizgran
3c4513b9c7 Convert test to BrambleMockTestCase. 2018-05-08 15:02:07 +01:00
akwizgran
5320737d49 Send end of stream marker when sync session finishes. 2018-05-08 14:41:53 +01:00
akwizgran
0ad9415850 Merge branch 'fix-javadoc' into 'master'
Fix random javadoc errors

See merge request akwizgran/briar!789
2018-05-08 12:56:25 +00:00
goapunk
6f1fba44b6 Fix random javadoc errors 2018-05-08 14:42:14 +02:00
akwizgran
46cd7e3719 Merge branch 'update_external_deps' into 'master'
Update external dependencies

See merge request akwizgran/briar!788
2018-05-08 12:34:44 +00:00
goapunk
496ffc22ba Update external dependencies
* Update CircleImageView
* Update Tap target prompt
2018-05-08 13:22:25 +02:00
akwizgran
286937e472 Resolve merge conflicts. 2018-05-07 14:37:32 +00:00
akwizgran
2908145a06 Merge branch 'android-studio-3-1-debugging' into 'master'
Upgrade to Android Studio 3.1

See merge request akwizgran/briar!784
2018-05-07 14:16:24 +00:00
akwizgran
ed53544226 Clear the UI in onLowMemory() if SDK_INT < 16. 2018-05-04 12:18:52 +01:00
akwizgran
6da45a4585 Clear the UI when memory is critically low. 2018-05-04 12:04:13 +01:00
akwizgran
0d85a56a29 Merge branch '1216-fix-testdata' into 'master'
Resolve "IllegalArgumentException when creating test data with zero contacts"

Closes #1216

See merge request akwizgran/briar!785
2018-05-03 08:39:23 +00:00
goapunk
3e5d1a0d20 Fix testdata creation:
* Create at least one contact
2018-05-02 20:49:33 +02:00
akwizgran
0e9af1d993 Add new code style settings. 2018-05-02 17:13:27 +01:00
akwizgran
a3b5ff0bc0 Add dependency hashes for Animal Sniffer. 2018-05-02 16:24:59 +01:00
akwizgran
9b583ab7ae Build tools 26.0.2 no longer needed. 2018-05-02 16:14:40 +01:00
akwizgran
1192f66487 Use Animal Sniffer to check Java 6 API compatibility. 2018-05-02 16:10:40 +01:00
akwizgran
a10dc45a6a Remove old attempt to work around build issues. 2018-05-02 15:11:21 +01:00
akwizgran
3c6e2e8875 Upgrade support library and constraint layout. 2018-05-02 15:06:34 +01:00
akwizgran
cccdacc3e4 Upgrade to Android Studio 3.1. 2018-05-02 14:34:34 +01:00
akwizgran
1833efa51b Bump version numbers for 1.0.1 release. 2018-04-30 18:05:57 +01:00
akwizgran
77461619f0 Merge branch '1217-get-message-status' into 'master'
Return default message status if group is invisible

Closes #1217

See merge request akwizgran/briar!783
2018-04-30 16:23:19 +00:00
Torsten Grote
3bed6de93b Update translations 2018-04-30 12:52:46 -03:00
Torsten Grote
8e814e1bbf Merge branch '1215-disable-low-memory-shutdowns' into 'master'
Disable low memory shutdowns

See merge request akwizgran/briar!782
2018-04-30 13:43:17 +00:00
akwizgran
1b9f975199 Return default message status if group is invisible. 2018-04-30 13:55:40 +01:00
akwizgran
c1b8552c2b Merge branch 'introduction-test-fix' into 'master'
Fix introduction test non-determinism

See merge request akwizgran/briar!781
2018-04-30 08:05:00 +00:00
akwizgran
0091fcef5d Updated translations. 2018-04-30 07:46:00 +01:00
akwizgran
5d1a4acd15 Disable low memory shutdowns. 2018-04-30 07:43:16 +01:00
akwizgran
9be6d6c00f Bump version numbers for 1.0.0 release. 2018-04-29 20:28:53 +01:00
akwizgran
d5643d8e5d Merge branch '617-protocol-versioning-for-contact-exchange' into 'master'
Protocol versioning for the contact exchange protocol

Closes #617

See merge request akwizgran/briar!765
2018-04-29 16:40:05 +00:00
Torsten Grote
d9ed4a3ac9 Fix introduction test non-determinism 2018-04-29 13:33:59 -03:00
akwizgran
30bfa91fc4 Use new client helper methods for transport properties. 2018-04-29 17:27:46 +01:00
akwizgran
c3e4742bfe Use buffers for record headers. No need to buffer payloads. 2018-04-29 17:27:45 +01:00
akwizgran
d4b87983e8 Avoid an unnecessary copy when parsing messages. 2018-04-29 17:27:42 +01:00
akwizgran
eed1439745 Use generic record reader/writer for contact exchange. 2018-04-29 17:26:45 +01:00
akwizgran
4ba3708931 Use wildcards to allow easier construction. 2018-04-29 17:26:45 +01:00
akwizgran
215c62ed23 Use generic record reader/writer for sync. 2018-04-29 17:26:42 +01:00
akwizgran
4100daaa47 Rename sync record reader/writer. 2018-04-29 17:25:35 +01:00
akwizgran
6fa6ceb5ee Use generic record reader/writer for key agreement. 2018-04-29 17:25:34 +01:00
akwizgran
cc2791c37f Unit tests for generic record reader/writer. 2018-04-29 17:25:34 +01:00
akwizgran
20a131bec5 Add generic record reader and writer. 2018-04-29 17:25:34 +01:00
Torsten Grote
edee90dbe2 Merge branch '237-versioning-client' into 'master'
Client for negotiating supported clients

Closes #237

See merge request akwizgran/briar!759
2018-04-29 16:08:08 +00:00
akwizgran
fd78139a5a Remove stale comments. 2018-04-29 16:40:29 +01:00
akwizgran
41242ef369 Check that there's only one local client versions message. 2018-04-29 16:40:29 +01:00
akwizgran
c55bef95ce Fix rebasing mistakes. 2018-04-29 16:40:29 +01:00
akwizgran
fb6b487212 Unit tests for client versioning manager. 2018-04-29 16:40:28 +01:00
akwizgran
97f40bd20b Check whether hooks need to be called before loading contact. 2018-04-29 16:40:28 +01:00
akwizgran
36b191e9d4 Use remote states to update local states at startup. 2018-04-29 16:40:28 +01:00
akwizgran
ebaa50b101 Don't send redundant updates to new contacts. 2018-04-29 16:40:28 +01:00
akwizgran
4c5331888a Unit tests for client versioning validator. 2018-04-29 16:40:27 +01:00
akwizgran
c5efb6e16d Move versioning client to its own package. 2018-04-29 16:40:27 +01:00
akwizgran
522cba6ac3 Rename utility classes, add comment. 2018-04-29 16:40:27 +01:00
akwizgran
f25fbc5b94 Merge registration methods for clients and hooks. 2018-04-29 16:40:27 +01:00
akwizgran
57a6c8cb3a Separate the crypto executor into its own module.
This allows it to be replaced for testing.
2018-04-29 16:40:26 +01:00
akwizgran
6942a368d4 Don't share groups unless the contact supports the client. 2018-04-29 16:40:26 +01:00
akwizgran
c4e9b6f2ab Remove debug logging. 2018-04-29 16:40:26 +01:00
akwizgran
05deaf42e3 Store and exchange client minor versions.
These don't affect client visibility.
2018-04-29 16:40:26 +01:00
akwizgran
2e570ba50d Rename client version to major version. 2018-04-29 16:40:23 +01:00
akwizgran
cadb17987c Use client versioning for messaging. 2018-04-29 16:39:54 +01:00
akwizgran
e76f114a72 Use client versioning for introductions. 2018-04-29 16:39:54 +01:00
akwizgran
cb11b55a9a Use client versioning for transport properties. 2018-04-29 16:39:54 +01:00
akwizgran
f4c5855dd8 Use client versioning for private groups. 2018-04-29 16:39:53 +01:00
akwizgran
be309057cd Use client versioning for blogs and forums. 2018-04-29 16:39:53 +01:00
akwizgran
cf396c2ce2 Check whether contact group exists before using it. 2018-04-29 16:39:53 +01:00
akwizgran
a9f77f0f90 Add a method for getting a client's visibility. 2018-04-29 16:39:53 +01:00
akwizgran
cc6fed0298 Add javadocs. 2018-04-29 16:39:53 +01:00
akwizgran
66137d4cfa Add method for comparing visibilities. 2018-04-29 16:39:52 +01:00
akwizgran
114044ee5f Use client version to register validators, delivery hooks. 2018-04-29 16:39:52 +01:00
akwizgran
1197d65d8d Extract ClientVersion inner class. 2018-04-29 16:39:52 +01:00
akwizgran
85c11f8e1f Remove redundant checks when adding contacts.
Hooks are now called exactly once per contact.
2018-04-29 16:39:52 +01:00
akwizgran
8c00f2417b Add client version to groups table. 2018-04-29 16:39:51 +01:00
akwizgran
a38f39207f Initial implementation of client versioning client. 2018-04-29 16:39:51 +01:00
akwizgran
b7874365a3 Expose getMessageIds() through DatabaseComponent interface. 2018-04-29 16:39:51 +01:00
akwizgran
196caa7b45 Update ID of transport properties client. 2018-04-29 16:39:51 +01:00
akwizgran
3fd6ce2313 Fix javadoc. 2018-04-29 16:39:50 +01:00
akwizgran
c42852cde2 Merge branch '1213-update-transport-keys' into 'master'
Update transport keys in-place to retain key set IDs

Closes #1213

See merge request akwizgran/briar!779
2018-04-29 15:00:02 +00:00
Torsten Grote
a38b0a8527 Merge branch 'bluetooth-connection-limiter' into 'master'
Don't make or accept Bluetooth contact connections during key agreement

See merge request akwizgran/briar!770
2018-04-29 14:42:00 +00:00
Torsten Grote
79d6fd28de Merge branch '474-alice-flag' into 'master'
IntroduceeProtocolEngine uses wrong role when adding keys

See merge request akwizgran/briar!780
2018-04-29 02:27:18 +00:00
akwizgran
68132d893b IntroduceeProtocolEngine uses wrong role when adding keys. 2018-04-28 23:04:08 +01:00
akwizgran
6b011d2a7d Update transport keys in-place to retain key set IDs. 2018-04-28 22:15:59 +01:00
akwizgran
d7492df81c Skip UTestTest, which literally fails at random. 2018-04-28 14:52:56 +01:00
akwizgran
9efb6ab38f Don't allow BT contact connections during key agreement. 2018-04-24 10:45:23 +01:00
353 changed files with 9321 additions and 4755 deletions

1
.gitignore vendored
View File

@@ -20,6 +20,7 @@ local.properties
.idea/*
!.idea/runConfigurations/
!.idea/codeStyleSettings.xml
!.idea/codeStyles
.gradle
build/
*.iml

View File

@@ -1,29 +1,31 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
# Accept the license for the Android build tools
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;26.0.2"
# Download OpenJDK 6 so we can compile against its standard library
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
- if [ ! -d openjdk ]
- then
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
- dpkg-deb -x $JDK_FILE openjdk
- fi
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
image: briar/ci-image-android:latest
test:
script:
- ./gradlew test
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
after_script:
# this file changes every time but should not be cached
cache:
paths:
- .gradle/wrapper
- .gradle/caches
script:
- ./gradlew --no-daemon animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon test
after_script:
# these file change every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
test_reproducible:
image: briar/reproducer:latest
script:
- cd /opt/briar-reproducer
- ./reproduce.py ${CI_COMMIT_REF_NAME}
only:
- tags

261
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,261 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="RIGHT_MARGIN" value="100" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
</value>
</option>
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
</JavaCodeStyleSettings>
<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>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="RESOURCE_LIST_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_LIST_WRAP" value="1" />
<option name="EXTENDS_KEYWORD_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<option name="ASSERT_STATEMENT_WRAP" value="1" />
<option name="PARAMETER_ANNOTATION_WRAP" value="1" />
<option name="VARIABLE_ANNOTATION_WRAP" value="1" />
<option name="ENUM_CONSTANTS_WRAP" value="1" />
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<option name="FORCE_REARRANGE_MODE" value="1" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_NAMESPACE>Namespace:</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:width</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:height</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View File

@@ -1,21 +1,15 @@
import de.undercouch.gradle.tasks.download.Download
import de.undercouch.gradle.tasks.download.Verify
import java.security.NoSuchAlgorithmException
apply plugin: 'com.android.library'
apply plugin: 'witness'
apply plugin: 'de.undercouch.download'
android {
compileSdkVersion 27
buildToolsVersion '26.0.2'
buildToolsVersion '27.0.3'
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 1700
versionName "0.17.0"
versionCode 10006
versionName "1.0.6"
consumerProguardFiles 'proguard-rules.txt'
}
@@ -25,9 +19,14 @@ android {
}
}
configurations {
tor
}
dependencies {
implementation project(path: ':bramble-core', configuration: 'default')
implementation fileTree(dir: 'libs', include: '*.jar')
implementation 'org.briarproject:jtorctl:0.3'
tor 'org.briarproject:tor-android:0.2.9.15@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
@@ -36,101 +35,87 @@ dependencies {
dependencyVerification {
verify = [
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.android.tools.analytics-library:protos:26.1.2:protos-26.1.2.jar:52672a0b42b572a06aecc3535d5068eb46c0e15d129b9f1085d3c16a1da5cdbb',
'com.android.tools.analytics-library:shared:26.1.2:shared-26.1.2.jar:5c7e0eda18c6f87feeb83628c707e8aaa3298b41fb72e38efe31ad1675f9e8e9',
'com.android.tools.analytics-library:tracker:26.1.2:tracker-26.1.2.jar:06f97aa0adf44ffb06f8681c6a79d9be153a08f61d21eddc42b8d3db96df4282',
'com.android.tools.build:apksig:3.1.2:apksig-3.1.2.jar:40696a4559124d1d57873d208857eee059d48859239d569c7d18374ac644a8be',
'com.android.tools.build:builder-model:3.1.2:builder-model-3.1.2.jar:d49bfa2a135c9562b6ca7aa4342036cfa1582c7074c2d1d93d1dae8b3a134e17',
'com.android.tools.build:builder-test-api:3.1.2:builder-test-api-3.1.2.jar:dfe2a50b740d41b11189101062434d4283d18647e89a492ad51710c719363e9f',
'com.android.tools.build:builder:3.1.2:builder-3.1.2.jar:b60f825a42e2efe8433619fbc759f3d9effecab718279048d36881188ceb1d14',
'com.android.tools.build:gradle-api:3.1.2:gradle-api-3.1.2.jar:e58bcc5b893e4583ab0f5c8ef89c4dbcce202b405a9d7fcc116d21e5357d4893',
'com.android.tools.build:manifest-merger:26.1.2:manifest-merger-26.1.2.jar:9c61c27ea5266573107b954acf1216d398f4d7e7ae6fad6409d6b2b767eb091c',
'com.android.tools.ddms:ddmlib:26.1.2:ddmlib-26.1.2.jar:18a2a5fbef36882f07d03c2b9e59eba05cf8248177bf5cbff736e4b582804c44',
'com.android.tools.external.com-intellij:intellij-core:26.1.2:intellij-core-26.1.2.jar:37c5acf279f1ae3e85b1a5be3c9f15f43bde7b08f978eefefffb9c4035760c52',
'com.android.tools.external.com-intellij:kotlin-compiler:26.1.2:kotlin-compiler-26.1.2.jar:152df0bee7580326c77316b669a9d96e3b09efb1d45f545dce4147271b0b8944',
'com.android.tools.external.org-jetbrains:uast:26.1.2:uast-26.1.2.jar:02d39582206d3f5fc0a6cb18bfd9e8b9f9c1acb805ec6dac08b4e3a56849d279',
'com.android.tools.layoutlib:layoutlib-api:26.1.2:layoutlib-api-26.1.2.jar:20220039fcc7d799f928153beff862e704457c0f55ab44258f3745ebeb662b4f',
'com.android.tools.lint:lint-api:26.1.2:lint-api-26.1.2.jar:e1d5b62b870a7c566e9877a6b96b27784a4d713f8caa07fdcb4705d47a40a1d9',
'com.android.tools.lint:lint-checks:26.1.2:lint-checks-26.1.2.jar:211e2afd58504372385d71b1e5be982c2b5121ab6fee1c04ddabeb75a8729e07',
'com.android.tools.lint:lint-gradle-api:26.1.2:lint-gradle-api-26.1.2.jar:71284f2a8b03c3e55c94511c9eb36f8184fbb85324325fc6b78abf5183f03d90',
'com.android.tools.lint:lint-gradle:26.1.2:lint-gradle-26.1.2.jar:855f0c82b7fc690df1b7319c0774f7517f7f8f5dd4eee1f6077dcf50e07c6240',
'com.android.tools.lint:lint-kotlin:26.1.2:lint-kotlin-26.1.2.jar:1e591f70bcbbc11569720a9bbcca2bc1f3d4f789f01f40f642848d920643d484',
'com.android.tools.lint:lint:26.1.2:lint-26.1.2.jar:93736c62e9f1976998c2b4aa716aea0734cdb162d05502f4af7292654aedb182',
'com.android.tools:annotations:26.1.2:annotations-26.1.2.jar:72773dcaf5c4ccca828e3c8467f1b78a8a00b3cc5f8ad1aab88fcf9379928018',
'com.android.tools:common:26.1.2:common-26.1.2.jar:ea4320f0c17dcbc4491896bb705c4d25ec08bd62ef02ab0579fe154e75e788e6',
'com.android.tools:dvlib:26.1.2:dvlib-26.1.2.jar:1187aa4fb666595c96c4deb6bc0e0f4b7e396bde9f6243330b49a232946130ea',
'com.android.tools:repository:26.1.2:repository-26.1.2.jar:8b86e512ad6d32bd76989451eefe2b271f5efce6d4d65ecb173afaf14606e01a',
'com.android.tools:sdk-common:26.1.2:sdk-common-26.1.2.jar:23584720a60a21cdcb5b1ec10269e3013789d6805d153cc696c39ec7ce251896',
'com.android.tools:sdklib:26.1.2:sdklib-26.1.2.jar:d3870fafc59ab8efa70d3f9649f40ee299c8ec5b58377b06e8853d7272a5bf4e',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.gson:gson:2.7:gson-2.7.jar:2d43eb5ea9e133d2ee2405cc14f5ee08951b8361302fdd93494a3a997b508d32',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'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.google.guava:guava:22.0:guava-22.0.jar:1158e94c7de4da480873f0b4ab4a1da14c0d23d4b1902cc94a58a6f0f9ab579e',
'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.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.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0',
'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.xml.fastinfoset:FastInfoset:1.2.13:FastInfoset-1.2.13.jar:27a77db909f3c2833c0b1a37c55af1db06045118ad2eed96ce567b6632bce038',
'commons-codec:commons-codec:1.6:commons-codec-1.6.jar:54b34e941b8e1414bd3e40d736efd3481772dc26db3296f6aa45cec9f6203d86',
'commons-logging:commons-logging:1.1.1:commons-logging-1.1.1.jar:ce6f913cad1f0db3aad70186d65c5bc7ffcc9a99e3fe8e0b137312819f7c362f',
'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.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
'org.briarproject:tor-android:0.2.9.15:tor-android-0.2.9.15.zip:34a6474ee219ffa52e0f3393e917dda6ed03d320b02247d4fa5075aa4094ee6d',
'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
]
}
ext.torBinaryDir = 'src/main/res/raw'
ext.torVersion = '0.2.9.14'
ext.geoipVersion = '2017-11-06'
ext.torDownloadUrl = 'https://briarproject.org/build/'
def torBinaries = [
"tor_arm" : '1710ea6c47b7f4c1a88bdf4858c7893837635db10e8866854eed8d61629f50e8',
"tor_arm_pie": '974e6949507db8fa2ea45231817c2c3677ed4ccf5488a2252317d744b0be1917',
"tor_x86" : '3a5e45b3f051fcda9353b098b7086e762ffe7ba9242f7d7c8bf6523faaa8b1e9',
"tor_x86_pie": 'd1d96d8ce1a4b68accf04850185780d10cd5563d3552f7e1f040f8ca32cb4e51',
"geoip" : '8239b98374493529a29096e45fc5877d4d6fdad0146ad8380b291f90d61484ea'
]
def verifyOrDeleteBinary(name, chksum, alreadyVerified) {
return tasks.create("verifyOrDeleteBinary${name}", VerifyOrDelete) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
result alreadyVerified
onlyIf {
src.exists()
}
}
}
def downloadBinary(name, chksum, alreadyVerified) {
return tasks.create([
name: "downloadBinary${name}",
type: Download,
dependsOn: verifyOrDeleteBinary(name, chksum, alreadyVerified)]) {
src "${torDownloadUrl}${name}.zip"
.replace('tor_', "tor-${torVersion}-")
.replace('geoip', "geoip-${geoipVersion}")
.replaceAll('_', '-')
dest "${torBinaryDir}/${name}.zip"
onlyIf {
!dest.exists()
}
}
}
def verifyBinary(name, chksum) {
boolean[] alreadyVerified = [false]
return tasks.create([
name : "verifyBinary${name}",
type : Verify,
dependsOn: downloadBinary(name, chksum, alreadyVerified)]) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
onlyIf {
!alreadyVerified[0]
}
}
}
project.afterEvaluate {
torBinaries.every { name, checksum ->
preBuild.dependsOn.add(verifyBinary(name, checksum))
}
}
class VerifyOrDelete extends Verify {
boolean[] result
@TaskAction
@Override
void verify() throws IOException, NoSuchAlgorithmException {
try {
super.verify()
result[0] = true
} catch (Exception e) {
println "${src} failed verification - deleting"
src.delete()
}
copy {
from configurations.tor.collect { zipTree(it) }
into 'src/main/res/raw'
}
}

View File

@@ -1,12 +1,10 @@
package org.briarproject.bramble;
import org.briarproject.bramble.plugin.AndroidPluginModule;
import org.briarproject.bramble.system.AndroidSystemModule;
import dagger.Module;
@Module(includes = {
AndroidPluginModule.class,
AndroidSystemModule.class
})
public class BrambleAndroidModule {

View File

@@ -1,69 +0,0 @@
package org.briarproject.bramble.plugin;
import android.app.Application;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.TorPluginFactory;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class AndroidPluginModule {
@Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new TorPluginFactory(ioExecutor, scheduler,
appContext, locationUtils, reporter, eventBus,
torSocketFactory, backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
scheduler, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return Collections.emptyList();
}
};
return pluginConfig;
}
}

View File

@@ -55,10 +55,12 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
// Non-null if the plugin started successfully
private volatile BluetoothAdapter adapter = null;
AndroidBluetoothPlugin(Executor ioExecutor, AndroidExecutor androidExecutor,
AndroidBluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, AndroidExecutor androidExecutor,
Context appContext, SecureRandom secureRandom, Backoff backoff,
DuplexPluginCallback callback, int maxLatency) {
super(ioExecutor, secureRandom, backoff, callback, maxLatency);
super(connectionLimiter, ioExecutor, secureRandom, backoff, callback,
maxLatency);
this.androidExecutor = androidExecutor;
this.appContext = appContext;
}
@@ -154,7 +156,8 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
}
private DuplexTransportConnection wrapSocket(BluetoothSocket s) {
return new AndroidBluetoothTransportConnection(this, s);
return new AndroidBluetoothTransportConnection(this,
connectionLimiter, s);
}
@Override

View File

@@ -59,11 +59,13 @@ public class AndroidBluetoothPluginFactory implements DuplexPluginFactory {
@Override
public DuplexPlugin createPlugin(DuplexPluginCallback callback) {
BluetoothConnectionLimiter connectionLimiter =
new BluetoothConnectionLimiterImpl();
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(ioExecutor,
androidExecutor, appContext, secureRandom, backoff, callback,
MAX_LATENCY);
AndroidBluetoothPlugin plugin = new AndroidBluetoothPlugin(
connectionLimiter, ioExecutor, androidExecutor, appContext,
secureRandom, backoff, callback, MAX_LATENCY);
eventBus.addListener(plugin);
return plugin;
}

View File

@@ -14,10 +14,14 @@ import java.io.OutputStream;
class AndroidBluetoothTransportConnection
extends AbstractDuplexTransportConnection {
private final BluetoothConnectionLimiter connectionManager;
private final BluetoothSocket socket;
AndroidBluetoothTransportConnection(Plugin plugin, BluetoothSocket socket) {
AndroidBluetoothTransportConnection(Plugin plugin,
BluetoothConnectionLimiter connectionManager,
BluetoothSocket socket) {
super(plugin);
this.connectionManager = connectionManager;
this.socket = socket;
}
@@ -33,6 +37,10 @@ class AndroidBluetoothTransportConnection
@Override
protected void closeConnection(boolean exception) throws IOException {
socket.close();
try {
socket.close();
} finally {
connectionManager.connectionClosed(this);
}
}
}

View File

@@ -32,11 +32,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
@@ -52,7 +50,6 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -114,7 +111,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final SocketFactory torSocketFactory;
private final Backoff backoff;
private final DuplexPluginCallback callback;
@@ -136,14 +132,13 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
TorPlugin(Executor ioExecutor, ScheduledExecutorService scheduler,
Context appContext, LocationUtils locationUtils,
DevReporter reporter, SocketFactory torSocketFactory,
Backoff backoff, DuplexPluginCallback callback,
String architecture, int maxLatency, int maxIdleTime) {
SocketFactory torSocketFactory, Backoff backoff,
DuplexPluginCallback callback, String architecture,
int maxLatency, int maxIdleTime) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.torSocketFactory = torSocketFactory;
this.backoff = backoff;
this.callback = callback;
@@ -338,7 +333,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return zin;
}
private InputStream getConfigInputStream() throws IOException {
private InputStream getConfigInputStream() {
int resId = getResourceId("torrc");
return appContext.getResources().openRawResource(resId);
}
@@ -389,14 +384,6 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
}
private void sendDevReports() {
ioExecutor.execute(() -> {
// TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
});
}
private void bind() {
ioExecutor.execute(() -> {
// If there's already a port number stored in config, reuse it
@@ -511,7 +498,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
@Override
public void stop() throws PluginException {
public void stop() {
running = false;
tryToClose(socket);
if (networkStateReceiver != null)
@@ -545,20 +532,16 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
@Override
public void poll(Collection<ContactId> connected) {
public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return;
backoff.increment();
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
connectAndCallBack(e.getKey(), e.getValue());
}
}
private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
@@ -568,13 +551,8 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
@Override
public DuplexTransportConnection createConnection(ContactId c) {
public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
String onion = p.get(PROP_ONION);
if (StringUtils.isNullOrEmpty(onion)) return null;
if (!ONION.matcher(onion).matches()) {
@@ -624,10 +602,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
connectionStatus.getAndSetCircuitBuilt()) {
LOG.info("First circuit built");
backoff.reset();
if (isRunning()) {
sendDevReports();
callback.transportEnabled();
}
if (isRunning()) callback.transportEnabled();
}
}
@@ -656,10 +631,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (severity.equals("NOTICE") && msg.startsWith("Bootstrapped 100%")) {
connectionStatus.setBootstrapped();
backoff.reset();
if (isRunning()) {
sendDevReports();
callback.transportEnabled();
}
if (isRunning()) callback.transportEnabled();
}
}

View File

@@ -12,7 +12,6 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.util.AndroidUtils;
@@ -40,21 +39,18 @@ public class TorPluginFactory implements DuplexPluginFactory {
private final ScheduledExecutorService scheduler;
private final Context appContext;
private final LocationUtils locationUtils;
private final DevReporter reporter;
private final EventBus eventBus;
private final SocketFactory torSocketFactory;
private final BackoffFactory backoffFactory;
public TorPluginFactory(Executor ioExecutor,
ScheduledExecutorService scheduler, Context appContext,
LocationUtils locationUtils, DevReporter reporter,
EventBus eventBus, SocketFactory torSocketFactory,
BackoffFactory backoffFactory) {
LocationUtils locationUtils, EventBus eventBus,
SocketFactory torSocketFactory, BackoffFactory backoffFactory) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.appContext = appContext;
this.locationUtils = locationUtils;
this.reporter = reporter;
this.eventBus = eventBus;
this.torSocketFactory = torSocketFactory;
this.backoffFactory = backoffFactory;
@@ -94,7 +90,7 @@ public class TorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorPlugin plugin = new TorPlugin(ioExecutor, scheduler, appContext,
locationUtils, reporter, torSocketFactory, backoff, callback,
locationUtils, torSocketFactory, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;

View File

@@ -9,6 +9,7 @@ import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Parcel;
import android.os.StrictMode;
import android.provider.Settings;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -66,9 +67,12 @@ class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
@Override
protected void writeSeed() {
// Silence strict mode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskWrites();
super.writeSeed();
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT <= 18)
applyOpenSslFix();
StrictMode.setThreadPolicy(tp);
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html

View File

@@ -1,7 +1,9 @@
package org.briarproject.bramble.util;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.provider.Settings;
@@ -10,11 +12,15 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import static android.content.Context.MODE_PRIVATE;
public class AndroidUtils {
private static final Logger LOG =
Logger.getLogger(AndroidUtils.class.getName());
// Fake Bluetooth address returned by BluetoothAdapter on API 23 and later
private static final String FAKE_BLUETOOTH_ADDRESS = "02:00:00:00:00:00";
@@ -35,6 +41,7 @@ public class AndroidUtils {
public static String getBluetoothAddress(Context ctx,
BluetoothAdapter adapter) {
// Return the adapter's address if it's valid and not fake
@SuppressLint("HardwareIds")
String address = adapter.getAddress();
if (isValidBluetoothAddress(address)) return address;
// Return the address from settings if it's valid and not fake
@@ -51,17 +58,28 @@ public class AndroidUtils {
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
}
public static void deleteAppData(Context ctx) {
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
if (!prefs.edit().clear().commit())
LOG.warning("Could not clear shared preferences");
}
// Delete files, except lib and shared_prefs directories
File dataDir = new File(ctx.getApplicationInfo().dataDir);
File[] children = dataDir.listFiles();
if (children != null) {
if (children == null) {
LOG.warning("Could not list files in app data dir");
} else {
for (File child : children) {
if (!child.getName().equals("lib"))
String name = child.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) {
IoUtils.deleteFileOrDir(child);
}
}
}
// Recreate the cache dir as some OpenGL drivers expect it to exist
new File(dataDir, "cache").mkdir();
if (!new File(dataDir, "cache").mkdir())
LOG.warning("Could not recreate cache dir");
}
public static File getReportDir(Context ctx) {

View File

@@ -2,6 +2,7 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'witness'
dependencies {
@@ -14,6 +15,8 @@ dependencies {
testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3"
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
}
dependencyVerification {
@@ -26,6 +29,9 @@ dependencyVerification {
'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.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
@@ -33,6 +39,7 @@ dependencyVerification {
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
]
}
@@ -48,8 +55,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
testOutput jarTest
}
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -12,19 +12,19 @@ public interface ContactGroupFactory {
/**
* Creates a group that is not shared with any contacts.
*/
Group createLocalGroup(ClientId clientId, int clientVersion);
Group createLocalGroup(ClientId clientId, int majorVersion);
/**
* Creates a group for the given client to share with the given contact.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact);
/**
* Creates a group for the given client to share between the given authors
* identified by their AuthorIds.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2);
}

View File

@@ -13,9 +13,9 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
public interface ContactExchangeTask {
/**
* The current version of the contact exchange protocol
* The current version of the contact exchange protocol.
*/
int PROTOCOL_VERSION = 0;
byte PROTOCOL_VERSION = 1;
/**
* Label for deriving Alice's header key from the master secret.

View File

@@ -24,6 +24,8 @@ public interface ContactManager {
* 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(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
@@ -38,11 +40,14 @@ public interface ContactManager {
/**
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
* 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 master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active)
throws DbException;
/**
* Returns the contact with the given ID.

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.contact;
/**
* Record types for the contact exchange protocol.
*/
public interface RecordTypes {
byte CONTACT_INFO = 0;
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey).
* A key pair consisting of a {@link PublicKey} and a {@link PrivateKey}.
*/
@Immutable
@NotNullByDefault

View File

@@ -24,9 +24,9 @@ public class BdfDictionary extends TreeMap<String, Object> {
* );
* </pre>
*/
public static BdfDictionary of(Entry<String, Object>... entries) {
public static BdfDictionary of(Entry<String, ?>... entries) {
BdfDictionary d = new BdfDictionary();
for (Entry<String, Object> e : entries) d.put(e.getKey(), e.getValue());
for (Entry<String, ?> e : entries) d.put(e.getKey(), e.getValue());
return d;
}

View File

@@ -8,6 +8,7 @@ import java.io.IOException;
public interface BdfReader {
int DEFAULT_NESTED_LIMIT = 5;
int DEFAULT_MAX_BUFFER_SIZE = 64 * 1024;
boolean eof() throws IOException;
@@ -39,13 +40,13 @@ public interface BdfReader {
boolean hasString() throws IOException;
String readString(int maxLength) throws IOException;
String readString() throws IOException;
void skipString() throws IOException;
boolean hasRaw() throws IOException;
byte[] readRaw(int maxLength) throws IOException;
byte[] readRaw() throws IOException;
void skipRaw() throws IOException;

View File

@@ -9,5 +9,6 @@ public interface BdfReaderFactory {
BdfReader createReader(InputStream in);
BdfReader createReader(InputStream in, int nestedLimit);
BdfReader createReader(InputStream in, int nestedLimit,
int maxBufferSize);
}

View File

@@ -104,18 +104,12 @@ public interface DatabaseComponent {
throws DbException;
/**
* Stores the given transport keys, optionally binding them to the given
* contact, and returns a key set ID.
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
KeySetId addTransportKeys(Transaction txn, @Nullable ContactId c,
KeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException;
/**
* Binds the given keys for the given transport to the given contact.
*/
void bindTransportKeys(Transaction txn, ContactId c, TransportId t,
KeySetId k) throws DbException;
/**
* Returns true if the database contains the given contact for the given
* local pseudonym.
@@ -136,8 +130,8 @@ public interface DatabaseComponent {
/**
* Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any
* other associated data are not deleted.
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
* dependencies, metadata, and any other associated state are not deleted.
*/
void deleteMessage(Transaction txn, MessageId m) throws DbException;
@@ -241,7 +235,8 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Collection<Group> getGroups(Transaction txn, ClientId c) throws DbException;
Collection<Group> getGroups(Transaction txn, ClientId c, int majorVersion)
throws DbException;
/**
* Returns the given group's visibility to the given contact, or
@@ -266,6 +261,14 @@ public interface DatabaseComponent {
*/
Collection<LocalAuthor> getLocalAuthors(Transaction txn) throws DbException;
/**
* Returns the IDs of all delivered messages in the given group.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
throws DbException;
/**
* Returns the IDs of any messages that need to be validated.
* <p/>
@@ -310,9 +313,9 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the metadata for any messages in the given group with metadata
* that matches all entries in the given query. If the query is empty, the
* metadata for all messages is returned.
* Returns the metadata for any delivered messages in the given group with
* metadata that matches all entries in the given query. If the query is
* empty, the metadata for all delivered messages is returned.
* <p/>
* Read-only.
*/
@@ -328,8 +331,8 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the metadata for the given delivered and pending message.
* This is meant to be only used by the ValidationManager
* Returns the metadata for the given delivered or pending message.
* This is only meant to be used by the ValidationManager.
* <p/>
* Read-only.
*/
@@ -337,8 +340,8 @@ public interface DatabaseComponent {
throws DbException;
/**
* Returns the status of all messages in the given group with respect to
* the given contact.
* Returns the status of all delivered messages in the given group with
* respect to the given contact.
* <p/>
* Read-only.
*/
@@ -373,8 +376,8 @@ public interface DatabaseComponent {
State getMessageState(Transaction txn, MessageId m) throws DbException;
/**
* Returns the status of the given message with respect to the given
* contact.
* Returns the status of the given delivered message with respect to the
* given contact.
* <p/>
* Read-only.
*/

View File

@@ -14,6 +14,8 @@ public interface DatabaseConfig {
File getDatabaseDirectory();
File getDatabaseKeyDirectory();
void setEncryptionKey(SecretKey key);
@Nullable

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.keyagreement.event;
import org.briarproject.bramble.api.event.Event;
/**
* An event that is broadcast when a BQP task stops listening.
*/
public class KeyAgreementStoppedListeningEvent extends Event {
}

View File

@@ -43,17 +43,20 @@ public interface LifecycleManager {
}
/**
* Registers a {@link Service} to be started and stopped.
* Registers a {@link Service} to be started and stopped. This method
* should be called before {@link #startServices(String)}.
*/
void registerService(Service s);
/**
* Registers a {@link Client} to be started.
* Registers a {@link Client} to be started. This method should be called
* before {@link #startServices(String)}.
*/
void registerClient(Client c);
/**
* Registers an {@link ExecutorService} to be shut down.
* Registers an {@link ExecutorService} to be shut down. This method
* should be called before {@link #startServices(String)}.
*/
void registerForShutdown(ExecutorService e);

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.plugin;
public interface FileConstants {
String PROP_PATH = "path";
}

View File

@@ -2,8 +2,9 @@ package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.util.Collection;
import java.util.Map;
@NotNullByDefault
public interface Plugin {
@@ -39,21 +40,19 @@ public interface Plugin {
boolean isRunning();
/**
* Returns true if the plugin's {@link #poll(Collection)} method should be
* called periodically to attempt to establish connections.
* Returns true if the plugin should be polled periodically to attempt to
* establish connections.
*/
boolean shouldPoll();
/**
* Returns the desired interval in milliseconds between calls to the
* plugin's {@link #poll(Collection)} method.
* Returns the desired interval in milliseconds between polling attempts.
*/
int getPollingInterval();
/**
* Attempts to establish connections to contacts, passing any created
* connections to the callback. To avoid creating redundant connections,
* the plugin may exclude the given contacts from polling.
* Attempts to establish connections to the given contacts, passing any
* created connections to the callback.
*/
void poll(Collection<ContactId> connected);
void poll(Map<ContactId, TransportProperties> contacts);
}

View File

@@ -1,12 +1,9 @@
package org.briarproject.bramble.api.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import java.util.Map;
/**
* An interface through which a transport plugin interacts with the rest of
* the application.
@@ -25,17 +22,7 @@ public interface PluginCallback {
TransportProperties getLocalProperties();
/**
* Returns the plugin's remote transport properties.
*/
Map<ContactId, TransportProperties> getRemoteProperties();
/**
* Returns the plugin's remote transport properties for the given contact.
*/
TransportProperties getRemoteProperties(ContactId c);
/**
* Merges the given settings with the namespaced settings
* Merges the given settings with the plugin's settings
*/
void mergeSettings(Settings s);
@@ -45,34 +32,12 @@ public interface PluginCallback {
void mergeLocalProperties(TransportProperties p);
/**
* Presents the user with a choice among two or more named options and
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
/**
* Signal that the transport got enabled.
* Signals that the transport is enabled.
*/
void transportEnabled();
/**
* Signal that the transport got disabled.
* Signals that the transport is disabled.
*/
void transportDisabled();
}

View File

@@ -12,4 +12,6 @@ public interface PluginConfig {
Collection<DuplexPluginFactory> getDuplexFactories();
Collection<SimplexPluginFactory> getSimplexFactories();
boolean shouldPoll();
}

View File

@@ -22,11 +22,6 @@ public interface TransportConnectionWriter {
*/
int getMaxIdleTime();
/**
* Returns the capacity of the transport connection in bytes.
*/
long getCapacity();
/**
* Returns an output stream for writing to the transport connection.
*/

View File

@@ -71,11 +71,6 @@ public abstract class AbstractDuplexTransportConnection
return plugin.getMaxIdleTime();
}
@Override
public long getCapacity() {
return Long.MAX_VALUE;
}
@Override
public OutputStream getOutputStream() throws IOException {
return AbstractDuplexTransportConnection.this.getOutputStream();

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.duplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable;
@@ -15,12 +15,11 @@ import javax.annotation.Nullable;
public interface DuplexPlugin extends Plugin {
/**
* Attempts to create and return a connection to the given contact using
* the current transport and configuration properties. Returns null if a
* connection cannot be created.
* Attempts to create and return a connection using the given transport
* properties. Returns null if a connection cannot be created.
*/
@Nullable
DuplexTransportConnection createConnection(ContactId c);
DuplexTransportConnection createConnection(TransportProperties p);
/**
* Returns true if the plugin supports short-range key agreement.

View File

@@ -5,7 +5,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginCallback;
/**
* An interface for handling connections created by a duplex transport plugin.
* An interface through which a duplex plugin interacts with the rest of the
* application.
*/
@NotNullByDefault
public interface DuplexPluginCallback extends PluginCallback {

View File

@@ -1,10 +1,10 @@
package org.briarproject.bramble.api.plugin.simplex;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.properties.TransportProperties;
import javax.annotation.Nullable;
@@ -15,18 +15,16 @@ import javax.annotation.Nullable;
public interface SimplexPlugin extends Plugin {
/**
* Attempts to create and return a reader for the given contact using the
* current transport and configuration properties. Returns null if a reader
* cannot be created.
* Attempts to create and return a reader for the given transport
* properties. Returns null if a reader cannot be created.
*/
@Nullable
TransportConnectionReader createReader(ContactId c);
TransportConnectionReader createReader(TransportProperties p);
/**
* Attempts to create and return a writer for the given contact using the
* current transport and configuration properties. Returns null if a writer
* cannot be created.
* Attempts to create and return a writer for the given transport
* properties. Returns null if a writer cannot be created.
*/
@Nullable
TransportConnectionWriter createWriter(ContactId c);
TransportConnectionWriter createWriter(TransportProperties p);
}

View File

@@ -7,8 +7,8 @@ import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
/**
* An interface for handling readers and writers created by a simplex transport
* plugin.
* An interface through which a simplex plugin interacts with the rest of the
* application.
*/
@NotNullByDefault
public interface SimplexPluginCallback extends PluginCallback {

View File

@@ -15,12 +15,17 @@ public interface TransportPropertyManager {
/**
* The unique ID of the transport property client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.properties");
/**
* The current version of the transport property client.
* The current major version of the transport property client.
*/
int CLIENT_VERSION = 0;
int MAJOR_VERSION = 0;
/**
* The current minor version of the transport property client.
*/
int MINOR_VERSION = 0;
/**
* Stores the given properties received while adding a contact - they will
@@ -37,8 +42,8 @@ public interface TransportPropertyManager {
/**
* Returns the local transport properties for all transports.
* <br/>
* TODO: Transaction can be read-only when code is simplified
* <p/>
* Read-only.
*/
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException;

View File

@@ -0,0 +1,36 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class Record {
public static final int RECORD_HEADER_BYTES = 4;
public static final int MAX_RECORD_PAYLOAD_BYTES = 48 * 1024; // 48 KiB
private final byte protocolVersion, recordType;
private final byte[] payload;
public Record(byte protocolVersion, byte recordType, byte[] payload) {
if (payload.length > MAX_RECORD_PAYLOAD_BYTES)
throw new IllegalArgumentException();
this.protocolVersion = protocolVersion;
this.recordType = recordType;
this.payload = payload;
}
public byte getProtocolVersion() {
return protocolVersion;
}
public byte getRecordType() {
return recordType;
}
public byte[] getPayload() {
return payload;
}
}

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.EOFException;
import java.io.IOException;
@NotNullByDefault
public interface RecordReader {
/**
* Reads and returns the next record.
*
* @throws EOFException if the end of the stream is reached without reading
* a complete record
*/
Record readRecord() throws IOException;
void close() throws IOException;
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.bramble.api.record;
import java.io.InputStream;
public interface RecordReaderFactory {
RecordReader createRecordReader(InputStream in);
}

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.record;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface RecordWriter {
void writeRecord(Record r) throws IOException;
void flush() throws IOException;
void close() throws IOException;
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.bramble.api.record;
import java.io.OutputStream;
public interface RecordWriterFactory {
RecordWriter createRecordWriter(OutputStream out);
}

View File

@@ -3,10 +3,14 @@ package org.briarproject.bramble.api.reporting;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
@NotNullByDefault
public interface DevConfig {
PublicKey getDevPublicKey();
String getDevOnionAddress();
File getReportDir();
}

View File

@@ -23,8 +23,6 @@ public interface DevReporter {
/**
* Sends any reports previously stored on disk.
*
* @param reportDir the directory where reports are stored.
*/
void sendReports(File reportDir);
void sendReports();
}

View File

@@ -5,9 +5,24 @@ import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPT
public class Group {
public enum Visibility {
INVISIBLE, // The group is not visible
VISIBLE, // The group is visible but messages are not shared
SHARED // The group is visible and messages are shared
INVISIBLE(0), // The group is not visible
VISIBLE(1), // The group is visible, messages are accepted but not sent
SHARED(2); // The group is visible, messages are accepted and sent
private final int value;
Visibility(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static Visibility min(Visibility a, Visibility b) {
return a.getValue() < b.getValue() ? a : b;
}
}
/**
@@ -17,13 +32,16 @@ public class Group {
private final GroupId id;
private final ClientId clientId;
private final int majorVersion;
private final byte[] descriptor;
public Group(GroupId id, ClientId clientId, byte[] descriptor) {
public Group(GroupId id, ClientId clientId, int majorVersion,
byte[] descriptor) {
if (descriptor.length > MAX_GROUP_DESCRIPTOR_LENGTH)
throw new IllegalArgumentException();
this.id = id;
this.clientId = clientId;
this.majorVersion = majorVersion;
this.descriptor = descriptor;
}
@@ -41,6 +59,13 @@ public class Group {
return clientId;
}
/**
* Returns the major version of the client to which the group belongs.
*/
public int getMajorVersion() {
return majorVersion;
}
/**
* Returns the group's descriptor.
*/

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface GroupFactory {
/**
* Creates a group with the given client ID, client version and descriptor.
* Creates a group with the given client ID, major version and descriptor.
*/
Group createGroup(ClientId c, int clientVersion, byte[] descriptor);
Group createGroup(ClientId c, int majorVersion, byte[] descriptor);
}

View File

@@ -7,5 +7,7 @@ public interface MessageFactory {
Message createMessage(GroupId g, long timestamp, byte[] body);
Message createMessage(byte[] raw);
Message createMessage(MessageId m, byte[] raw);
}

View File

@@ -2,6 +2,8 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.UniqueId;
import static org.briarproject.bramble.api.record.Record.MAX_RECORD_PAYLOAD_BYTES;
public interface SyncConstants {
/**
@@ -10,16 +12,8 @@ public interface SyncConstants {
byte PROTOCOL_VERSION = 0;
/**
* The length of the record header in bytes.
* The maximum length of a group descriptor in bytes.
*/
int RECORD_HEADER_LENGTH = 4;
/**
* The maximum length of the record payload in bytes.
*/
int MAX_RECORD_PAYLOAD_LENGTH = 48 * 1024; // 48 KiB
/** The maximum length of a group descriptor in bytes. */
int MAX_GROUP_DESCRIPTOR_LENGTH = 16 * 1024; // 16 KiB
/**
@@ -40,5 +34,5 @@ public interface SyncConstants {
/**
* The maximum number of message IDs in an ack, offer or request record.
*/
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_LENGTH / UniqueId.LENGTH;
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface RecordReader {
public interface SyncRecordReader {
boolean eof() throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.InputStream;
@NotNullByDefault
public interface RecordReaderFactory {
public interface SyncRecordReaderFactory {
RecordReader createRecordReader(InputStream in);
SyncRecordReader createRecordReader(InputStream in);
}

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
@NotNullByDefault
public interface RecordWriter {
public interface SyncRecordWriter {
void writeAck(Ack a) throws IOException;

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.OutputStream;
@NotNullByDefault
public interface RecordWriterFactory {
public interface SyncRecordWriterFactory {
RecordWriter createRecordWriter(OutputStream out);
SyncRecordWriter createRecordWriter(OutputStream out);
}

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamWriter;
import java.io.InputStream;
import java.io.OutputStream;
@NotNullByDefault
public interface SyncSessionFactory {
@@ -12,8 +12,8 @@ public interface SyncSessionFactory {
SyncSession createIncomingSession(ContactId c, InputStream in);
SyncSession createSimplexOutgoingSession(ContactId c, int maxLatency,
OutputStream out);
StreamWriter streamWriter);
SyncSession createDuplexOutgoingSession(ContactId c, int maxLatency,
int maxIdleTime, OutputStream out);
int maxIdleTime, StreamWriter streamWriter);
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.api.sync;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
@@ -33,15 +34,20 @@ public interface ValidationManager {
}
/**
* Sets the message validator for the given client.
* Registers the message validator for the given client. This method
* should be called before {@link LifecycleManager#startServices(String)}.
*/
void registerMessageValidator(ClientId c, MessageValidator v);
void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
/**
* Sets the incoming message hook for the given client. The hook will be
* called once for each incoming message that passes validation.
* Registers the incoming message hook for the given client. The hook will
* be called once for each incoming message that passes validation. This
* method should be called before
* {@link LifecycleManager#startServices(String)}.
*/
void registerIncomingMessageHook(ClientId c, IncomingMessageHook hook);
void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
interface MessageValidator {

View File

@@ -7,12 +7,12 @@ package org.briarproject.bramble.api.system;
public interface Clock {
/**
* @see {@link System#currentTimeMillis()}
* @see System#currentTimeMillis()
*/
long currentTimeMillis();
/**
* @see {@link Thread#sleep(long)}
* @see Thread#sleep(long)
*/
void sleep(long milliseconds) throws InterruptedException;
}

View File

@@ -19,44 +19,24 @@ public interface KeyManager {
/**
* Informs the key manager that a new contact has been added. Derives and
* stores a set of transport keys for communicating with the contact over
* each transport.
* each transport and returns the key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams
*/
void addContact(Transaction txn, ContactId c, SecretKey master,
long timestamp, boolean alice) throws DbException;
/**
* Derives and stores a set of unbound transport keys for each transport
* and returns the key set IDs.
* <p/>
* The keys must be bound before they can be used for incoming streams,
* and also activated before they can be used for outgoing streams.
*/
Map<TransportId, KeySetId> addUnboundKeys(Transaction txn, SecretKey master,
long timestamp, boolean alice) throws DbException;
/**
* Binds the given transport keys to the given contact.
*/
void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
SecretKey master, long timestamp, boolean alice, boolean active)
throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams. Keys must
* be bound before they are activated.
* Marks the given transport keys as usable for outgoing streams.
*/
void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException;
/**
* Removes the given transport keys, which must not have been bound, from
* the manager and the database.
*/
void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException;
/**
* Returns true if we have keys that can be used for outgoing streams to
* the given contact over the given transport.

View File

@@ -3,23 +3,20 @@ package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A set of transport keys for communicating with a contact. If the keys have
* not yet been bound to a contact, {@link #getContactId()}} returns null.
* A set of transport keys for communicating with a contact.
*/
@Immutable
@NotNullByDefault
public class KeySet {
private final KeySetId keySetId;
@Nullable
private final ContactId contactId;
private final TransportKeys transportKeys;
public KeySet(KeySetId keySetId, @Nullable ContactId contactId,
public KeySet(KeySetId keySetId, ContactId contactId,
TransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
@@ -30,7 +27,6 @@ public class KeySet {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}

View File

@@ -0,0 +1,19 @@
package org.briarproject.bramble.api.transport;
import java.io.IOException;
import java.io.OutputStream;
/**
* An interface for writing data to a transport connection. Data will be
* encrypted and authenticated before being written to the connection.
*/
public interface StreamWriter {
OutputStream getOutputStream();
/**
* Sends the end of stream marker, informing the recipient that no more
* data will be sent. The connection is flushed but not closed.
*/
void sendEndOfStream() throws IOException;
}

View File

@@ -12,12 +12,12 @@ public interface StreamWriterFactory {
* Creates an {@link OutputStream OutputStream} for writing to a
* transport stream
*/
OutputStream createStreamWriter(OutputStream out, StreamContext ctx);
StreamWriter createStreamWriter(OutputStream out, StreamContext ctx);
/**
* Creates an {@link OutputStream OutputStream} for writing to a contact
* exchange stream.
*/
OutputStream createContactExchangeStreamWriter(OutputStream out,
StreamWriter createContactExchangeStreamWriter(OutputStream out,
SecretKey headerKey);
}

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.ui;
public interface UiCallback {
/**
* Presents the user with a choice among two or more named options and
* returns the user's response. The message may consist of a translatable
* format string and arguments.
*
* @return an index into the array of options indicating the user's choice,
* or -1 if the user cancelled the choice.
*/
int showChoice(String[] options, String... message);
/**
* Asks the user to confirm an action and returns the user's response. The
* message may consist of a translatable format string and arguments.
*/
boolean showConfirmationMessage(String... message);
/**
* Shows a message to the user. The message may consist of a translatable
* format string and arguments.
*/
void showMessage(String... message);
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.ClientId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class ClientMajorVersion implements Comparable<ClientMajorVersion> {
private final ClientId clientId;
private final int majorVersion;
public ClientMajorVersion(ClientId clientId, int majorVersion) {
this.clientId = clientId;
this.majorVersion = majorVersion;
}
public ClientId getClientId() {
return clientId;
}
public int getMajorVersion() {
return majorVersion;
}
@Override
public boolean equals(Object o) {
if (o instanceof ClientMajorVersion) {
ClientMajorVersion cv = (ClientMajorVersion) o;
return clientId.equals(cv.clientId)
&& majorVersion == cv.majorVersion;
}
return false;
}
@Override
public int hashCode() {
return (clientId.hashCode() << 16) + majorVersion;
}
@Override
public int compareTo(ClientMajorVersion cv) {
int compare = clientId.compareTo(cv.clientId);
if (compare != 0) return compare;
return majorVersion - cv.majorVersion;
}
}

View File

@@ -0,0 +1,45 @@
package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
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.sync.ClientId;
import org.briarproject.bramble.api.sync.Group.Visibility;
@NotNullByDefault
public interface ClientVersioningManager {
/**
* The unique ID of the versioning client.
*/
ClientId CLIENT_ID = new ClientId("org.briarproject.bramble.versioning");
/**
* The current major version of the versioning client.
*/
int MAJOR_VERSION = 0;
/**
* Registers a client that will be advertised to contacts. The hook will
* be called when the visibility of the client changes. This method should
* be called before {@link LifecycleManager#startServices(String)}.
*/
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
ClientVersioningHook hook);
/**
* Returns the visibility of the given client with respect to the given
* contact.
*/
Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook {
void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException;
}
}

View File

@@ -9,25 +9,40 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
public class IoUtils {
private static final Logger LOG = Logger.getLogger(IoUtils.class.getName());
public static void deleteFileOrDir(File f) {
if (f.isFile()) {
f.delete();
delete(f);
} else if (f.isDirectory()) {
File[] children = f.listFiles();
if (children != null)
if (children == null) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Could not list files in "
+ f.getAbsolutePath());
}
} else {
for (File child : children) deleteFileOrDir(child);
f.delete();
}
delete(f);
}
}
public static void copyAndClose(InputStream in, OutputStream out)
throws IOException {
private static void delete(File f) {
if (!f.delete() && LOG.isLoggable(WARNING))
LOG.warning("Could not delete " + f.getAbsolutePath());
}
public static void copyAndClose(InputStream in, OutputStream out) {
byte[] buf = new byte[4096];
try {
while (true) {

View File

@@ -22,19 +22,6 @@ public class OsUtils {
return os != null && os.contains("Mac OS");
}
public static boolean isMacLeopardOrNewer() {
if (!isMac() || version == null) return false;
try {
String[] v = version.split("\\.");
if (v.length != 3) return false;
int major = Integer.parseInt(v[0]);
int minor = Integer.parseInt(v[1]);
return major >= 10 && minor >= 5;
} catch (NumberFormatException e) {
return false;
}
}
public static boolean isLinux() {
return os != null && os.contains("Linux") && !isAndroid();
}

View File

@@ -117,15 +117,16 @@ public class TestUtils {
return new Author(id, FORMAT_VERSION, name, publicKey);
}
public static Group getGroup(ClientId clientId) {
public static Group getGroup(ClientId clientId, int majorVersion) {
int descriptorLength = 1 + random.nextInt(MAX_GROUP_DESCRIPTOR_LENGTH);
return getGroup(clientId, descriptorLength);
return getGroup(clientId, majorVersion, descriptorLength);
}
public static Group getGroup(ClientId clientId, int descriptorLength) {
public static Group getGroup(ClientId clientId, int majorVersion,
int descriptorLength) {
GroupId groupId = new GroupId(getRandomId());
byte[] descriptor = getRandomBytes(descriptorLength);
return new Group(groupId, clientId, descriptor);
return new Group(groupId, clientId, majorVersion, descriptor);
}
public static Message getMessage(GroupId groupId) {

View File

@@ -2,6 +2,7 @@ apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'ru.vyarus.animalsniffer'
apply plugin: 'net.ltgt.apt'
apply plugin: 'idea'
apply plugin: 'witness'
@@ -26,12 +27,13 @@ dependencies {
testImplementation "org.hamcrest:hamcrest-core:1.3"
testApt 'com.google.dagger:dagger-compiler:2.0.2'
signature 'org.codehaus.mojo.signature:java16:1.1@signature'
}
dependencyVerification {
verify = [
'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.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
@@ -45,6 +47,9 @@ dependencyVerification {
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
@@ -53,6 +58,7 @@ dependencyVerification {
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
]
@@ -69,8 +75,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
testOutput jarTest
}
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -1,21 +1,23 @@
package org.briarproject.bramble;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
public interface BrambleCoreEagerSingletons {
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(CryptoExecutorModule.EagerSingletons init);
void inject(DatabaseExecutorModule.EagerSingletons init);
@@ -27,9 +29,13 @@ public interface BrambleCoreEagerSingletons {
void inject(PropertiesModule.EagerSingletons init);
void inject(ReportingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseExecutorModule;
@@ -12,6 +13,7 @@ import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.properties.PropertiesModule;
import org.briarproject.bramble.record.RecordModule;
import org.briarproject.bramble.reliability.ReliabilityModule;
import org.briarproject.bramble.reporting.ReportingModule;
import org.briarproject.bramble.settings.SettingsModule;
@@ -19,6 +21,7 @@ import org.briarproject.bramble.socks.SocksModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import dagger.Module;
@@ -26,6 +29,7 @@ import dagger.Module;
ClientModule.class,
ContactModule.class,
CryptoModule.class,
CryptoExecutorModule.class,
DataModule.class,
DatabaseModule.class,
DatabaseExecutorModule.class,
@@ -35,26 +39,30 @@ import dagger.Module;
LifecycleModule.class,
PluginModule.class,
PropertiesModule.class,
RecordModule.class,
ReliabilityModule.class,
ReportingModule.class,
SettingsModule.class,
SocksModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
TransportModule.class,
VersioningModule.class
})
public class BrambleCoreModule {
public static void initEagerSingletons(BrambleCoreEagerSingletons c) {
c.inject(new ContactModule.EagerSingletons());
c.inject(new CryptoModule.EagerSingletons());
c.inject(new CryptoExecutorModule.EagerSingletons());
c.inject(new DatabaseExecutorModule.EagerSingletons());
c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new ReportingModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons());
c.inject(new VersioningModule.EagerSingletons());
}
}

View File

@@ -32,25 +32,25 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
}
@Override
public Group createLocalGroup(ClientId clientId, int clientVersion) {
return groupFactory.createGroup(clientId, clientVersion,
public Group createLocalGroup(ClientId clientId, int majorVersion) {
return groupFactory.createGroup(clientId, majorVersion,
LOCAL_GROUP_DESCRIPTOR);
}
@Override
public Group createContactGroup(ClientId clientId, int clientVersion,
public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, clientVersion, descriptor);
return groupFactory.createGroup(clientId, majorVersion, descriptor);
}
@Override
public Group createContactGroup(ClientId clientId, int clientVersion,
public Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2) {
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
return groupFactory.createGroup(clientId, clientVersion, descriptor);
return groupFactory.createGroup(clientId, majorVersion, descriptor);
}
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {

View File

@@ -1,23 +1,20 @@
package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriter;
import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.db.ContactExistsException;
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.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
@@ -26,30 +23,31 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.record.Record;
import org.briarproject.bramble.api.record.RecordReader;
import org.briarproject.bramble.api.record.RecordReaderFactory;
import org.briarproject.bramble.api.record.RecordWriter;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
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_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.contact.RecordTypes.CONTACT_INFO;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.api.plugin.TransportId.MAX_TRANSPORT_ID_LENGTH;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
import static org.briarproject.bramble.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -62,9 +60,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
"org.briarproject.briar.contact/EXCHANGE";
private final DatabaseComponent db;
private final AuthorFactory authorFactory;
private final BdfReaderFactory bdfReaderFactory;
private final BdfWriterFactory bdfWriterFactory;
private final ClientHelper clientHelper;
private final RecordReaderFactory recordReaderFactory;
private final RecordWriterFactory recordWriterFactory;
private final Clock clock;
private final ConnectionManager connectionManager;
private final ContactManager contactManager;
@@ -81,17 +79,17 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private volatile boolean alice;
@Inject
ContactExchangeTaskImpl(DatabaseComponent db,
AuthorFactory authorFactory, BdfReaderFactory bdfReaderFactory,
BdfWriterFactory bdfWriterFactory, Clock clock,
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory, Clock clock,
ConnectionManager connectionManager, ContactManager contactManager,
TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) {
this.db = db;
this.authorFactory = authorFactory;
this.bdfReaderFactory = bdfReaderFactory;
this.bdfWriterFactory = bdfWriterFactory;
this.clientHelper = clientHelper;
this.recordReaderFactory = recordReaderFactory;
this.recordWriterFactory = recordWriterFactory;
this.clock = clock;
this.connectionManager = connectionManager;
this.contactManager = contactManager;
@@ -126,18 +124,18 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
tryToClose(conn);
return;
}
// Get the local transport properties
Map<TransportId, TransportProperties> localProperties, remoteProperties;
Map<TransportId, TransportProperties> localProperties;
try {
localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
tryToClose(conn);
return;
}
@@ -151,159 +149,138 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
InputStream streamReader =
streamReaderFactory.createContactExchangeStreamReader(in,
alice ? bobHeaderKey : aliceHeaderKey);
BdfReader r = bdfReaderFactory.createReader(streamReader);
RecordReader recordReader =
recordReaderFactory.createRecordReader(streamReader);
// Create the writers
OutputStream streamWriter =
StreamWriter streamWriter =
streamWriterFactory.createContactExchangeStreamWriter(out,
alice ? aliceHeaderKey : bobHeaderKey);
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
RecordWriter recordWriter =
recordWriterFactory.createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] localNonce = alice ? aliceNonce : bobNonce;
byte[] remoteNonce = alice ? bobNonce : aliceNonce;
// Exchange pseudonyms, signed nonces, and timestamps
// Sign the nonce
byte[] localSignature = sign(localAuthor, localNonce);
// Exchange contact info
long localTimestamp = clock.currentTimeMillis();
Author remoteAuthor;
long remoteTimestamp;
ContactInfo remoteInfo;
try {
if (alice) {
sendPseudonym(w, aliceNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteAuthor = receivePseudonym(r, bobNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendContactInfo(recordWriter, localAuthor, localProperties,
localSignature, localTimestamp);
recordWriter.flush();
remoteInfo = receiveContactInfo(recordReader);
} else {
remoteAuthor = receivePseudonym(r, aliceNonce);
remoteTimestamp = receiveTimestamp(r);
remoteProperties = receiveTransportProperties(r);
sendPseudonym(w, bobNonce);
sendTimestamp(w, localTimestamp);
sendTransportProperties(w, localProperties);
w.flush();
remoteInfo = receiveContactInfo(recordReader);
sendContactInfo(recordWriter, localAuthor, localProperties,
localSignature, localTimestamp);
recordWriter.flush();
}
// Close the outgoing stream and expect EOF on the incoming stream
w.close();
if (!r.eof()) LOG.warning("Unexpected data at end of connection");
} catch (GeneralSecurityException | IOException e) {
// Send EOF on the outgoing stream
streamWriter.sendEndOfStream();
// Skip any remaining records from the incoming stream
try {
while (true) recordReader.readRecord();
} catch (EOFException expected) {
LOG.info("End of stream");
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
tryToClose(conn);
return;
}
// Verify the contact's signature
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
LOG.warning("Invalid signature");
listener.contactExchangeFailed();
tryToClose(conn);
return;
}
// The agreed timestamp is the minimum of the peers' timestamps
long timestamp = Math.min(localTimestamp, remoteTimestamp);
long timestamp = Math.min(localTimestamp, remoteInfo.timestamp);
try {
// Add the contact
ContactId contactId = addContact(remoteAuthor, timestamp,
remoteProperties);
ContactId contactId = addContact(remoteInfo.author, timestamp,
remoteInfo.properties);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId,
conn);
// Pseudonym exchange succeeded
LOG.info("Pseudonym exchange succeeded");
listener.contactExchangeSucceeded(remoteAuthor);
listener.contactExchangeSucceeded(remoteInfo.author);
} catch (ContactExistsException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
tryToClose(conn, true);
listener.duplicateContact(remoteAuthor);
tryToClose(conn);
listener.duplicateContact(remoteInfo.author);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
tryToClose(conn, true);
tryToClose(conn);
listener.contactExchangeFailed();
}
}
private void sendPseudonym(BdfWriter w, byte[] nonce)
throws GeneralSecurityException, IOException {
// Sign the nonce
byte[] privateKey = localAuthor.getPrivateKey();
byte[] sig = crypto.sign(SIGNING_LABEL_EXCHANGE, nonce, privateKey);
// Write the name, public key and signature
w.writeListStart();
w.writeLong(localAuthor.getFormatVersion());
w.writeString(localAuthor.getName());
w.writeRaw(localAuthor.getPublicKey());
w.writeRaw(sig);
w.writeListEnd();
LOG.info("Sent pseudonym");
}
private Author receivePseudonym(BdfReader r, byte[] nonce)
throws GeneralSecurityException, IOException {
// Read the format version, name, public key and signature
r.readListStart();
int formatVersion = (int) r.readLong();
if (formatVersion != FORMAT_VERSION) throw new FormatException();
String name = r.readString(MAX_AUTHOR_NAME_LENGTH);
if (name.isEmpty()) throw new FormatException();
byte[] publicKey = r.readRaw(MAX_PUBLIC_KEY_LENGTH);
if (publicKey.length == 0) throw new FormatException();
byte[] sig = r.readRaw(MAX_SIGNATURE_LENGTH);
if (sig.length == 0) throw new FormatException();
r.readListEnd();
LOG.info("Received pseudonym");
// Verify the signature
if (!crypto.verifySignature(sig, SIGNING_LABEL_EXCHANGE, nonce,
publicKey)) {
if (LOG.isLoggable(INFO))
LOG.info("Invalid signature");
throw new GeneralSecurityException();
private byte[] sign(LocalAuthor author, byte[] nonce) {
try {
return crypto.sign(SIGNING_LABEL_EXCHANGE, nonce,
author.getPrivateKey());
} catch (GeneralSecurityException e) {
throw new AssertionError();
}
return authorFactory.createAuthor(formatVersion, name, publicKey);
}
private void sendTimestamp(BdfWriter w, long timestamp)
private boolean verify(Author author, byte[] nonce, byte[] signature) {
try {
return crypto.verifySignature(signature, SIGNING_LABEL_EXCHANGE,
nonce, author.getPublicKey());
} catch (GeneralSecurityException e) {
return false;
}
}
private void sendContactInfo(RecordWriter recordWriter, Author author,
Map<TransportId, TransportProperties> properties, byte[] signature,
long timestamp) throws IOException {
BdfList authorList = clientHelper.toList(author);
BdfDictionary props = clientHelper.toDictionary(properties);
BdfList payload = BdfList.of(authorList, props, signature, timestamp);
recordWriter.writeRecord(new Record(PROTOCOL_VERSION, CONTACT_INFO,
clientHelper.toByteArray(payload)));
LOG.info("Sent contact info");
}
private ContactInfo receiveContactInfo(RecordReader recordReader)
throws IOException {
w.writeLong(timestamp);
LOG.info("Sent timestamp");
}
private long receiveTimestamp(BdfReader r) throws IOException {
long timestamp = r.readLong();
Record record;
do {
record = recordReader.readRecord();
if (record.getProtocolVersion() != PROTOCOL_VERSION)
throw new FormatException();
} while (record.getRecordType() != CONTACT_INFO);
LOG.info("Received contact info");
BdfList payload = clientHelper.toList(record.getPayload());
checkSize(payload, 4);
Author author = clientHelper.parseAndValidateAuthor(payload.getList(0));
BdfDictionary props = payload.getDictionary(1);
Map<TransportId, TransportProperties> properties =
clientHelper.parseAndValidateTransportPropertiesMap(props);
byte[] signature = payload.getRaw(2);
checkLength(signature, 1, MAX_SIGNATURE_LENGTH);
long timestamp = payload.getLong(3);
if (timestamp < 0) throw new FormatException();
LOG.info("Received timestamp");
return timestamp;
}
private void sendTransportProperties(BdfWriter w,
Map<TransportId, TransportProperties> local) throws IOException {
w.writeListStart();
for (Entry<TransportId, TransportProperties> e : local.entrySet())
w.writeList(BdfList.of(e.getKey().getString(), e.getValue()));
w.writeListEnd();
}
private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote = new HashMap<>();
r.readListStart();
while (!r.hasListEnd()) {
r.readListStart();
String id = r.readString(MAX_TRANSPORT_ID_LENGTH);
if (id.isEmpty()) throw new FormatException();
TransportProperties p = new TransportProperties();
r.readDictionaryStart();
while (!r.hasDictionaryEnd()) {
if (p.size() == MAX_PROPERTIES_PER_TRANSPORT)
throw new FormatException();
String key = r.readString(MAX_PROPERTY_LENGTH);
String value = r.readString(MAX_PROPERTY_LENGTH);
p.put(key, value);
}
r.readDictionaryEnd();
r.readListEnd();
remote.put(new TransportId(id), p);
}
r.readListEnd();
return remote;
return new ContactInfo(author, properties, signature, timestamp);
}
private ContactId addContact(Author remoteAuthor, long timestamp,
@@ -324,13 +301,30 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return contactId;
}
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
private void tryToClose(DuplexTransportConnection conn) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(exception, true);
conn.getWriter().dispose(exception);
conn.getReader().dispose(true, true);
conn.getWriter().dispose(true);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private static class ContactInfo {
private final Author author;
private final Map<TransportId, TransportProperties> properties;
private final byte[] signature;
private final long timestamp;
private ContactInfo(Author author,
Map<TransportId, TransportProperties> properties,
byte[] signature, long timestamp) {
this.author = author;
this.properties = properties;
this.signature = signature;
this.timestamp = timestamp;
}
}
}

View File

@@ -46,7 +46,7 @@ class ContactManagerImpl implements ContactManager {
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice);
keyManager.addContact(txn, c, master, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;

View File

@@ -0,0 +1,67 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module
public class CryptoExecutorModule {
public static class EagerSingletons {
@Inject
@CryptoExecutor
ExecutorService cryptoExecutor;
}
/**
* The maximum number of executor threads.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_EXECUTOR_THREADS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
private final ExecutorService cryptoExecutor;
public CryptoExecutorModule() {
// Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Create a limited # of threads and keep them in the pool for 60 secs
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
}
@Provides
@Singleton
@CryptoExecutor
ExecutorService provideCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor provideCryptoExecutor() {
return cryptoExecutor;
}
}

View File

@@ -1,64 +1,24 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.security.SecureRandom;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import javax.inject.Inject;
import javax.inject.Provider;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.TimeUnit.SECONDS;
@Module
public class CryptoModule {
public static class EagerSingletons {
@Inject
@CryptoExecutor
ExecutorService cryptoExecutor;
}
/**
* The maximum number of executor threads.
* <p>
* The number of available processors can change during the lifetime of the
* JVM, so this is just a reasonable guess.
*/
private static final int MAX_EXECUTOR_THREADS =
Math.max(1, Runtime.getRuntime().availableProcessors() - 1);
private final ExecutorService cryptoExecutor;
public CryptoModule() {
// Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
// Create a limited # of threads and keep them in the pool for 60 secs
cryptoExecutor = new TimeLoggingExecutor("CryptoExecutor", 0,
MAX_EXECUTOR_THREADS, 60, SECONDS, queue, policy);
}
@Provides
AuthenticatedCipher provideAuthenticatedCipher() {
return new XSalsa20Poly1305AuthenticatedCipher();
@@ -103,21 +63,6 @@ public class CryptoModule {
return keyAgreementCrypto;
}
@Provides
@Singleton
@CryptoExecutor
ExecutorService getCryptoExecutorService(
LifecycleManager lifecycleManager) {
lifecycleManager.registerForShutdown(cryptoExecutor);
return cryptoExecutor;
}
@Provides
@CryptoExecutor
Executor getCryptoExecutor() {
return cryptoExecutor;
}
@Provides
SecureRandom getSecureRandom(CryptoComponent crypto) {
return crypto.getSecureRandom();

View File

@@ -152,59 +152,47 @@ public class MessageEncrypter {
}
}
public static void main(String[] args) throws Exception {
public static void main(String[] args) {
if (args.length < 1) {
printUsage();
return;
System.exit(1);
}
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
if (args[0].equals("generate")) {
if (args.length != 3) {
printUsage();
return;
System.exit(1);
}
try {
generateKeyPair(args[1], args[2]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
}
// Generate a key pair
KeyPair keyPair = encrypter.generateKeyPair();
PrintStream out = new PrintStream(new FileOutputStream(args[1]));
out.print(
StringUtils.toHexString(keyPair.getPublic().getEncoded()));
out.flush();
out.close();
out = new PrintStream(new FileOutputStream(args[2]));
out.print(
StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
out.flush();
out.close();
} else if (args[0].equals("encrypt")) {
if (args.length != 2) {
printUsage();
return;
System.exit(1);
}
try {
encryptMessage(args[1]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
}
// Encrypt a decrypted message
InputStream in = new FileInputStream(args[1]);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PublicKey publicKey =
encrypter.getKeyParser().parsePublicKey(keyBytes);
String message = readFully(System.in);
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
} else if (args[0].equals("decrypt")) {
if (args.length != 2) {
printUsage();
return;
System.exit(1);
}
try {
decryptMessage(args[1]);
} catch (Exception e) {
e.printStackTrace();
System.exit(2);
}
// Decrypt an encrypted message
InputStream in = new FileInputStream(args[1]);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PrivateKey privateKey =
encrypter.getKeyParser().parsePrivateKey(keyBytes);
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
} else {
printUsage();
System.exit(1);
}
}
@@ -216,6 +204,46 @@ public class MessageEncrypter {
System.err.println("MessageEncrypter decrypt <private_key_file>");
}
private static void generateKeyPair(String publicKeyFile,
String privateKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
KeyPair keyPair = encrypter.generateKeyPair();
PrintStream out = new PrintStream(new FileOutputStream(publicKeyFile));
out.print(StringUtils.toHexString(keyPair.getPublic().getEncoded()));
out.flush();
out.close();
out = new PrintStream(new FileOutputStream(privateKeyFile));
out.print(StringUtils.toHexString(keyPair.getPrivate().getEncoded()));
out.flush();
out.close();
}
private static void encryptMessage(String publicKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
InputStream in = new FileInputStream(publicKeyFile);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PublicKey publicKey =
encrypter.getKeyParser().parsePublicKey(keyBytes);
String message = readFully(System.in);
byte[] plaintext = message.getBytes(Charset.forName("UTF-8"));
byte[] ciphertext = encrypter.encrypt(publicKey, plaintext);
System.out.println(AsciiArmour.wrap(ciphertext, LINE_LENGTH));
}
private static void decryptMessage(String privateKeyFile) throws Exception {
SecureRandom random = new SecureRandom();
MessageEncrypter encrypter = new MessageEncrypter(random);
InputStream in = new FileInputStream(privateKeyFile);
byte[] keyBytes = StringUtils.fromHexString(readFully(in).trim());
PrivateKey privateKey =
encrypter.getKeyParser().parsePrivateKey(keyBytes);
byte[] ciphertext = AsciiArmour.unwrap(readFully(System.in));
byte[] plaintext = encrypter.decrypt(privateKey, ciphertext);
System.out.println(new String(plaintext, Charset.forName("UTF-8")));
}
private static String readFully(InputStream in) throws IOException {
String newline = System.getProperty("line.separator");
StringBuilder stringBuilder = new StringBuilder();

View File

@@ -10,37 +10,37 @@ import java.security.GeneralSecurityException;
interface Signature {
/**
* @see {@link java.security.Signature#initSign(java.security.PrivateKey)}
* @see java.security.Signature#initSign(java.security.PrivateKey)
*/
void initSign(PrivateKey k) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#initVerify(java.security.PublicKey)}
* @see java.security.Signature#initVerify(java.security.PublicKey)
*/
void initVerify(PublicKey k) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte)}
* @see java.security.Signature#update(byte)
*/
void update(byte b) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte[])}
* @see java.security.Signature#update(byte[])
*/
void update(byte[] b) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#update(byte[], int, int)}
* @see java.security.Signature#update(byte[], int, int)
*/
void update(byte[] b, int off, int len) throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#sign()}
* @see java.security.Signature#sign()}
*/
byte[] sign() throws GeneralSecurityException;
/**
* @see {@link java.security.Signature#verify(byte[])}
* @see java.security.Signature#verify(byte[])
*/
boolean verify(byte[] signature) throws GeneralSecurityException;
}

View File

@@ -8,6 +8,7 @@ import java.io.InputStream;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_MAX_BUFFER_SIZE;
import static org.briarproject.bramble.api.data.BdfReader.DEFAULT_NESTED_LIMIT;
@Immutable
@@ -16,11 +17,13 @@ class BdfReaderFactoryImpl implements BdfReaderFactory {
@Override
public BdfReader createReader(InputStream in) {
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT);
return new BdfReaderImpl(in, DEFAULT_NESTED_LIMIT,
DEFAULT_MAX_BUFFER_SIZE);
}
@Override
public BdfReader createReader(InputStream in, int nestedLimit) {
return new BdfReaderImpl(in, nestedLimit);
public BdfReader createReader(InputStream in, int nestedLimit,
int maxBufferSize) {
return new BdfReaderImpl(in, nestedLimit, maxBufferSize);
}
}

View File

@@ -37,15 +37,16 @@ class BdfReaderImpl implements BdfReader {
private static final byte[] EMPTY_BUFFER = new byte[0];
private final InputStream in;
private final int nestedLimit;
private final int nestedLimit, maxBufferSize;
private boolean hasLookahead = false, eof = false;
private byte next;
private byte[] buf = new byte[8];
BdfReaderImpl(InputStream in, int nestedLimit) {
BdfReaderImpl(InputStream in, int nestedLimit, int maxBufferSize) {
this.in = in;
this.nestedLimit = nestedLimit;
this.maxBufferSize = maxBufferSize;
}
private void readLookahead() throws IOException {
@@ -91,8 +92,8 @@ class BdfReaderImpl implements BdfReader {
if (hasBoolean()) return readBoolean();
if (hasLong()) return readLong();
if (hasDouble()) return readDouble();
if (hasString()) return readString(Integer.MAX_VALUE);
if (hasRaw()) return readRaw(Integer.MAX_VALUE);
if (hasString()) return readString();
if (hasRaw()) return readRaw();
if (hasList()) return readList(level);
if (hasDictionary()) return readDictionary(level);
throw new FormatException();
@@ -245,11 +246,11 @@ class BdfReaderImpl implements BdfReader {
}
@Override
public String readString(int maxLength) throws IOException {
public String readString() throws IOException {
if (!hasString()) throw new FormatException();
hasLookahead = false;
int length = readStringLength();
if (length < 0 || length > maxLength) throw new FormatException();
if (length < 0 || length > maxBufferSize) throw new FormatException();
if (length == 0) return "";
readIntoBuffer(length);
return new String(buf, 0, length, "UTF-8");
@@ -279,11 +280,11 @@ class BdfReaderImpl implements BdfReader {
}
@Override
public byte[] readRaw(int maxLength) throws IOException {
public byte[] readRaw() throws IOException {
if (!hasRaw()) throw new FormatException();
hasLookahead = false;
int length = readRawLength();
if (length < 0 || length > maxLength) throw new FormatException();
if (length < 0 || length > maxBufferSize) throw new FormatException();
if (length == 0) return EMPTY_BUFFER;
byte[] b = new byte[length];
readIntoBuffer(b, length);
@@ -381,7 +382,7 @@ class BdfReaderImpl implements BdfReader {
BdfDictionary dictionary = new BdfDictionary();
readDictionaryStart();
while (!hasDictionaryEnd())
dictionary.put(readString(Integer.MAX_VALUE), readObject(level + 1));
dictionary.put(readString(), readObject(level + 1));
readDictionaryEnd();
return dictionary;
}

View File

@@ -59,8 +59,8 @@ class MetadataParserImpl implements MetadataParser {
if (reader.hasBoolean()) return reader.readBoolean();
if (reader.hasLong()) return reader.readLong();
if (reader.hasDouble()) return reader.readDouble();
if (reader.hasString()) return reader.readString(Integer.MAX_VALUE);
if (reader.hasRaw()) return reader.readRaw(Integer.MAX_VALUE);
if (reader.hasString()) return reader.readString();
if (reader.hasRaw()) return reader.readRaw();
if (reader.hasList()) return reader.readList();
if (reader.hasDictionary()) return reader.readDictionary();
throw new FormatException();

View File

@@ -34,8 +34,8 @@ import javax.annotation.Nullable;
* A low-level interface to the database (DatabaseComponent provides a
* high-level interface). Most operations take a transaction argument, which is
* obtained by calling {@link #startTransaction()}. Every transaction must be
* terminated by calling either {@link #abortTransaction(T)} or
* {@link #commitTransaction(T)}, even if an exception is thrown.
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
*/
@NotNullByDefault
interface Database<T> {
@@ -125,16 +125,10 @@ interface Database<T> {
throws DbException;
/**
* Stores the given transport keys, optionally binding them to the given
* contact, and returns a key set ID.
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
KeySetId addTransportKeys(T txn, @Nullable ContactId c, TransportKeys k)
throws DbException;
/**
* Binds the given keys for the given transport to the given contact.
*/
void bindTransportKeys(T txn, ContactId c, TransportId t, KeySetId k)
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
@@ -266,7 +260,8 @@ interface Database<T> {
* <p/>
* Read-only.
*/
Collection<Group> getGroups(T txn, ClientId c) throws DbException;
Collection<Group> getGroups(T txn, ClientId c, int majorVersion)
throws DbException;
/**
* Returns the given group's visibility to the given contact, or
@@ -321,16 +316,16 @@ interface Database<T> {
throws DbException;
/**
* Returns the IDs of all messages in the given group.
* Returns the IDs of all delivered messages in the given group.
* <p/>
* Read-only.
*/
Collection<MessageId> getMessageIds(T txn, GroupId g) throws DbException;
/**
* Returns the IDs of any messages in the given group with metadata
* matching all entries in the given query. If the query is empty, the IDs
* of all messages are returned.
* Returns the IDs of any delivered messages in the given group with
* metadata that matches all entries in the given query. If the query is
* empty, the IDs of all delivered messages are returned.
* <p/>
* Read-only.
*/
@@ -346,9 +341,9 @@ interface Database<T> {
throws DbException;
/**
* Returns the metadata for any messages in the given group with metadata
* matching all entries in the given query. If the query is empty, the
* metadata for all messages is returned.
* Returns the metadata for any delivered messages in the given group with
* metadata that matches all entries in the given query. If the query is
* empty, the metadata for all delivered messages is returned.
* <p/>
* Read-only.
*/
@@ -356,7 +351,8 @@ interface Database<T> {
Metadata query) throws DbException;
/**
* Returns the metadata for the given delivered message.
* Returns the metadata for the given delivered or pending message.
* This is only meant to be used by the ValidationManager.
* <p/>
* Read-only.
*/
@@ -364,7 +360,7 @@ interface Database<T> {
throws DbException;
/**
* Returns the metadata for the given message.
* Returns the metadata for the given delivered message.
* <p/>
* Read-only.
*/
@@ -378,8 +374,8 @@ interface Database<T> {
State getMessageState(T txn, MessageId m) throws DbException;
/**
* Returns the status of all messages in the given group with respect
* to the given contact.
* Returns the status of all delivered messages in the given group with
* respect to the given contact.
* <p/>
* Read-only.
*/
@@ -387,11 +383,13 @@ interface Database<T> {
throws DbException;
/**
* Returns the status of the given message with respect to the given
* Returns the status of the given delivered message with respect to the
* given contact, or null if the message's group is invisible to the
* contact.
* <p/>
* Read-only.
*/
@Nullable
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
throws DbException;
@@ -654,7 +652,7 @@ interface Database<T> {
throws DbException;
/**
* Stores the given transport keys, deleting any keys they have replaced.
* Updates the given transport keys following key rotation.
*/
void updateTransportKeys(T txn, Collection<KeySet> keys) throws DbException;
void updateTransportKeys(T txn, KeySet ks) throws DbException;
}

View File

@@ -234,27 +234,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public KeySetId addTransportKeys(Transaction transaction,
@Nullable ContactId c, TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (c != null && !db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addTransportKeys(txn, c, k);
}
@Override
public void bindTransportKeys(Transaction transaction, ContactId c,
TransportId t, KeySetId k) throws DbException {
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, t))
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
db.bindTransportKeys(txn, c, t, k);
return db.addTransportKeys(txn, c, k);
}
@Override
@@ -435,10 +423,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public Collection<Group> getGroups(Transaction transaction, ClientId c)
throws DbException {
public Collection<Group> getGroups(Transaction transaction, ClientId c,
int majorVersion) throws DbException {
T txn = unbox(transaction);
return db.getGroups(txn, c);
return db.getGroups(txn, c, majorVersion);
}
@Override
@@ -466,6 +454,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getLocalAuthors(txn);
}
@Override
public Collection<MessageId> getMessageIds(Transaction transaction,
GroupId g) throws DbException {
T txn = unbox(transaction);
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
return db.getMessageIds(txn, g);
}
@Override
public Collection<MessageId> getMessagesToValidate(Transaction transaction)
throws DbException {
@@ -551,6 +548,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchContactException();
if (!db.containsGroup(txn, g))
throw new NoSuchGroupException();
if (db.getGroupVisibility(txn, c, g) == INVISIBLE) {
// No status rows exist - return default statuses
Collection<MessageStatus> statuses = new ArrayList<>();
for (MessageId m : db.getMessageIds(txn, g))
statuses.add(new MessageStatus(m, c, false, false));
return statuses;
}
return db.getMessageStatus(txn, c, g);
}
@@ -562,7 +566,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchContactException();
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
return db.getMessageStatus(txn, c, m);
MessageStatus status = db.getMessageStatus(txn, c, m);
if (status == null) return new MessageStatus(m, c, false, false);
return status;
}
@Override
@@ -903,11 +909,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
Collection<KeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
Collection<KeySet> filtered = new ArrayList<>();
for (KeySet ks : keys) {
TransportId t = ks.getTransportKeys().getTransportId();
if (db.containsTransport(txn, t)) filtered.add(ks);
if (db.containsTransport(txn, t)) db.updateTransportKeys(txn, ks);
}
db.updateTransportKeys(txn, filtered);
}
}

View File

@@ -53,6 +53,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.sql.Types.INTEGER;
import static java.util.Collections.singletonList;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
@@ -74,7 +75,12 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
static final int CODE_SCHEMA_VERSION = 36;
static final int CODE_SCHEMA_VERSION = 39;
// Rotation period offsets for incoming transport keys
private static final int OFFSET_PREV = -1;
private static final int OFFSET_CURR = 0;
private static final int OFFSET_NEXT = 1;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"
@@ -112,6 +118,7 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE TABLE groups"
+ " (groupId _HASH NOT NULL,"
+ " clientId _STRING NOT NULL,"
+ " majorVersion INT NOT NULL,"
+ " descriptor _BINARY NOT NULL,"
+ " PRIMARY KEY (groupId))";
@@ -230,7 +237,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER,"
+ " rotationPeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if keys are not bound
+ " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL,"
@@ -249,12 +256,13 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL,"
+ " rotationPeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if keys are not bound
+ " contactId INT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL,"
+ " bitmap _BINARY NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId, rotationPeriod),"
+ " periodOffset INT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
@@ -269,9 +277,9 @@ abstract class JdbcDatabase implements Database<Connection> {
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID =
"CREATE INDEX IF NOT EXISTS groupsByClientId"
+ " ON groups (clientId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID_MAJOR_VERSION =
"CREATE INDEX IF NOT EXISTS groupsByClientIdMajorVersion"
+ " ON groups (clientId, majorVersion)";
private static final String INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE =
"CREATE INDEX IF NOT EXISTS messageMetadataByGroupIdState"
@@ -382,7 +390,7 @@ abstract class JdbcDatabase implements Database<Connection> {
// Package access for testing
List<Migration<Connection>> getMigrations() {
return Collections.emptyList();
return singletonList(new Migration38_39());
}
private void storeSchemaVersion(Connection txn, int version)
@@ -438,7 +446,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try {
s = txn.createStatement();
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID_MAJOR_VERSION);
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_GROUP_ID_STATE);
s.executeUpdate(INDEX_MESSAGE_DEPENDENCIES_BY_DEPENDENCY_ID);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT_ID_GROUP_ID);
@@ -606,12 +614,14 @@ abstract class JdbcDatabase implements Database<Connection> {
public void addGroup(Connection txn, Group g) throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO groups (groupId, clientId, descriptor)"
+ " VALUES (?, ?, ?)";
String sql = "INSERT INTO groups"
+ " (groupId, clientId, majorVersion, descriptor)"
+ " VALUES (?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getId().getBytes());
ps.setString(2, g.getClientId().getString());
ps.setBytes(3, g.getDescriptor());
ps.setInt(3, g.getMajorVersion());
ps.setBytes(4, g.getDescriptor());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -874,7 +884,7 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public KeySetId addTransportKeys(Connection txn, @Nullable ContactId c,
public KeySetId addTransportKeys(Connection txn, ContactId c,
TransportKeys k) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
@@ -884,8 +894,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " rotationPeriod, tagKey, headerKey, stream, active)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
if (c == null) ps.setNull(1, INTEGER);
else ps.setInt(1, c.getInt());
ps.setInt(1, c.getInt());
ps.setString(2, k.getTransportId().getString());
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(3, outCurr.getRotationPeriod());
@@ -908,12 +917,12 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.close();
// Store the incoming keys
sql = "INSERT INTO incomingKeys (keySetId, contactId, transportId,"
+ " rotationPeriod, tagKey, headerKey, base, bitmap)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ " rotationPeriod, tagKey, headerKey, base, bitmap,"
+ " periodOffset)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setInt(1, keySetId.getInt());
if (c == null) ps.setNull(2, INTEGER);
else ps.setInt(2, c.getInt());
ps.setInt(2, c.getInt());
ps.setString(3, k.getTransportId().getString());
// Previous rotation period
IncomingKeys inPrev = k.getPreviousIncomingKeys();
@@ -922,6 +931,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(6, inPrev.getHeaderKey().getBytes());
ps.setLong(7, inPrev.getWindowBase());
ps.setBytes(8, inPrev.getWindowBitmap());
ps.setInt(9, OFFSET_PREV);
ps.addBatch();
// Current rotation period
IncomingKeys inCurr = k.getCurrentIncomingKeys();
@@ -930,6 +940,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(6, inCurr.getHeaderKey().getBytes());
ps.setLong(7, inCurr.getWindowBase());
ps.setBytes(8, inCurr.getWindowBitmap());
ps.setInt(9, OFFSET_CURR);
ps.addBatch();
// Next rotation period
IncomingKeys inNext = k.getNextIncomingKeys();
@@ -938,6 +949,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(6, inNext.getHeaderKey().getBytes());
ps.setLong(7, inNext.getWindowBase());
ps.setBytes(8, inNext.getWindowBitmap());
ps.setInt(9, OFFSET_NEXT);
ps.addBatch();
int[] batchAffected = ps.executeBatch();
if (batchAffected.length != 3) throw new DbStateException();
@@ -952,33 +964,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
@Override
public void bindTransportKeys(Connection txn, ContactId c, TransportId t,
KeySetId k) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE outgoingKeys SET contactId = ?"
+ " WHERE keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, k.getInt());
int affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
sql = "UPDATE incomingKeys SET contactId = ?"
+ " WHERE keySetId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setInt(2, k.getInt());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
@Override
public boolean containsContact(Connection txn, AuthorId remote,
AuthorId local) throws DbException {
@@ -1336,17 +1321,18 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT clientId, descriptor FROM groups"
+ " WHERE groupId = ?";
String sql = "SELECT clientId, majorVersion, descriptor"
+ " FROM groups WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
ClientId clientId = new ClientId(rs.getString(1));
byte[] descriptor = rs.getBytes(2);
int majorVersion = rs.getInt(2);
byte[] descriptor = rs.getBytes(3);
rs.close();
ps.close();
return new Group(g, clientId, descriptor);
return new Group(g, clientId, majorVersion, descriptor);
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -1355,21 +1341,22 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public Collection<Group> getGroups(Connection txn, ClientId c)
throws DbException {
public Collection<Group> getGroups(Connection txn, ClientId c,
int majorVersion) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT groupId, descriptor FROM groups"
+ " WHERE clientId = ?";
+ " WHERE clientId = ? AND majorVersion = ?";
ps = txn.prepareStatement(sql);
ps.setString(1, c.getString());
ps.setInt(2, majorVersion);
rs = ps.executeQuery();
List<Group> groups = new ArrayList<>();
while (rs.next()) {
GroupId id = new GroupId(rs.getBytes(1));
byte[] descriptor = rs.getBytes(2);
groups.add(new Group(id, c, descriptor));
groups.add(new Group(id, c, majorVersion, descriptor));
}
rs.close();
ps.close();
@@ -1501,32 +1488,11 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages WHERE groupId = ?";
String sql = "SELECT messageId FROM messages"
+ " WHERE groupId = ? AND state = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close();
ps.close();
return ids;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
private Collection<MessageId> getMessageIds(Connection txn, GroupId g,
State state) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT messageId FROM messages"
+ " WHERE state = ? AND groupId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, state.getValue());
ps.setBytes(2, g.getBytes());
ps.setInt(2, DELIVERED.getValue());
rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
@@ -1544,7 +1510,7 @@ abstract class JdbcDatabase implements Database<Connection> {
public Collection<MessageId> getMessageIds(Connection txn, GroupId g,
Metadata query) throws DbException {
// If there are no query terms, return all delivered messages
if (query.isEmpty()) return getMessageIds(txn, g, DELIVERED);
if (query.isEmpty()) return getMessageIds(txn, g);
PreparedStatement ps = null;
ResultSet rs = null;
try {
@@ -1703,10 +1669,11 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null;
try {
String sql = "SELECT messageId, txCount > 0, seen FROM statuses"
+ " WHERE groupId = ? AND contactId = ?";
+ " WHERE groupId = ? AND contactId = ? AND state = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
ps.setInt(2, c.getInt());
ps.setInt(3, DELIVERED.getValue());
rs = ps.executeQuery();
List<MessageStatus> statuses = new ArrayList<>();
while (rs.next()) {
@@ -1726,24 +1693,29 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
@Nullable
public MessageStatus getMessageStatus(Connection txn, ContactId c,
MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT txCount > 0, seen FROM statuses"
+ " WHERE messageId = ? AND contactId = ?";
+ " WHERE messageId = ? AND contactId = ? AND state = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
ps.setInt(3, DELIVERED.getValue());
rs = ps.executeQuery();
if (!rs.next()) throw new DbStateException();
boolean sent = rs.getBoolean(1);
boolean seen = rs.getBoolean(2);
MessageStatus status = null;
if (rs.next()) {
boolean sent = rs.getBoolean(1);
boolean seen = rs.getBoolean(2);
status = new MessageStatus(m, c, sent, seen);
}
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
return new MessageStatus(m, c, sent, seen);
return status;
} catch (SQLException e) {
tryToClose(rs);
tryToClose(ps);
@@ -2141,7 +2113,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " base, bitmap"
+ " FROM incomingKeys"
+ " WHERE transportId = ?"
+ " ORDER BY keySetId, rotationPeriod";
+ " ORDER BY keySetId, periodOffset";
ps = txn.prepareStatement(sql);
ps.setString(1, t.getString());
rs = ps.executeQuery();
@@ -2172,7 +2144,6 @@ abstract class JdbcDatabase implements Database<Connection> {
if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
KeySetId keySetId = new KeySetId(rs.getInt(1));
ContactId contactId = new ContactId(rs.getInt(2));
if (rs.wasNull()) contactId = null;
long rotationPeriod = rs.getLong(3);
SecretKey tagKey = new SecretKey(rs.getBytes(4));
SecretKey headerKey = new SecretKey(rs.getBytes(5));
@@ -2563,7 +2534,14 @@ abstract class JdbcDatabase implements Database<Connection> {
if (affected != 1) throw new DbStateException();
ps.close();
// Remove status rows for the messages in the group
for (MessageId m : getMessageIds(txn, g)) removeStatus(txn, c, m);
sql = "DELETE FROM statuses"
+ " WHERE contactId = ? AND groupId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt());
ps.setBytes(2, g.getBytes());
affected = ps.executeUpdate();
if (affected < 0) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
@@ -2647,24 +2625,6 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
private void removeStatus(Connection txn, ContactId c, MessageId m)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM statuses"
+ " WHERE messageId = ? AND contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
@Override
public void removeTransport(Connection txn, TransportId t)
throws DbException {
@@ -2944,12 +2904,69 @@ abstract class JdbcDatabase implements Database<Connection> {
}
@Override
public void updateTransportKeys(Connection txn, Collection<KeySet> keys)
public void updateTransportKeys(Connection txn, KeySet ks)
throws DbException {
for (KeySet ks : keys) {
PreparedStatement ps = null;
try {
// Update the outgoing keys
String sql = "UPDATE outgoingKeys SET rotationPeriod = ?,"
+ " tagKey = ?, headerKey = ?, stream = ?"
+ " WHERE transportId = ? AND keySetId = ?";
ps = txn.prepareStatement(sql);
TransportKeys k = ks.getTransportKeys();
removeTransportKeys(txn, k.getTransportId(), ks.getKeySetId());
addTransportKeys(txn, ks.getContactId(), k);
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
ps.setLong(1, outCurr.getRotationPeriod());
ps.setBytes(2, outCurr.getTagKey().getBytes());
ps.setBytes(3, outCurr.getHeaderKey().getBytes());
ps.setLong(4, outCurr.getStreamCounter());
ps.setString(5, k.getTransportId().getString());
ps.setInt(6, ks.getKeySetId().getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
// Update the incoming keys
sql = "UPDATE incomingKeys SET rotationPeriod = ?,"
+ " tagKey = ?, headerKey = ?, base = ?, bitmap = ?"
+ " WHERE transportId = ? AND keySetId = ?"
+ " AND periodOffset = ?";
ps = txn.prepareStatement(sql);
ps.setString(6, k.getTransportId().getString());
ps.setInt(7, ks.getKeySetId().getInt());
// Previous rotation period
IncomingKeys inPrev = k.getPreviousIncomingKeys();
ps.setLong(1, inPrev.getRotationPeriod());
ps.setBytes(2, inPrev.getTagKey().getBytes());
ps.setBytes(3, inPrev.getHeaderKey().getBytes());
ps.setLong(4, inPrev.getWindowBase());
ps.setBytes(5, inPrev.getWindowBitmap());
ps.setInt(8, OFFSET_PREV);
ps.addBatch();
// Current rotation period
IncomingKeys inCurr = k.getCurrentIncomingKeys();
ps.setLong(1, inCurr.getRotationPeriod());
ps.setBytes(2, inCurr.getTagKey().getBytes());
ps.setBytes(3, inCurr.getHeaderKey().getBytes());
ps.setLong(4, inCurr.getWindowBase());
ps.setBytes(5, inCurr.getWindowBitmap());
ps.setInt(8, OFFSET_CURR);
ps.addBatch();
// Next rotation period
IncomingKeys inNext = k.getNextIncomingKeys();
ps.setLong(1, inNext.getRotationPeriod());
ps.setBytes(2, inNext.getTagKey().getBytes());
ps.setBytes(3, inNext.getHeaderKey().getBytes());
ps.setLong(4, inNext.getWindowBase());
ps.setBytes(5, inNext.getWindowBitmap());
ps.setInt(8, OFFSET_NEXT);
ps.addBatch();
int[] batchAffected = ps.executeBatch();
if (batchAffected.length != 3) throw new DbStateException();
for (int rows : batchAffected)
if (rows < 0 || rows > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
}

View File

@@ -0,0 +1,54 @@
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 javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
class Migration38_39 implements Migration<Connection> {
private static final Logger LOG =
Logger.getLogger(Migration38_39.class.getName());
@Override
public int getStartVersion() {
return 38;
}
@Override
public int getEndVersion() {
return 39;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
// Add not null constraints
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
s.execute("ALTER TABLE incomingKeys"
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -13,6 +13,8 @@ import org.briarproject.bramble.api.plugin.PluginManager;
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.record.RecordReaderFactory;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -44,6 +46,8 @@ class KeyAgreementConnector {
private final KeyAgreementCrypto keyAgreementCrypto;
private final PluginManager pluginManager;
private final ConnectionChooser connectionChooser;
private final RecordReaderFactory recordReaderFactory;
private final RecordWriterFactory recordWriterFactory;
private final List<KeyAgreementListener> listeners =
new CopyOnWriteArrayList<>();
@@ -54,11 +58,15 @@ class KeyAgreementConnector {
KeyAgreementConnector(Callbacks callbacks,
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager,
ConnectionChooser connectionChooser) {
ConnectionChooser connectionChooser,
RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory) {
this.callbacks = callbacks;
this.keyAgreementCrypto = keyAgreementCrypto;
this.pluginManager = pluginManager;
this.connectionChooser = connectionChooser;
this.recordReaderFactory = recordReaderFactory;
this.recordWriterFactory = recordWriterFactory;
}
Payload listen(KeyPair localKeyPair) {
@@ -119,7 +127,8 @@ class KeyAgreementConnector {
KeyAgreementConnection chosen =
connectionChooser.poll(CONNECTION_TIMEOUT);
if (chosen == null) return null;
return new KeyAgreementTransport(chosen);
return new KeyAgreementTransport(recordReaderFactory,
recordWriterFactory, chosen);
} catch (InterruptedException e) {
LOG.info("Interrupted while waiting for connection");
Thread.currentThread().interrupt();

View File

@@ -14,10 +14,13 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.record.RecordReaderFactory;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import java.io.IOException;
import java.util.logging.Logger;
@@ -48,14 +51,17 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
KeyAgreementTaskImpl(CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
PayloadEncoder payloadEncoder, PluginManager pluginManager,
ConnectionChooser connectionChooser) {
ConnectionChooser connectionChooser,
RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory) {
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.eventBus = eventBus;
this.payloadEncoder = payloadEncoder;
localKeyPair = crypto.generateAgreementKeyPair();
connector = new KeyAgreementConnector(this, keyAgreementCrypto,
pluginManager, connectionChooser);
pluginManager, connectionChooser, recordReaderFactory,
recordWriterFactory);
}
@Override
@@ -71,6 +77,7 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
if (localPayload != null) {
if (remotePayload == null) connector.stopListening();
else interrupt();
eventBus.broadcast(new KeyAgreementStoppedListeningEvent());
}
}

View File

@@ -4,9 +4,12 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.api.record.Record;
import org.briarproject.bramble.api.record.RecordReader;
import org.briarproject.bramble.api.record.RecordReaderFactory;
import org.briarproject.bramble.api.record.RecordWriter;
import org.briarproject.bramble.api.record.RecordWriterFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -14,8 +17,6 @@ import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.RECORD_HEADER_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.RECORD_HEADER_PAYLOAD_LENGTH_OFFSET;
import static org.briarproject.bramble.api.keyagreement.RecordTypes.ABORT;
import static org.briarproject.bramble.api.keyagreement.RecordTypes.CONFIRM;
import static org.briarproject.bramble.api.keyagreement.RecordTypes.KEY;
@@ -30,14 +31,17 @@ class KeyAgreementTransport {
Logger.getLogger(KeyAgreementTransport.class.getName());
private final KeyAgreementConnection kac;
private final InputStream in;
private final OutputStream out;
private final RecordReader reader;
private final RecordWriter writer;
KeyAgreementTransport(KeyAgreementConnection kac)
KeyAgreementTransport(RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory, KeyAgreementConnection kac)
throws IOException {
this.kac = kac;
in = kac.getConnection().getReader().getInputStream();
out = kac.getConnection().getWriter().getOutputStream();
InputStream in = kac.getConnection().getReader().getInputStream();
reader = recordReaderFactory.createRecordReader(in);
OutputStream out = kac.getConnection().getWriter().getOutputStream();
writer = recordWriterFactory.createRecordWriter(out);
}
public DuplexTransportConnection getConnection() {
@@ -74,9 +78,8 @@ class KeyAgreementTransport {
tryToClose(exception);
}
public void tryToClose(boolean exception) {
private void tryToClose(boolean exception) {
try {
LOG.info("Closing connection");
kac.getConnection().getReader().dispose(exception, true);
kac.getConnection().getWriter().dispose(exception);
} catch (IOException e) {
@@ -85,59 +88,27 @@ class KeyAgreementTransport {
}
private void writeRecord(byte type, byte[] payload) throws IOException {
byte[] recordHeader = new byte[RECORD_HEADER_LENGTH];
recordHeader[0] = PROTOCOL_VERSION;
recordHeader[1] = type;
ByteUtils.writeUint16(payload.length, recordHeader,
RECORD_HEADER_PAYLOAD_LENGTH_OFFSET);
out.write(recordHeader);
out.write(payload);
out.flush();
writer.writeRecord(new Record(PROTOCOL_VERSION, type, payload));
writer.flush();
}
private byte[] readRecord(byte expectedType) throws AbortException {
while (true) {
byte[] header = readHeader();
byte version = header[0], type = header[1];
int len = ByteUtils.readUint16(header,
RECORD_HEADER_PAYLOAD_LENGTH_OFFSET);
// Reject unrecognised protocol version
if (version != PROTOCOL_VERSION) throw new AbortException(false);
if (type == ABORT) throw new AbortException(true);
if (type == expectedType) {
try {
return readData(len);
} catch (IOException e) {
throw new AbortException(e);
}
}
// Reject recognised but unexpected record type
if (type == KEY || type == CONFIRM) throw new AbortException(false);
// Skip unrecognised record type
try {
readData(len);
Record record = reader.readRecord();
// Reject unrecognised protocol version
if (record.getProtocolVersion() != PROTOCOL_VERSION)
throw new AbortException(false);
byte type = record.getRecordType();
if (type == ABORT) throw new AbortException(true);
if (type == expectedType) return record.getPayload();
// Reject recognised but unexpected record type
if (type == KEY || type == CONFIRM)
throw new AbortException(false);
// Skip unrecognised record type
} catch (IOException e) {
throw new AbortException(e);
}
}
}
private byte[] readHeader() throws AbortException {
try {
return readData(RECORD_HEADER_LENGTH);
} catch (IOException e) {
throw new AbortException(e);
}
}
private byte[] readData(int len) throws IOException {
byte[] data = new byte[len];
int offset = 0;
while (offset < data.length) {
int read = in.read(data, offset, data.length - offset);
if (read == -1) throw new EOFException();
offset += read;
}
return data;
}
}

View File

@@ -14,12 +14,12 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createSimplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createSimplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), streamWriter);
@@ -109,7 +109,7 @@ class ConnectionManagerImpl implements ConnectionManager {
private SyncSession createDuplexOutgoingSession(StreamContext ctx,
TransportConnectionWriter w) throws IOException {
OutputStream streamWriter = streamWriterFactory.createStreamWriter(
StreamWriter streamWriter = streamWriterFactory.createStreamWriter(
w.getOutputStream(), ctx);
return syncSessionFactory.createDuplexOutgoingSession(
ctx.getContactId(), w.getMaxLatency(), w.getMaxIdleTime(),
@@ -300,8 +300,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null)
outgoingSession.interrupt();
// Interrupt the outgoing session so it finishes cleanly
if (outgoingSession != null) outgoingSession.interrupt();
try {
reader.dispose(exception, recognised);
} catch (IOException e) {
@@ -310,6 +310,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null)
incomingSession.interrupt();
try {
@@ -407,8 +409,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeReader(boolean exception, boolean recognised) {
if (exception && outgoingSession != null)
outgoingSession.interrupt();
// Interrupt the outgoing session so it finishes cleanly
if (outgoingSession != null) outgoingSession.interrupt();
try {
reader.dispose(exception, recognised);
} catch (IOException e) {
@@ -417,6 +419,8 @@ class ConnectionManagerImpl implements ConnectionManager {
}
private void disposeWriter(boolean exception) {
// Interrupt the incoming session if an exception occurred,
// otherwise wait for the end of stream marker
if (exception && incomingSession != null)
incomingSession.interrupt();
try {

View File

@@ -106,7 +106,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet());
if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected");
LOG.info(ids.size() + " contacts connected: " + t);
return ids;
} finally {
lock.unlock();

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.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.PluginCallback;
import org.briarproject.bramble.api.plugin.PluginConfig;
@@ -29,17 +30,19 @@ import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.bramble.api.ui.UiCallback;
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.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
@@ -57,12 +60,15 @@ class PluginManagerImpl implements PluginManager, Service {
Logger.getLogger(PluginManagerImpl.class.getName());
private final Executor ioExecutor;
private final ScheduledExecutorService scheduler;
private final EventBus eventBus;
private final PluginConfig pluginConfig;
private final ConnectionManager connectionManager;
private final ConnectionRegistry connectionRegistry;
private final SettingsManager settingsManager;
private final TransportPropertyManager transportPropertyManager;
private final UiCallback uiCallback;
private final SecureRandom random;
private final Clock clock;
private final Map<TransportId, Plugin> plugins;
private final List<SimplexPlugin> simplexPlugins;
private final List<DuplexPlugin> duplexPlugins;
@@ -70,27 +76,41 @@ class PluginManagerImpl implements PluginManager, Service {
private final AtomicBoolean used = new AtomicBoolean(false);
@Inject
PluginManagerImpl(@IoExecutor Executor ioExecutor, EventBus eventBus,
PluginManagerImpl(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler, EventBus eventBus,
PluginConfig pluginConfig, ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry,
SettingsManager settingsManager,
TransportPropertyManager transportPropertyManager,
UiCallback uiCallback) {
SecureRandom random, Clock clock) {
this.ioExecutor = ioExecutor;
this.scheduler = scheduler;
this.eventBus = eventBus;
this.pluginConfig = pluginConfig;
this.connectionManager = connectionManager;
this.connectionRegistry = connectionRegistry;
this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback;
this.random = random;
this.clock = clock;
plugins = new ConcurrentHashMap<>();
simplexPlugins = new CopyOnWriteArrayList<>();
duplexPlugins = new CopyOnWriteArrayList<>();
startLatches = new ConcurrentHashMap<>();
}
@Override
public void startService() throws ServiceException {
public void startService() {
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
LOG.info("Starting simplex plugins");
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) {
@@ -273,26 +293,6 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() {
try {
return transportPropertyManager.getRemoteProperties(id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return Collections.emptyMap();
}
}
@Override
public TransportProperties getRemoteProperties(ContactId c) {
try {
return transportPropertyManager.getRemoteProperties(c, id);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return new TransportProperties();
}
}
@Override
public void mergeSettings(Settings s) {
try {
@@ -311,21 +311,6 @@ class PluginManagerImpl implements PluginManager, Service {
}
}
@Override
public int showChoice(String[] options, String... message) {
return uiCallback.showChoice(options, message);
}
@Override
public boolean showConfirmationMessage(String... message) {
return uiCallback.showConfirmationMessage(message);
}
@Override
public void showMessage(String... message) {
uiCallback.showMessage(message);
}
@Override
public void transportEnabled() {
eventBus.broadcast(new TransportEnabledEvent(id));

View File

@@ -1,18 +1,10 @@
package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.ConnectionManager;
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler;
import java.security.SecureRandom;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -26,8 +18,6 @@ public class PluginModule {
public static class EagerSingletons {
@Inject
PluginManager pluginManager;
@Inject
Poller poller;
}
@Provides
@@ -35,19 +25,6 @@ public class PluginModule {
return new BackoffFactoryImpl();
}
@Provides
@Singleton
Poller providePoller(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
ConnectionManager connectionManager,
ConnectionRegistry connectionRegistry, PluginManager pluginManager,
SecureRandom random, Clock clock, EventBus eventBus) {
Poller poller = new Poller(ioExecutor, scheduler, connectionManager,
connectionRegistry, pluginManager, random, clock);
eventBus.addListener(poller);
return poller;
}
@Provides
@Singleton
ConnectionManager provideConnectionManager(

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
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;
@@ -19,10 +20,13 @@ 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;
@@ -33,10 +37,10 @@ import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
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;
@ThreadSafe
@NotNullByDefault
@@ -49,22 +53,24 @@ class Poller implements EventListener {
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
@Inject
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();
@@ -120,10 +126,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, SimplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
TransportConnectionWriter w = p.createWriter(c);
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) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
});
}
@@ -131,10 +142,15 @@ class Poller implements EventListener {
private void connectToContact(ContactId c, DuplexPlugin p) {
ioExecutor.execute(() -> {
TransportId t = p.getId();
if (!connectionRegistry.isConnected(c, t)) {
DuplexTransportConnection d = p.createConnection(c);
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) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
});
}
@@ -186,7 +202,17 @@ class Poller implements EventListener {
private void poll(Plugin p) {
TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
p.poll(connectionRegistry.getConnectedContacts(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) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
private class ScheduledPollTask {

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault
interface BluetoothConnectionLimiter {
/**
* Informs the limiter that key agreement has started.
*/
void keyAgreementStarted();
/**
* Informs the limiter that key agreement has ended.
*/
void keyAgreementEnded();
/**
* Returns true if a contact connection can be opened. This method does not
* need to be called for key agreement connections.
*/
boolean canOpenContactConnection();
/**
* Informs the limiter that a contact connection has been opened. The
* limiter may close the new connection if key agreement is in progress.
* <p/>
* Returns false if the limiter has closed the new connection.
*/
boolean contactConnectionOpened(DuplexTransportConnection conn);
/**
* Informs the limiter that a key agreement connection has been opened.
*/
void keyAgreementConnectionOpened(DuplexTransportConnection conn);
/**
* Informs the limiter that the given connection has been closed.
*/
void connectionClosed(DuplexTransportConnection conn);
/**
* Informs the limiter that all connections have been closed.
*/
void allConnectionsClosed();
}

View File

@@ -0,0 +1,115 @@
package org.briarproject.bramble.plugin.bluetooth;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@NotNullByDefault
@ThreadSafe
class BluetoothConnectionLimiterImpl implements BluetoothConnectionLimiter {
private static final Logger LOG =
Logger.getLogger(BluetoothConnectionLimiterImpl.class.getName());
private final Object lock = new Object();
// The following are locking: lock
private final LinkedList<DuplexTransportConnection> connections =
new LinkedList<>();
private boolean keyAgreementInProgress = false;
@Override
public void keyAgreementStarted() {
List<DuplexTransportConnection> close;
synchronized (lock) {
keyAgreementInProgress = true;
close = new ArrayList<>(connections);
connections.clear();
}
if (LOG.isLoggable(INFO)) {
LOG.info("Key agreement started, closing " + close.size() +
" connections");
}
for (DuplexTransportConnection conn : close) tryToClose(conn);
}
@Override
public void keyAgreementEnded() {
synchronized (lock) {
keyAgreementInProgress = false;
}
LOG.info("Key agreement ended");
}
@Override
public boolean canOpenContactConnection() {
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Can't open contact connection during key agreement");
return false;
} else {
LOG.info("Can open contact connection");
return true;
}
}
}
@Override
public boolean contactConnectionOpened(DuplexTransportConnection conn) {
boolean accept = true;
synchronized (lock) {
if (keyAgreementInProgress) {
LOG.info("Refusing contact connection during key agreement");
accept = false;
} else {
LOG.info("Accepting contact connection");
connections.add(conn);
}
}
if (!accept) tryToClose(conn);
return accept;
}
@Override
public void keyAgreementConnectionOpened(DuplexTransportConnection conn) {
synchronized (lock) {
LOG.info("Accepting key agreement connection");
connections.add(conn);
}
}
private void tryToClose(DuplexTransportConnection conn) {
try {
conn.getWriter().dispose(false);
conn.getReader().dispose(false, false);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
@Override
public void connectionClosed(DuplexTransportConnection conn) {
synchronized (lock) {
connections.remove(conn);
if (LOG.isLoggable(INFO))
LOG.info("Connection closed, " + connections.size() + " open");
}
}
@Override
public void allConnectionsClosed() {
synchronized (lock) {
connections.clear();
LOG.info("All connections closed");
}
}
}

View File

@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStoppedListeningEvent;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff;
@@ -24,7 +26,6 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
@@ -51,6 +52,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
private static final Logger LOG =
Logger.getLogger(BluetoothPlugin.class.getName());
final BluetoothConnectionLimiter connectionLimiter;
private final Executor ioExecutor;
private final SecureRandom secureRandom;
private final Backoff backoff;
@@ -91,8 +94,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
abstract DuplexTransportConnection connectTo(String address, String uuid)
throws IOException;
BluetoothPlugin(Executor ioExecutor, SecureRandom secureRandom,
BluetoothPlugin(BluetoothConnectionLimiter connectionLimiter,
Executor ioExecutor, SecureRandom secureRandom,
Backoff backoff, DuplexPluginCallback callback, int maxLatency) {
this.connectionLimiter = connectionLimiter;
this.ioExecutor = ioExecutor;
this.secureRandom = secureRandom;
this.backoff = backoff;
@@ -110,6 +115,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
void onAdapterDisabled() {
LOG.info("Bluetooth disabled");
tryToClose(socket);
connectionLimiter.allConnectionsClosed();
callback.transportDisabled();
}
@@ -213,7 +219,8 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
return;
}
backoff.reset();
callback.incomingConnectionCreated(conn);
if (connectionLimiter.contactConnectionOpened(conn))
callback.incomingConnectionCreated(conn);
if (!running) return;
}
}
@@ -242,25 +249,24 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
}
@Override
public void poll(Collection<ContactId> connected) {
public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning() || !shouldAllowContactConnections()) return;
backoff.increment();
// Try to connect to known devices in parallel
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (connected.contains(c)) continue;
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue;
ContactId c = e.getKey();
ioExecutor.execute(() -> {
if (!isRunning() || !shouldAllowContactConnections()) return;
if (!connectionLimiter.canOpenContactConnection()) return;
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, conn);
if (connectionLimiter.contactConnectionOpened(conn))
callback.outgoingConnectionCreated(c, conn);
}
});
}
@@ -298,14 +304,17 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
}
@Override
public DuplexTransportConnection createConnection(ContactId c) {
public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning() || !shouldAllowContactConnections()) return null;
TransportProperties p = callback.getRemoteProperties(c);
if (!connectionLimiter.canOpenContactConnection()) return null;
String address = p.get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) return null;
String uuid = p.get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) return null;
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn == null) return null;
// TODO: Why don't we reset the backoff here?
return connectionLimiter.contactConnectionOpened(conn) ? conn : null;
}
@Override
@@ -355,7 +364,9 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO))
LOG.info("Connecting to key agreement UUID " + uuid);
return connect(address, uuid);
DuplexTransportConnection conn = connect(address, uuid);
if (conn != null) connectionLimiter.keyAgreementConnectionOpened(conn);
return conn;
}
private String parseAddress(BdfList descriptor) throws FormatException {
@@ -376,6 +387,10 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
SettingsUpdatedEvent s = (SettingsUpdatedEvent) e;
if (s.getNamespace().equals(ID.getString()))
ioExecutor.execute(this::onSettingsUpdated);
} else if (e instanceof KeyAgreementListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementStarted);
} else if (e instanceof KeyAgreementStoppedListeningEvent) {
ioExecutor.execute(connectionLimiter::keyAgreementEnded);
}
}
@@ -408,6 +423,7 @@ abstract class BluetoothPlugin<SS> implements DuplexPlugin, EventListener {
public KeyAgreementConnection accept() throws IOException {
DuplexTransportConnection conn = acceptConnection(ss);
if (LOG.isLoggable(INFO)) LOG.info(ID + ": Incoming connection");
connectionLimiter.keyAgreementConnectionOpened(conn);
return new KeyAgreementConnection(conn, ID);
}

View File

@@ -1,27 +1,21 @@
package org.briarproject.bramble.plugin.file;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.properties.TransportProperties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MIN_STREAM_LENGTH;
import static org.briarproject.bramble.api.plugin.FileConstants.PROP_PATH;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
@NotNullByDefault
abstract class FilePlugin implements SimplexPlugin {
@@ -29,25 +23,15 @@ abstract class FilePlugin implements SimplexPlugin {
private static final Logger LOG =
Logger.getLogger(FilePlugin.class.getName());
protected final Executor ioExecutor;
protected final SimplexPluginCallback callback;
protected final int maxLatency;
protected final AtomicBoolean used = new AtomicBoolean(false);
protected volatile boolean running = false;
protected abstract void writerFinished(File f, boolean exception);
@Nullable
protected abstract File chooseOutputDirectory();
protected abstract void readerFinished(File f, boolean exception,
boolean recognised);
protected abstract Collection<File> findFilesByName(String filename);
protected abstract void writerFinished(File f);
protected abstract void readerFinished(File f);
protected FilePlugin(Executor ioExecutor, SimplexPluginCallback callback,
int maxLatency) {
this.ioExecutor = ioExecutor;
FilePlugin(SimplexPluginCallback callback, int maxLatency) {
this.callback = callback;
this.maxLatency = maxLatency;
}
@@ -58,81 +42,36 @@ abstract class FilePlugin implements SimplexPlugin {
}
@Override
public int getMaxIdleTime() {
return Integer.MAX_VALUE; // We don't need keepalives
}
@Override
public boolean isRunning() {
return running;
}
@Override
public TransportConnectionReader createReader(ContactId c) {
return null;
}
@Override
public TransportConnectionWriter createWriter(ContactId c) {
if (!running) return null;
return createWriter(createConnectionFilename());
}
private String createConnectionFilename() {
StringBuilder s = new StringBuilder(12);
for (int i = 0; i < 8; i++) s.append((char) ('a' + Math.random() * 26));
s.append(".dat");
return s.toString();
}
// Package access for testing
boolean isPossibleConnectionFilename(String filename) {
return filename.toLowerCase(Locale.US).matches("[a-z]{8}\\.dat");
}
@Nullable
private TransportConnectionWriter createWriter(String filename) {
if (!running) return null;
File dir = chooseOutputDirectory();
if (dir == null || !dir.exists() || !dir.isDirectory()) return null;
File f = new File(dir, filename);
public TransportConnectionReader createReader(TransportProperties p) {
if (!isRunning()) return null;
String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
try {
long capacity = dir.getFreeSpace();
if (capacity < MIN_STREAM_LENGTH) return null;
OutputStream out = new FileOutputStream(f);
return new FileTransportWriter(f, out, capacity, this);
File file = new File(path);
FileInputStream in = new FileInputStream(file);
return new FileTransportReader(file, in, this);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
f.delete();
return null;
}
}
protected void createReaderFromFile(File f) {
if (!running) return;
ioExecutor.execute(new ReaderCreator(f));
}
private class ReaderCreator implements Runnable {
private final File file;
private ReaderCreator(File file) {
this.file = file;
}
@Override
public void run() {
if (isPossibleConnectionFilename(file.getName())) {
try {
FileInputStream in = new FileInputStream(file);
callback.readerCreated(new FileTransportReader(file, in,
FilePlugin.this));
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
@Override
public TransportConnectionWriter createWriter(TransportProperties p) {
if (!isRunning()) return null;
String path = p.get(PROP_PATH);
if (isNullOrEmpty(path)) return null;
try {
File file = new File(path);
if (!file.exists() && !file.createNewFile()) {
LOG.info("Failed to create file");
return null;
}
FileOutputStream out = new FileOutputStream(file);
return new FileTransportWriter(file, out, this);
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
}

View File

@@ -38,9 +38,6 @@ class FileTransportReader implements TransportConnectionReader {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
if (recognised) {
file.delete();
plugin.readerFinished(file);
}
plugin.readerFinished(file, exception, recognised);
}
}

View File

@@ -18,14 +18,11 @@ class FileTransportWriter implements TransportConnectionWriter {
private final File file;
private final OutputStream out;
private final long capacity;
private final FilePlugin plugin;
FileTransportWriter(File file, OutputStream out, long capacity,
FilePlugin plugin) {
FileTransportWriter(File file, OutputStream out, FilePlugin plugin) {
this.file = file;
this.out = out;
this.capacity = capacity;
this.plugin = plugin;
}
@@ -39,11 +36,6 @@ class FileTransportWriter implements TransportConnectionWriter {
return plugin.getMaxIdleTime();
}
@Override
public long getCapacity() {
return capacity;
}
@Override
public OutputStream getOutputStream() {
return out;
@@ -56,7 +48,6 @@ class FileTransportWriter implements TransportConnectionWriter {
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
if (exception) file.delete();
else plugin.writerFinished(file);
plugin.writerFinished(file, exception);
}
}

View File

@@ -207,20 +207,16 @@ abstract class TcpPlugin implements DuplexPlugin {
}
@Override
public void poll(Collection<ContactId> connected) {
public void poll(Map<ContactId, TransportProperties> contacts) {
if (!isRunning()) return;
backoff.increment();
Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey();
if (!connected.contains(c)) connectAndCallBack(c, e.getValue());
for (Entry<ContactId, TransportProperties> e : contacts.entrySet()) {
connectAndCallBack(e.getKey(), e.getValue());
}
}
private void connectAndCallBack(ContactId c, TransportProperties p) {
ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
@@ -230,13 +226,8 @@ abstract class TcpPlugin implements DuplexPlugin {
}
@Override
public DuplexTransportConnection createConnection(ContactId c) {
public DuplexTransportConnection createConnection(TransportProperties p) {
if (!isRunning()) return null;
return createConnection(callback.getRemoteProperties(c));
}
@Nullable
private DuplexTransportConnection createConnection(TransportProperties p) {
for (InetSocketAddress remote : getRemoteSocketAddresses(p)) {
if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) {

View File

@@ -7,6 +7,7 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.ValidationManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import javax.inject.Inject;
import javax.inject.Singleton;
@@ -15,6 +16,8 @@ import dagger.Module;
import dagger.Provides;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MINOR_VERSION;
@Module
public class PropertiesModule {
@@ -33,7 +36,8 @@ public class PropertiesModule {
Clock clock) {
TransportPropertyValidator validator = new TransportPropertyValidator(
clientHelper, metadataEncoder, clock);
validationManager.registerMessageValidator(CLIENT_ID, validator);
validationManager.registerMessageValidator(CLIENT_ID, MAJOR_VERSION,
validator);
return validator;
}
@@ -42,11 +46,14 @@ public class PropertiesModule {
TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager,
ClientVersioningManager clientVersioningManager,
TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
validationManager.registerIncomingMessageHook(CLIENT_ID, MAJOR_VERSION,
transportPropertyManager);
contactManager.registerContactHook(transportPropertyManager);
clientVersioningManager.registerClient(CLIENT_ID, MAJOR_VERSION,
MINOR_VERSION, transportPropertyManager);
return transportPropertyManager;
}
}

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