From 6c04aa683e9aee3ee16f85d6e3d17eb6a4750bb9 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Fri, 21 Jul 2017 10:27:20 +0200 Subject: [PATCH] Added exports for messages, labels and adddresses and some code improvements --- build.gradle | 2 + .../ch/dissem/bitmessage/entity/Plaintext.kt | 4 +- .../entity/valueobject/PrivateKey.kt | 90 +++++++++------ .../utils/ConversationServiceTest.kt | 32 +++--- exports/build.gradle | 23 ++++ .../bitmessage/exports/ContactExport.kt | 81 +++++++++++++ .../bitmessage/exports/MessageExport.kt | 106 ++++++++++++++++++ .../bitmessage/exports/ContactExportTest.kt | 73 ++++++++++++ .../bitmessage/exports/MessageExportTest.kt | 89 +++++++++++++++ gradle/wrapper/gradle-wrapper.properties | 4 +- .../networking/NetworkHandlerTest.kt | 1 - settings.gradle | 4 +- 12 files changed, 453 insertions(+), 56 deletions(-) create mode 100644 exports/build.gradle create mode 100644 exports/src/main/kotlin/ch/dissem/bitmessage/exports/ContactExport.kt create mode 100644 exports/src/main/kotlin/ch/dissem/bitmessage/exports/MessageExport.kt create mode 100644 exports/src/test/kotlin/ch/dissem/bitmessage/exports/ContactExportTest.kt create mode 100644 exports/src/test/kotlin/ch/dissem/bitmessage/exports/MessageExportTest.kt diff --git a/build.gradle b/build.gradle index df0b9d4..41f5805 100644 --- a/build.gradle +++ b/build.gradle @@ -28,6 +28,7 @@ subprojects { repositories { mavenCentral() maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + jcenter() } dependencies { compile "org.jetbrains.kotlin:kotlin-stdlib-jre7" @@ -142,6 +143,7 @@ subprojects { dependency 'com.madgag.spongycastle:prov:1.56.0.0' dependency 'org.apache.commons:commons-lang3:3.6' dependency 'org.flywaydb:flyway-core:4.2.0' + dependency 'com.beust:klaxon:0.31' dependency 'args4j:args4j:2.33' dependency 'org.ini4j:ini4j:0.5.4' diff --git a/core/src/main/kotlin/ch/dissem/bitmessage/entity/Plaintext.kt b/core/src/main/kotlin/ch/dissem/bitmessage/entity/Plaintext.kt index 3f0f551..72506b6 100644 --- a/core/src/main/kotlin/ch/dissem/bitmessage/entity/Plaintext.kt +++ b/core/src/main/kotlin/ch/dissem/bitmessage/entity/Plaintext.kt @@ -438,7 +438,7 @@ class Plaintext private constructor( if (this === other) return true if (other !is Plaintext) return false return encoding == other.encoding && - from == other.from && + from.address == other.from.address && Arrays.equals(message, other.message) && ackMessage == other.ackMessage && Arrays.equals(to?.ripe, other.to?.ripe) && @@ -655,7 +655,7 @@ class Plaintext private constructor( return this } - fun signature(signature: ByteArray): Builder { + fun signature(signature: ByteArray?): Builder { this.signature = signature return this } diff --git a/core/src/main/kotlin/ch/dissem/bitmessage/entity/valueobject/PrivateKey.kt b/core/src/main/kotlin/ch/dissem/bitmessage/entity/valueobject/PrivateKey.kt index cfc618a..c7f0b54 100644 --- a/core/src/main/kotlin/ch/dissem/bitmessage/entity/valueobject/PrivateKey.kt +++ b/core/src/main/kotlin/ch/dissem/bitmessage/entity/valueobject/PrivateKey.kt @@ -34,48 +34,37 @@ import java.util.* * Represents a private key. Additional information (stream, version, features, ...) is stored in the accompanying * [Pubkey] object. */ -class PrivateKey : Streamable { - - val privateSigningKey: ByteArray - val privateEncryptionKey: ByteArray +data class PrivateKey( + val privateSigningKey: ByteArray, + val privateEncryptionKey: ByteArray, val pubkey: Pubkey +) : Streamable { - constructor(shorter: Boolean, stream: Long, nonceTrialsPerByte: Long, extraBytes: Long, vararg features: Pubkey.Feature) { - var privSK: ByteArray - var pubSK: ByteArray - var privEK: ByteArray - var pubEK: ByteArray - var ripe: ByteArray - do { - privSK = cryptography().randomBytes(PRIVATE_KEY_SIZE) - privEK = cryptography().randomBytes(PRIVATE_KEY_SIZE) - pubSK = cryptography().createPublicKey(privSK) - pubEK = cryptography().createPublicKey(privEK) - ripe = Pubkey.getRipe(pubSK, pubEK) - } while (ripe[0].toInt() != 0 || shorter && ripe[1].toInt() != 0) - this.privateSigningKey = privSK - this.privateEncryptionKey = privEK - this.pubkey = cryptography().createPubkey(Pubkey.LATEST_VERSION, stream, privateSigningKey, privateEncryptionKey, - nonceTrialsPerByte, extraBytes, *features) - } - - constructor(privateSigningKey: ByteArray, privateEncryptionKey: ByteArray, pubkey: Pubkey) { - this.privateSigningKey = privateSigningKey - this.privateEncryptionKey = privateEncryptionKey - this.pubkey = pubkey - } + constructor( + shorter: Boolean, + stream: Long, + nonceTrialsPerByte: Long, extraBytes: Long, + vararg features: Pubkey.Feature + ) : this( + Builder(version = Pubkey.LATEST_VERSION, stream = stream, shorter = shorter) + .random() + .nonceTrialsPerByte(nonceTrialsPerByte) + .extraBytes(extraBytes) + .features(features) + .generate()) constructor(address: BitmessageAddress, passphrase: String) : this(address.version, address.stream, passphrase) - constructor(version: Long, stream: Long, passphrase: String) : this(Builder(version, stream, false).seed(passphrase).generate()) + constructor(version: Long, stream: Long, passphrase: String) : this( + Builder(version, stream, false).seed(passphrase).generate() + ) - private constructor(builder: Builder) { - this.privateSigningKey = builder.privSK!! - this.privateEncryptionKey = builder.privEK!! - this.pubkey = Factory.createPubkey(builder.version, builder.stream, builder.pubSK!!, builder.pubEK!!, - InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE, InternalContext.NETWORK_EXTRA_BYTES) - } + private constructor(builder: Builder) : this( + builder.privSK!!, builder.privEK!!, + Factory.createPubkey(builder.version, builder.stream, builder.pubSK!!, builder.pubEK!!, + builder.nonceTrialsPerByte, builder.extraBytes, *builder.features) + ) private class Builder internal constructor(internal val version: Long, internal val stream: Long, internal val shorter: Boolean) { @@ -87,6 +76,30 @@ class PrivateKey : Streamable { internal var pubSK: ByteArray? = null internal var pubEK: ByteArray? = null + internal var nonceTrialsPerByte = InternalContext.NETWORK_NONCE_TRIALS_PER_BYTE + internal var extraBytes = InternalContext.NETWORK_EXTRA_BYTES + internal var features: Array = emptyArray() + + internal fun random(): Builder { + seed = cryptography().randomBytes(1024) + return this + } + + fun nonceTrialsPerByte(nonceTrialsPerByte: Long): Builder { + this.nonceTrialsPerByte = nonceTrialsPerByte + return this + } + + fun extraBytes(extraBytes: Long): Builder { + this.extraBytes = extraBytes + return this + } + + fun features(features: Array): Builder { + this.features = features + return this + } + internal fun seed(passphrase: String): Builder { try { seed = passphrase.toByteArray(charset("UTF-8")) @@ -143,6 +156,13 @@ class PrivateKey : Streamable { Encode.varBytes(privateEncryptionKey, buffer) } + override fun equals(other: Any?) = other is PrivateKey + && Arrays.equals(privateEncryptionKey, other.privateEncryptionKey) + && Arrays.equals(privateSigningKey, other.privateSigningKey) + && pubkey == other.pubkey + + override fun hashCode() = pubkey.hashCode() + companion object { @JvmField val PRIVATE_KEY_SIZE = 32 diff --git a/core/src/test/kotlin/ch/dissem/bitmessage/utils/ConversationServiceTest.kt b/core/src/test/kotlin/ch/dissem/bitmessage/utils/ConversationServiceTest.kt index cb26e5f..f960086 100644 --- a/core/src/test/kotlin/ch/dissem/bitmessage/utils/ConversationServiceTest.kt +++ b/core/src/test/kotlin/ch/dissem/bitmessage/utils/ConversationServiceTest.kt @@ -36,6 +36,8 @@ class ConversationServiceTest : TestBase() { private val messageRepository = mock() private val conversationService = spy(ConversationService(messageRepository)) + private val conversation = conversation(alice, bob) + @Test fun `ensure conversation is sorted properly`() { MockitoKotlin.registerInstanceCreator { UUID.randomUUID() } @@ -46,8 +48,9 @@ class ConversationServiceTest : TestBase() { assertThat(actual, `is`(expected)) } - private val conversation: List - get() { + companion object { + private var timer = 2 + fun conversation(alice: BitmessageAddress, bob: BitmessageAddress): List<Plaintext> { val result = LinkedList<Plaintext>() val older = plaintext(alice, bob, @@ -99,19 +102,18 @@ class ConversationServiceTest : TestBase() { return result } - private var timer = 2 - - private fun plaintext(from: BitmessageAddress, to: BitmessageAddress, - content: ExtendedEncoding, status: Plaintext.Status): Plaintext { - val builder = Plaintext.Builder(MSG) - .IV(TestUtils.randomInventoryVector()) - .from(from) - .to(to) - .message(content) - .status(status) - if (status !== Plaintext.Status.DRAFT && status !== Plaintext.Status.DOING_PROOF_OF_WORK) { - builder.received(5L * ++timer - RANDOM.nextInt(10)) + fun plaintext(from: BitmessageAddress, to: BitmessageAddress, + content: ExtendedEncoding, status: Plaintext.Status): Plaintext { + val builder = Plaintext.Builder(MSG) + .IV(TestUtils.randomInventoryVector()) + .from(from) + .to(to) + .message(content) + .status(status) + if (status !== Plaintext.Status.DRAFT && status !== Plaintext.Status.DOING_PROOF_OF_WORK) { + builder.received(5L * ++timer - RANDOM.nextInt(10)) + } + return builder.build() } - return builder.build() } } diff --git a/exports/build.gradle b/exports/build.gradle new file mode 100644 index 0000000..718aabb --- /dev/null +++ b/exports/build.gradle @@ -0,0 +1,23 @@ +uploadArchives { + repositories { + mavenDeployer { + pom.project { + name 'Jabit Exports' + artifactId = 'jabit-exports' + description 'Import and export data to JSON files.' + } + } + } +} + +dependencies { + compile project(':core') + compile 'org.slf4j:slf4j-api' + compile 'com.beust:klaxon' + + testCompile 'junit:junit:4.12' + testCompile 'org.hamcrest:hamcrest-library:1.3' + testCompile 'com.nhaarman:mockito-kotlin:1.5.0' + testCompile project(path: ':core', configuration: 'testArtifacts') + testCompile project(':cryptography-bc') +} diff --git a/exports/src/main/kotlin/ch/dissem/bitmessage/exports/ContactExport.kt b/exports/src/main/kotlin/ch/dissem/bitmessage/exports/ContactExport.kt new file mode 100644 index 0000000..5a5d6bc --- /dev/null +++ b/exports/src/main/kotlin/ch/dissem/bitmessage/exports/ContactExport.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2017 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.exports + +import ch.dissem.bitmessage.entity.BitmessageAddress +import ch.dissem.bitmessage.entity.valueobject.PrivateKey +import ch.dissem.bitmessage.factory.Factory +import ch.dissem.bitmessage.utils.Encode +import com.beust.klaxon.* +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.util.* + +/** + * Exports and imports contacts and identities + */ +object ContactExport { + fun exportContacts(contacts: List<BitmessageAddress>, includePrivateKey: Boolean = false) = json { + val base64 = Base64.getEncoder() + array( + contacts.map { + obj( + "alias" to it.alias, + "address" to it.address, + "chan" to it.isChan, + "subscribed" to it.isSubscribed, + "pubkey" to it.pubkey?.let { + val out = ByteArrayOutputStream() + it.writeUnencrypted(out) + base64.encodeToString(out.toByteArray()) + }, + "privateKey" to if (includePrivateKey) { + it.privateKey?.let { base64.encodeToString(Encode.bytes(it)) } + } else { + null + } + ) + } + ) + } + + fun importContacts(input: JsonArray<*>): List<BitmessageAddress> { + return input.filterIsInstance(JsonObject::class.java).map { json -> + val base64 = Base64.getDecoder() + fun JsonObject.bytes(fieldName: String) = string(fieldName)?.let { base64.decode(it) } + val privateKey = json.bytes("privateKey")?.let { PrivateKey.read(ByteArrayInputStream(it)) } + if (privateKey != null) { + BitmessageAddress(privateKey) + } else { + BitmessageAddress(json.string("address") ?: throw IllegalArgumentException("address expected")) + }.apply { + alias = json.string("alias") + isChan = json.boolean("chan") ?: false + isSubscribed = json.boolean("subscribed") ?: false + pubkey = json.bytes("pubkey")?.let { + Factory.readPubkey( + version = version, + stream = stream, + `is` = ByteArrayInputStream(it), + length = it.size, + encrypted = false + ) + } + } + } + } +} diff --git a/exports/src/main/kotlin/ch/dissem/bitmessage/exports/MessageExport.kt b/exports/src/main/kotlin/ch/dissem/bitmessage/exports/MessageExport.kt new file mode 100644 index 0000000..8b4bd4e --- /dev/null +++ b/exports/src/main/kotlin/ch/dissem/bitmessage/exports/MessageExport.kt @@ -0,0 +1,106 @@ +/* + * Copyright 2017 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.exports + +import ch.dissem.bitmessage.entity.BitmessageAddress +import ch.dissem.bitmessage.entity.Plaintext +import ch.dissem.bitmessage.entity.valueobject.InventoryVector +import ch.dissem.bitmessage.entity.valueobject.Label +import ch.dissem.bitmessage.utils.Encode +import ch.dissem.bitmessage.utils.TTL +import com.beust.klaxon.* +import java.util.* + +/** + * Exports and imports messages and labels + */ +object MessageExport { + fun exportLabels(labels: List<Label>) = json { + array( + labels.map { + obj( + "label" to it.toString(), + "type" to it.type?.name, + "color" to it.color + ) + } + ) + } + + fun exportMessages(messages: List<Plaintext>) = json { + val base64 = Base64.getEncoder() + array(messages.map { + obj( + "type" to it.type.name, + "from" to it.from.address, + "to" to it.to?.address, + "subject" to it.subject, + "body" to it.text, + + "conversationId" to it.conversationId.toString(), + "msgId" to it.inventoryVector?.hash?.let { base64.encodeToString(it) }, + "encoding" to it.encodingCode, + "status" to it.status.name, + "message" to base64.encodeToString(it.message), + "ackData" to it.ackData?.let { base64.encodeToString(it) }, + "ackMessage" to it.ackMessage?.let { base64.encodeToString(Encode.bytes(it)) }, + "signature" to it.signature?.let { base64.encodeToString(it) }, + "sent" to it.sent, + "received" to it.received, + "ttl" to it.ttl, + "labels" to array(it.labels.map { it.toString() }) + ) + }) + } + + fun importMessages(input: JsonArray<*>, labels: Map<String, Label>): List<Plaintext> { + return input.filterIsInstance(JsonObject::class.java).map { json -> + val base64 = Base64.getDecoder() + fun JsonObject.bytes(fieldName: String) = string(fieldName)?.let { base64.decode(it) } + Plaintext.Builder(Plaintext.Type.valueOf(json.string("type") ?: "MSG")) + .from(json.string("from")?.let { BitmessageAddress(it) } ?: throw IllegalArgumentException("'from' address expected")) + .to(json.string("to")?.let { BitmessageAddress(it) }) + .conversation(json.string("conversationId")?.let { UUID.fromString(it) } ?: UUID.randomUUID()) + .IV(json.bytes("msgId")?.let { InventoryVector(it) }) + .encoding(json.long("encoding") ?: throw IllegalArgumentException("encoding expected")) + .status(json.string("status")?.let { Plaintext.Status.valueOf(it) } ?: throw IllegalArgumentException("status expected")) + .message(json.bytes("message") ?: throw IllegalArgumentException("message expected")) + .ackData(json.bytes("ackData")) + .ackMessage(json.bytes("ackMessage")) + .signature(json.bytes("signature")) + .sent(json.long("sent")) + .received(json.long("received")) + .ttl(json.long("ttl") ?: TTL.msg) + .labels( + json.array<String>("labels")?.map { labels[it] }?.filterNotNull() ?: emptyList() + ) + .build() + } + } + + fun importLabels(input: JsonArray<Any?>): List<Label> { + return input.filterIsInstance(JsonObject::class.java).map { json -> + Label( + label = json.string("label") ?: throw IllegalArgumentException("label expected"), + type = json.string("type")?.let { Label.Type.valueOf(it) }, + color = json.int("color") ?: 0 + ) + } + } + + fun createLabelMap(labels: List<Label>) = labels.associateBy { it.toString() } +} diff --git a/exports/src/test/kotlin/ch/dissem/bitmessage/exports/ContactExportTest.kt b/exports/src/test/kotlin/ch/dissem/bitmessage/exports/ContactExportTest.kt new file mode 100644 index 0000000..dd3aa5f --- /dev/null +++ b/exports/src/test/kotlin/ch/dissem/bitmessage/exports/ContactExportTest.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2017 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.exports + +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography +import ch.dissem.bitmessage.entity.BitmessageAddress +import ch.dissem.bitmessage.utils.TestUtils +import org.hamcrest.CoreMatchers.`is` +import org.hamcrest.CoreMatchers.nullValue +import org.junit.Assert.assertThat +import org.junit.Test + +class ContactExportTest { + + init { + TestUtils.mockedInternalContext(cryptography = BouncyCryptography()) + } + + @Test + fun `ensure contacts are exported`() { + val alice = BitmessageAddress("BM-2cTtkBnb4BUYDndTKun6D9PjtueP2h1bQj") + alice.alias = "Alice" + alice.isSubscribed = true + val contacts = listOf( + BitmessageAddress("BM-2cWJ4UFRTCehWuWNsW8fJkAYMxU4S8jxci"), + TestUtils.loadContact(), + alice + ) + val export = ContactExport.exportContacts(contacts) + print(export.toJsonString(true)) + assertThat(ContactExport.importContacts(export), `is`(contacts)) + } + + @Test + fun `ensure private keys are omitted by default`() { + val contacts = listOf( + BitmessageAddress.chan(1, "test") + ) + val export = ContactExport.exportContacts(contacts) + print(export.toJsonString(true)) + val import = ContactExport.importContacts(export) + assertThat(import.size, `is`(1)) + assertThat(import[0].isChan, `is`(true)) + assertThat(import[0].privateKey, `is`(nullValue())) + } + + @Test + fun `ensure private keys are exported if flag is set`() { + val contacts = listOf( + BitmessageAddress.chan(1, "test") + ) + val export = ContactExport.exportContacts(contacts, true) + print(export.toJsonString(true)) + val import = ContactExport.importContacts(export) + assertThat(import.size, `is`(1)) + assertThat(import[0].isChan, `is`(true)) + assertThat(import[0].privateKey, `is`(contacts[0].privateKey)) + } +} diff --git a/exports/src/test/kotlin/ch/dissem/bitmessage/exports/MessageExportTest.kt b/exports/src/test/kotlin/ch/dissem/bitmessage/exports/MessageExportTest.kt new file mode 100644 index 0000000..04fd799 --- /dev/null +++ b/exports/src/test/kotlin/ch/dissem/bitmessage/exports/MessageExportTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2017 Christian Basler + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package ch.dissem.bitmessage.exports + +import ch.dissem.bitmessage.cryptography.bc.BouncyCryptography +import ch.dissem.bitmessage.entity.BitmessageAddress +import ch.dissem.bitmessage.entity.Plaintext +import ch.dissem.bitmessage.entity.valueobject.Label +import ch.dissem.bitmessage.utils.ConversationServiceTest +import ch.dissem.bitmessage.utils.Singleton +import ch.dissem.bitmessage.utils.TestUtils +import org.hamcrest.CoreMatchers.`is` +import org.junit.Assert.assertThat +import org.junit.Test + +class MessageExportTest { + val inbox = Label("Inbox", Label.Type.INBOX, 0x0000ff) + val outbox = Label("Outbox", Label.Type.OUTBOX, 0x00ff00) + val unread = Label("Unread", Label.Type.UNREAD, 0x000000) + val trash = Label("Trash", Label.Type.TRASH, 0x555555) + + val labels = listOf( + inbox, + outbox, + unread, + trash + ) + val labelMap = MessageExport.createLabelMap(labels) + + init { + TestUtils.mockedInternalContext(cryptography = BouncyCryptography()) + } + + @Test + fun `ensure labels are exported`() { + val export = MessageExport.exportLabels(labels) + print(export.toJsonString(true)) + assertThat(MessageExport.importLabels(export), `is`(labels)) + } + + @Test + fun `ensure messages are exported`() { + val messages = listOf( + Plaintext.Builder(Plaintext.Type.MSG) + .ackData(Singleton.cryptography().randomBytes(32)) + .from(BitmessageAddress("BM-2cWJ4UFRTCehWuWNsW8fJkAYMxU4S8jxci")) + .to(BitmessageAddress("BM-2DAjcCFrqFrp88FUxExhJ9kPqHdunQmiyn")) + .message("Subject", "Message") + .status(Plaintext.Status.RECEIVED) + .labels(listOf(inbox)) + .build(), + Plaintext.Builder(Plaintext.Type.BROADCAST) + .from(BitmessageAddress("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .message("Subject", "Message") + .labels(listOf(inbox, unread)) + .status(Plaintext.Status.SENT) + .build(), + Plaintext.Builder(Plaintext.Type.MSG) + .ttl(1) + .message("subject", "message") + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .labels(listOf(trash)) + .status(Plaintext.Status.SENT_ACKNOWLEDGED) + .build(), + *ConversationServiceTest.conversation( + TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"), + TestUtils.loadContact() + ).toTypedArray() + ) + val export = MessageExport.exportMessages(messages) + print(export.toJsonString(true)) + assertThat(MessageExport.importMessages(export, labelMap), `is`(messages)) + } +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0cf2d0f..65d177a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Jul 02 11:22:52 CEST 2017 +#Mon Jul 17 06:32:41 CEST 2017 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-all.zip diff --git a/networking/src/test/kotlin/ch/dissem/bitmessage/networking/NetworkHandlerTest.kt b/networking/src/test/kotlin/ch/dissem/bitmessage/networking/NetworkHandlerTest.kt index 990b31d..a94cd1f 100644 --- a/networking/src/test/kotlin/ch/dissem/bitmessage/networking/NetworkHandlerTest.kt +++ b/networking/src/test/kotlin/ch/dissem/bitmessage/networking/NetworkHandlerTest.kt @@ -241,7 +241,6 @@ class NetworkHandlerTest { Thread.sleep(100) } catch (ignore: InterruptedException) { } - } while (ctx.isRunning()) } diff --git a/settings.gradle b/settings.gradle index 4caa813..59771e2 100644 --- a/settings.gradle +++ b/settings.gradle @@ -14,4 +14,6 @@ include 'cryptography-sc' include 'cryptography-bc' -include 'extensions' \ No newline at end of file +include 'extensions' + +include 'exports'