From 0bb455d433641eceb878429dcd41909725273010 Mon Sep 17 00:00:00 2001 From: Christian Basler Date: Wed, 30 Nov 2016 17:26:22 +0100 Subject: [PATCH] Extended encoding basics works for basic subject/body messages, no attachments and no other features supported yet --- core/build.gradle | 1 + .../dissem/bitmessage/entity/Plaintext.java | 88 ++++++-- .../entity/valueobject/Attachment.java | 95 ++++++++ .../entity/valueobject/ExtendedEncoding.java | 203 ++++++++++++++++++ .../entity/ExtendedEncodingTest.java | 39 ++++ .../bitmessage/entity/SerializationTest.java | 60 ++++-- 6 files changed, 457 insertions(+), 29 deletions(-) create mode 100644 core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Attachment.java create mode 100644 core/src/main/java/ch/dissem/bitmessage/entity/valueobject/ExtendedEncoding.java create mode 100644 core/src/test/java/ch/dissem/bitmessage/entity/ExtendedEncodingTest.java diff --git a/core/build.gradle b/core/build.gradle index 73f8fdf..1c2d07f 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -25,6 +25,7 @@ artifacts { dependencies { compile 'org.slf4j:slf4j-api:1.7.12' + compile 'org.msgpack:msgpack-core:0.8.11' testCompile 'junit:junit:4.12' testCompile 'org.hamcrest:hamcrest-library:1.3' testCompile 'org.mockito:mockito-core:1.10.19' diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java b/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java index 710e041..e08df3d 100644 --- a/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java +++ b/core/src/main/java/ch/dissem/bitmessage/entity/Plaintext.java @@ -18,17 +18,23 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.Msg; import ch.dissem.bitmessage.entity.payload.Pubkey.Feature; +import ch.dissem.bitmessage.entity.valueobject.Attachment; +import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.exception.ApplicationException; import ch.dissem.bitmessage.factory.Factory; -import ch.dissem.bitmessage.utils.*; +import ch.dissem.bitmessage.utils.Decode; +import ch.dissem.bitmessage.utils.Encode; +import ch.dissem.bitmessage.utils.TTL; +import ch.dissem.bitmessage.utils.UnixTime; import java.io.*; import java.nio.ByteBuffer; import java.util.*; -import java.util.Collections; +import static ch.dissem.bitmessage.entity.Plaintext.Encoding.EXTENDED; +import static ch.dissem.bitmessage.entity.Plaintext.Encoding.SIMPLE; import static ch.dissem.bitmessage.utils.Singleton.cryptography; /** @@ -42,6 +48,7 @@ public class Plaintext implements Streamable { private final long encoding; private final byte[] message; private final byte[] ackData; + private ExtendedEncoding extendedData; private ObjectMessage ackMessage; private Object id; private InventoryVector inventoryVector; @@ -143,6 +150,10 @@ public class Plaintext implements Streamable { return labels; } + public Encoding getEncoding() { + return Encoding.fromCode(encoding); + } + public long getStream() { return from.getStream(); } @@ -299,7 +310,13 @@ public class Plaintext implements Streamable { public String getSubject() { Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8"); String firstLine = s.nextLine(); - if (encoding == 2) { + if (encoding == EXTENDED.code) { + if (getExtendedData().getMessage() == null) { + return null; + } else { + return extendedData.getMessage().getSubject(); + } + } else if (encoding == SIMPLE.code) { return firstLine.substring("Subject:".length()).trim(); } else if (firstLine.length() > 50) { return firstLine.substring(0, 50).trim() + "..."; @@ -309,14 +326,46 @@ public class Plaintext implements Streamable { } public String getText() { - try { - String text = new String(message, "UTF-8"); - if (encoding == 2) { - return text.substring(text.indexOf("\nBody:") + 6); + if (encoding == EXTENDED.code) { + if (getExtendedData().getMessage() == null) { + return null; + } else { + return extendedData.getMessage().getBody(); } - return text; - } catch (UnsupportedEncodingException e) { - throw new ApplicationException(e); + } else { + try { + String text = new String(message, "UTF-8"); + if (encoding == SIMPLE.code) { + return text.substring(text.indexOf("\nBody:") + 6); + } + return text; + } catch (UnsupportedEncodingException e) { + throw new ApplicationException(e); + } + } + } + + protected ExtendedEncoding getExtendedData() { + if (extendedData == null && encoding == EXTENDED.code) { + // TODO: make sure errors are properly handled + extendedData = ExtendedEncoding.unzip(message); + } + return extendedData; + } + + public List getParents() { + if (getExtendedData() == null || extendedData.getMessage() == null) { + return Collections.emptyList(); + } else { + return extendedData.getMessage().getParents(); + } + } + + public List getFiles() { + if (getExtendedData() == null || extendedData.getMessage() == null) { + return Collections.emptyList(); + } else { + return extendedData.getMessage().getFiles(); } } @@ -386,7 +435,7 @@ public class Plaintext implements Streamable { } public enum Encoding { - IGNORE(0), TRIVIAL(1), SIMPLE(2); + IGNORE(0), TRIVIAL(1), SIMPLE(2), EXTENDED(3); long code; @@ -397,6 +446,15 @@ public class Plaintext implements Streamable { public long getCode() { return code; } + + public static Encoding fromCode(long code) { + for (Encoding e : values()) { + if (e.getCode() == code) { + return e; + } + } + return null; + } } public enum Status { @@ -517,9 +575,15 @@ public class Plaintext implements Streamable { return this; } + public Builder message(ExtendedEncoding message) { + this.encoding = EXTENDED.getCode(); + this.message = message.zip(); + return this; + } + public Builder message(String subject, String message) { try { - this.encoding = Encoding.SIMPLE.getCode(); + this.encoding = SIMPLE.getCode(); this.message = ("Subject:" + subject + '\n' + "Body:" + message).getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new ApplicationException(e); diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Attachment.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Attachment.java new file mode 100644 index 0000000..8ea64de --- /dev/null +++ b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/Attachment.java @@ -0,0 +1,95 @@ +package ch.dissem.bitmessage.entity.valueobject; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Objects; + +/** + * A "file" attachment as used by extended encoding type messages. Could either be an attachment, + * or used inline to be used by a HTML message, for example. + */ +public class Attachment implements Serializable { + private static final long serialVersionUID = 7319139427666943189L; + + private String name; + private byte[] data; + private String type; + private Disposition disposition; + + public String getName() { + return name; + } + + public byte[] getData() { + return data; + } + + public String getType() { + return type; + } + + public Disposition getDisposition() { + return disposition; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Attachment that = (Attachment) o; + return Objects.equals(name, that.name) && + Arrays.equals(data, that.data) && + Objects.equals(type, that.type) && + disposition == that.disposition; + } + + @Override + public int hashCode() { + return Objects.hash(name, data, type, disposition); + } + + private enum Disposition { + inline, attachment + } + + public static final class Builder { + private String name; + private byte[] data; + private String type; + private Disposition disposition; + + public Builder name(String name) { + this.name = name; + return this; + } + + public Builder data(byte[] data) { + this.data = data; + return this; + } + + public Builder type(String type) { + this.type = type; + return this; + } + + public Builder inline() { + this.disposition = Disposition.inline; + return this; + } + + public Builder attachment() { + this.disposition = Disposition.attachment; + return this; + } + + public Attachment build() { + Attachment attachment = new Attachment(); + attachment.type = this.type; + attachment.disposition = this.disposition; + attachment.data = this.data; + attachment.name = this.name; + return attachment; + } + } +} diff --git a/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/ExtendedEncoding.java b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/ExtendedEncoding.java new file mode 100644 index 0000000..699cd22 --- /dev/null +++ b/core/src/main/java/ch/dissem/bitmessage/entity/valueobject/ExtendedEncoding.java @@ -0,0 +1,203 @@ +package ch.dissem.bitmessage.entity.valueobject; + +import ch.dissem.bitmessage.exception.ApplicationException; +import org.msgpack.core.MessagePack; +import org.msgpack.core.MessagePacker; +import org.msgpack.core.MessageUnpacker; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; +import java.util.zip.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Extended encoding message object. + */ +public class ExtendedEncoding implements Serializable { + private static final long serialVersionUID = 3876871488247305200L; + + private Message message; + + public ExtendedEncoding(Message message) { + this.message = message; + } + + private ExtendedEncoding() { + } + + public Message getMessage() { + return message; + } + + public byte[] zip() { + try (ByteArrayOutputStream out = new ByteArrayOutputStream(); + DeflaterOutputStream zipper = new DeflaterOutputStream(out)) { + + MessagePacker packer = MessagePack.newDefaultPacker(zipper); + // FIXME: this should work for trivial cases + if (message != null) { + message.pack(packer); + } + packer.close(); + zipper.close(); + return out.toByteArray(); + } catch (IOException e) { + throw new ApplicationException(e); + } + } + + public static ExtendedEncoding unzip(byte[] zippedData) { + ExtendedEncoding result = new ExtendedEncoding(); + try (InflaterInputStream unzipper = new InflaterInputStream(new ByteArrayInputStream(zippedData))) { + MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(unzipper); + int mapSize = unpacker.unpackMapHeader(); + for (int i = 0; i < mapSize; i++) { + String key = unpacker.unpackString(); + switch (key) { + case "": + switch (unpacker.unpackString()) { + case "message": + result.message = new Message(); + break; + } + break; + case "subject": + result.message.subject = unpacker.unpackString(); + break; + case "body": + result.message.body = unpacker.unpackString(); + break; + default: + break; + } + } + } catch (IOException e) { + throw new ApplicationException(e); + } + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ExtendedEncoding that = (ExtendedEncoding) o; + return Objects.equals(message, that.message); + } + + @Override + public int hashCode() { + return Objects.hash(message); + } + + public static class Message implements Serializable { + private static final long serialVersionUID = -2724977231484285467L; + + private String subject; + private String body; + private List parents; + private List files; + + private Message() { + parents = Collections.emptyList(); + files = Collections.emptyList(); + } + + private Message(Builder builder) { + subject = builder.subject; + body = builder.body; + parents = Collections.unmodifiableList(builder.parents); + files = Collections.unmodifiableList(builder.files); + } + + public String getSubject() { + return subject; + } + + public String getBody() { + return body; + } + + public List getParents() { + return parents; + } + + public List getFiles() { + return files; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Message message = (Message) o; + return Objects.equals(subject, message.subject) && + Objects.equals(body, message.body) && + Objects.equals(parents, message.parents) && + Objects.equals(files, message.files); + } + + @Override + public int hashCode() { + return Objects.hash(subject, body, parents, files); + } + + public void pack(MessagePacker packer) throws IOException { + packer.packMapHeader(3); + packer.packString(""); + packer.packString("message"); + packer.packString("subject"); + packer.packString(subject); + packer.packString("body"); + packer.packString(body); + } + + public static class Builder { + private String subject; + private String body; + private List parents = new LinkedList<>(); + private List files = new LinkedList<>(); + + private Builder() { + } + + public Builder subject(String subject) { + this.subject = subject; + return this; + } + + public Builder body(String body) { + this.body = body; + return this; + } + + public Builder addParent(InventoryVector iv) { + parents.add(iv); + return this; + } + + public Builder addFile(Attachment file) { + files.add(file); + return this; + } + + public ExtendedEncoding build() { + return new ExtendedEncoding(new Message(this)); + } + } + } + + public static class Builder { + public Message.Builder message() { + return new Message.Builder(); + } + + // TODO: vote (etc.?) + } +} diff --git a/core/src/test/java/ch/dissem/bitmessage/entity/ExtendedEncodingTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/ExtendedEncodingTest.java new file mode 100644 index 0000000..b4a1017 --- /dev/null +++ b/core/src/test/java/ch/dissem/bitmessage/entity/ExtendedEncodingTest.java @@ -0,0 +1,39 @@ +package ch.dissem.bitmessage.entity; + +import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding; +import ch.dissem.bitmessage.utils.Bytes; +import org.junit.Test; + +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertThat; + +/** + * @author Christian Basler + */ +public class ExtendedEncodingTest { + + @Test + public void ensureSimpleMessageIsDecoded() { + ExtendedEncoding extended = ExtendedEncoding.unzip(Bytes.fromHex("78da9d59dd8e1bb715ee359fa097c4b65b498876babb76d0760bd970368eedb68e8daed3c0700c81d25012b333c3e99063ad1c0430d0a728d002bd289077caa59fa4df39244723693705b2f0da339c730ebff3c3f343ffe33f3f94da39b5d4ff9dd97cf3e32f9d10524afe4b3a2b57762dfd4acb453bbf967355c967b2755a1a2f1f0afe791eb85f2a7c374e2ab950ce8fe5dc96b59a7b3933956a36d2e9c6a8c2bc57ded84a2e6c532a10b9d678352b342d48674a53a846e6ca2be9adfcd3d58b2f33f96a05a190748d3d64ddd87726d74e5ebedcf81504417a6eaaa563018d56f4225595cb75633c3df7d191e04c3cab9c574521c4af656d6a69c2ab2cdd927639a959b0102f372f3742ecae6ef7afdb0668c2a229eb4297baf2ac5b2689932d4576f2809f89af01d3ae61d9af57ba921bdbd2f78f1ffee999269a2837ce3766d692943113555ae7648984f16fc6b5aa9057becd8d95d038ca95579fff5902497ccdf00064ad97fac6ebcab1bcd69139eec64dfcc9aa4d5b39e90abb2e3699105f60a3f8e13cfbdd583e37f3c63abbf009cfe5279fc84bb8db14ba61476ca929221a8d58c03e399471b660fd76c5decb3ebd456c54f3fcf4ec53125fb695f11bf93837016e231fdfd40ddcdb2d91cd679a4cca569bb5a6c8b72690a5cddb4267e22945b42532f1a2d2276e653d0798fc8d6c2b7a10e22b78859e66ac0e3da5b80a14613d3ce34b96a2671b20795bd68e390aab723ce14f615408543e1bdecc4c411aade12cf9ad034222afcdfc9a500a96472b248a313882bd80954f0a73ada59d7dabe73e8b30babde23b766a6c79482ec483070fc8f7b6f109362fc5e78cf51bbe391bcbf3b1bcf7762406dfdcfce1de3737a767f83dc7efbdc10e7db4c8703a121d93101154c2128911f2b043d3a80da912bccf6b85a19c81d80f07273083c4b788d10b71eb86b3436014e67a4ac2265fa8c2e99118464823215ee344c1d92d8242156bb5a1b482e8a1e496b8e4b5deac6d038266d9d2d1c8e4954628e88633563547e273aed514d485e20413222970db9ae370a6717460e8bf221fb1f8dccee968839abc6fb1d4445ae4862b8fbc55d2c72e9e1080fc0832cea81f3ffccbed51e9e6e3877f67f299df9ab82dbc81b5a2a3a3ff6da565e09543eccd6bb38da7141642951036b65dae90d2c14319a7d4f04b3e82067b61c2dc389271f93392f3ec8510b3762127e9753812a4a541d6928daa967a78767a3abaa07a02ba8c12b31eee065b2033a3d18865654eebebe1e9284511cc30e9bc9f2c330461d82912e5b46162e0eda0a2a97cf71d393d98f7b79da1a55dc879eb3cd40a5567536b216054327b81ea575be70c1528b8997822eb011372ee53dd68e6ab907314e556f63648b437a5ceba87ceac69e5c0ce0211b5688b696e504027f23b56e6c8e44717f26c1c5ee670a9d7b472b84165d7c3d1587c2f44ae173247facdf5347d1d223c8237a459c8d960da7d994e0764427c0f9fe9072f0070b80542aa4ed2de1c2937c5c2d1dbb13c3a7e7d5c1ee7af8e9f5e1c3fbf38beca8e17472396d668df362c3dc0d2d54fc0328e4b1ece1bad8f0f018cb610a3e0ef7635b990af9a568fe52060c33b0411ec05ef762bceef0f808a1039c911bb51db731210ea85c2019ceca93512d40030cd542d15cc3b39c863bd3dc6f1f04e57d65e4ff63c37daa605ce993d5264cda2985160a2d86af34e53d620817fe4fc13296b651ab7475f221593ad2955f56a6714032921b12d28339ebc53454b85116232f1980a2be70f8aff9f776a6257421851a779edee021597b87cf042886eb67b0c9f2ccbe41d21c46c19ff3de25a49f1401b928de5642207f9602ba01708c95dd0f715c887f7cfc781d7869c3e44d64a6c8d32e81c88ec71d3d86678f455758dd358b16617f2b83992c792f104a64e0d68cf8e19121a8e77b5ab4e4279ff7c17a44240f5541b428bd12e4146593b4265b9b729a9b62a8485a46d0fcf1630f9ef60dbb17c739651a9cdeea36920ba9896f7cf0db16f0f4cfc377074a9fcae5332ee2c35490fa31ea6c936d9d3f117e251fe8e4220dfd65658b2f28d45ffff8873b52abc6e2ad4f2771cae284e8d0a1d78578353418d1533cac2f4726dea314f1d53b6c2748547d05374f16aa9eab4166aaaa37986eb428935bfb64cc725a3f206f5238e62a14ed3b108b59b7671085542596052d0719c72a92d0d5e4b6709c430e9987a64b3ac2c771ec687bdd1b6f8b877c4945c4e9c555bce48634464980d1c95046e916a74adc97e4864ba19d378e8570a63869aaf1203d19359c9208c603771f067641d182eb5ebc9e73402c2a23557f2dcc0d498027802794cf28109389cee6053ea0a5d1491f1c4a7196a94c7218139954cccadb3daa6bd455bcd8913ade55d4d0d67973492e961ea2cc672a9fd144d229e6315aa487527bbe06bb23def0f4309dc6b8b025baf92fd0acd660127036890cf1ca4d1127eafc8921d2d9efb1b8687b80ffd4401931eda2178466267b72af4a7c13d34a224dba53884d50273c7c6cd636828fb2d5ffadc2122570eb7c4a1efdbc7874eafcafb5440cb6028a98a2f2dd645ec9c29cae3a01cbb34d4e882bcbc71e36efecaad7661b026d781af356e256f17415307e6a238839e85e3411fe265020d9714d07771a3da55aa84b11a45dd7e824061fdff37e73d501a32f919861ec3b5b3c220da87749e754f52e5396731c38520c80cbc69df467768c86d20e13d03e3cc841ac41303b69ae9b9a2ab876badebdbc6513e3bb1c784cbd9d28599217f91f8b572a153e0f8a4ca5dda86ce9eaaa28eb0c765db00922f9005d62b4ca17b372cd2b5351d38a8add71dbeae2e48a73dcf575b8fc66b271e89b96a3b06032c61a0496d0c1245b2d390ac00d1e99e81669ad7e1f645ce57740af98286340c9d088d72c032252c132a2169a62321a11ad0fedce5817e8221acf58b93dfe3df1e65aa1c77f7327794c637b381ab554973ec402f9783b7e34344a35b87e1ae3c266403c68566e0cd9e482152ab25c42b1e5ebbde6b1c26e2bd8e27d6bfd4ccfd0cb56e6ba166839bcdfbf79bc1e8a7f419897e1f32213e8acc49e2dd7a33064708de1842eca9d82584413e4ee694583876f742f2343bcf6e4854837e96b2c1c1f77b72a5d028385b62bb2a1d1c04778826b4c5cc971084c44a0179d705031d468e332e43d82093bd7b8aee82a223d7377561e606c78a15a062b65690b77386b3eda4d05506b606e38ecd782aa34842a16f75f245eb5f2c3ee7b2b93d4b57ded6cf525794edbe12fa2e1184d689eecb1aebeddc16d25654bed9da6a46f791bd8b14f1e41239a27705f4e412394b514258d325e9b2b16bce9b4561e79c86d27d17df08975a55aed7d5513310521a7e0bba157c72194c99a26339cf9096e9b619c6e02db6dc05ecdb355f99d8bbd011e22ff462822bb609eaefada61b214ee73054d0252465beb7228ec22c57dc6f516e2499bba076afab62a2dce26298bbb74ff1d8a98aaef27ae6e311b57fabcc0a203952a740beefdf849351d7e9266e119a3452012d4bcd85650f57a4df02732d3ab2642ff9a80a775aeb70afb74716d38749d3dec1349a891f5ccb8b3ffee2ac1b2b5326eb5d6c73d7e0a43c917c5548ff35f1f0e1ff00b5629026")); + assertThat(extended, notNullValue()); + assertThat(extended.getMessage().getSubject(), is("Extended encoding on Windows works - but how ??")); + assertThat(extended.getMessage().getBody(), notNullValue()); + assertThat(extended.getMessage().getBody().length(), is(6233)); + } + + + @Test + public void ensureSimpleMessageIsEncoded() { + ExtendedEncoding in = new ExtendedEncoding.Builder() + .message() + .subject("Test sübject") + .body("test bödy") + .build(); + + assertThat(in.zip(), notNullValue()); + ExtendedEncoding out = ExtendedEncoding.unzip(in.zip()); + + assertThat(out, is(in)); + } +} diff --git a/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java b/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java index 5405f23..899c60b 100644 --- a/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java +++ b/core/src/test/java/ch/dissem/bitmessage/entity/SerializationTest.java @@ -17,6 +17,7 @@ package ch.dissem.bitmessage.entity; import ch.dissem.bitmessage.entity.payload.*; +import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding; import ch.dissem.bitmessage.entity.valueobject.InventoryVector; import ch.dissem.bitmessage.entity.valueobject.Label; import ch.dissem.bitmessage.factory.Factory; @@ -79,12 +80,37 @@ public class SerializationTest extends TestBase { @Test public void ensurePlaintextIsSerializedAndDeserializedCorrectly() throws Exception { Plaintext p1 = new Plaintext.Builder(MSG) - .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) - .to(TestUtils.loadContact()) - .message("Subject", "Message") - .ackData("ackMessage".getBytes()) - .signature(new byte[0]) - .build(); + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .message("Subject", "Message") + .ackData("ackMessage".getBytes()) + .signature(new byte[0]) + .build(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + p1.write(out); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + Plaintext p2 = Plaintext.read(MSG, in); + + // Received is automatically set on deserialization, so we'll need to set it to 0 + Field received = Plaintext.class.getDeclaredField("received"); + received.setAccessible(true); + received.set(p2, 0L); + + assertEquals(p1, p2); + } + + @Test + public void ensurePlaintextWithExtendedEncodingIsSerializedAndDeserializedCorrectly() throws Exception { + Plaintext p1 = new Plaintext.Builder(MSG) + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .message(new ExtendedEncoding.Builder().message() + .subject("Subject") + .body("Message") + .build()) + .ackData("ackMessage".getBytes()) + .signature(new byte[0]) + .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); p1.write(out); ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); @@ -101,12 +127,12 @@ public class SerializationTest extends TestBase { @Test public void ensurePlaintextWithAckMessageIsSerializedAndDeserializedCorrectly() throws Exception { Plaintext p1 = new Plaintext.Builder(MSG) - .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) - .to(TestUtils.loadContact()) - .message("Subject", "Message") - .ackData("ackMessage".getBytes()) - .signature(new byte[0]) - .build(); + .from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .to(TestUtils.loadContact()) + .message("Subject", "Message") + .ackData("ackMessage".getBytes()) + .signature(new byte[0]) + .build(); ObjectMessage ackMessage1 = p1.getAckMessage(); assertNotNull(ackMessage1); @@ -156,11 +182,11 @@ public class SerializationTest extends TestBase { @Test public void ensureSystemSerializationWorks() throws Exception { Plaintext plaintext = new Plaintext.Builder(MSG) - .from(TestUtils.loadContact()) - .to(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) - .labels(Collections.singletonList(new Label("Test", Label.Type.INBOX, 0))) - .message("Test", "Test Test.\nTest") - .build(); + .from(TestUtils.loadContact()) + .to(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8")) + .labels(Collections.singletonList(new Label("Test", Label.Type.INBOX, 0))) + .message("Test", "Test Test.\nTest") + .build(); ByteArrayOutputStream out = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(out); oos.writeObject(plaintext);