First implementation of extended message encoding. Works as far as the PyBitmessage implementation, with some additional code.
This commit is contained in:
parent
e1dcbbf19c
commit
6d67598a40
@ -18,11 +18,13 @@ 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.extended.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.entity.valueobject.extended.Message;
|
||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import ch.dissem.bitmessage.factory.ExtendedEncodingFactory;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.utils.Decode;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
@ -333,10 +335,10 @@ public class Plaintext implements Streamable {
|
||||
Scanner s = new Scanner(new ByteArrayInputStream(message), "UTF-8");
|
||||
String firstLine = s.nextLine();
|
||||
if (encoding == EXTENDED.code) {
|
||||
if (getExtendedData().getMessage() == null) {
|
||||
return null;
|
||||
if (Message.TYPE.equals(getExtendedData().getType())) {
|
||||
return ((Message) extendedData.getContent()).getSubject();
|
||||
} else {
|
||||
return extendedData.getMessage().getSubject();
|
||||
return null;
|
||||
}
|
||||
} else if (encoding == SIMPLE.code) {
|
||||
return firstLine.substring("Subject:".length()).trim();
|
||||
@ -349,10 +351,10 @@ public class Plaintext implements Streamable {
|
||||
|
||||
public String getText() {
|
||||
if (encoding == EXTENDED.code) {
|
||||
if (getExtendedData().getMessage() == null) {
|
||||
return null;
|
||||
if (Message.TYPE.equals(getExtendedData().getType())) {
|
||||
return ((Message) extendedData.getContent()).getBody();
|
||||
} else {
|
||||
return extendedData.getMessage().getBody();
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
@ -370,24 +372,24 @@ public class Plaintext implements Streamable {
|
||||
protected ExtendedEncoding getExtendedData() {
|
||||
if (extendedData == null && encoding == EXTENDED.code) {
|
||||
// TODO: make sure errors are properly handled
|
||||
extendedData = ExtendedEncoding.unzip(message);
|
||||
extendedData = ExtendedEncodingFactory.getInstance().unzip(message);
|
||||
}
|
||||
return extendedData;
|
||||
}
|
||||
|
||||
public List<InventoryVector> getParents() {
|
||||
if (getExtendedData() == null || extendedData.getMessage() == null) {
|
||||
return Collections.emptyList();
|
||||
if (Message.TYPE.equals(getExtendedData().getType())) {
|
||||
return ((Message) extendedData.getContent()).getParents();
|
||||
} else {
|
||||
return extendedData.getMessage().getParents();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Attachment> getFiles() {
|
||||
if (getExtendedData() == null || extendedData.getMessage() == null) {
|
||||
return Collections.emptyList();
|
||||
if (Message.TYPE.equals(getExtendedData().getType())) {
|
||||
return ((Message) extendedData.getContent()).getFiles();
|
||||
} else {
|
||||
return extendedData.getMessage().getFiles();
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,35 +4,38 @@ import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import org.msgpack.core.MessagePack;
|
||||
import org.msgpack.core.MessagePacker;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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 static final Logger LOG = LoggerFactory.getLogger(ExtendedEncoding.class);
|
||||
|
||||
private Message message;
|
||||
private ExtendedType content;
|
||||
|
||||
public ExtendedEncoding(Message message) {
|
||||
this.message = message;
|
||||
public ExtendedEncoding(ExtendedType content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
private ExtendedEncoding() {
|
||||
public String getType() {
|
||||
if (content == null) {
|
||||
return null;
|
||||
} else {
|
||||
return content.getType();
|
||||
}
|
||||
}
|
||||
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
public ExtendedType getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public byte[] zip() {
|
||||
@ -40,10 +43,7 @@ public class ExtendedEncoding implements Serializable {
|
||||
DeflaterOutputStream zipper = new DeflaterOutputStream(out)) {
|
||||
|
||||
MessagePacker packer = MessagePack.newDefaultPacker(zipper);
|
||||
// FIXME: this should work for trivial cases
|
||||
if (message != null) {
|
||||
message.pack(packer);
|
||||
}
|
||||
content.pack(packer);
|
||||
packer.close();
|
||||
zipper.close();
|
||||
return out.toByteArray();
|
||||
@ -52,152 +52,28 @@ public class ExtendedEncoding implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
return Objects.equals(content, that.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(message);
|
||||
return Objects.hash(content);
|
||||
}
|
||||
|
||||
public static class Message implements Serializable {
|
||||
private static final long serialVersionUID = -2724977231484285467L;
|
||||
public interface Unpacker<T extends ExtendedType> {
|
||||
String getType();
|
||||
|
||||
private String subject;
|
||||
private String body;
|
||||
private List<InventoryVector> parents;
|
||||
private List<Attachment> files;
|
||||
|
||||
private Message() {
|
||||
parents = Collections.emptyList();
|
||||
files = Collections.emptyList();
|
||||
T unpack(MessageUnpacker unpacker, int size);
|
||||
}
|
||||
|
||||
private Message(Builder builder) {
|
||||
subject = builder.subject;
|
||||
body = builder.body;
|
||||
parents = Collections.unmodifiableList(builder.parents);
|
||||
files = Collections.unmodifiableList(builder.files);
|
||||
}
|
||||
public interface ExtendedType extends Serializable {
|
||||
String getType();
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public List<InventoryVector> getParents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
public List<Attachment> 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<InventoryVector> parents = new LinkedList<>();
|
||||
private List<Attachment> 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.?)
|
||||
void pack(MessagePacker packer) throws IOException;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package ch.dissem.bitmessage.entity.valueobject;
|
||||
package ch.dissem.bitmessage.entity.valueobject.extended;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Arrays;
|
||||
@ -48,7 +48,7 @@ public class Attachment implements Serializable {
|
||||
return Objects.hash(name, data, type, disposition);
|
||||
}
|
||||
|
||||
private enum Disposition {
|
||||
public enum Disposition {
|
||||
inline, attachment
|
||||
}
|
||||
|
||||
@ -83,6 +83,11 @@ public class Attachment implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder disposition(Disposition disposition) {
|
||||
this.disposition = disposition;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Attachment build() {
|
||||
Attachment attachment = new Attachment();
|
||||
attachment.type = this.type;
|
@ -0,0 +1,257 @@
|
||||
package ch.dissem.bitmessage.entity.valueobject.extended;
|
||||
|
||||
import ch.dissem.bitmessage.entity.Plaintext;
|
||||
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import org.msgpack.core.MessagePacker;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Files;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Extended encoding type 'message'. Properties 'parents' and 'files' not yet supported by PyBitmessage, so they might not work
|
||||
* properly with future PyBitmessage implementations.
|
||||
*/
|
||||
public class Message implements ExtendedEncoding.ExtendedType {
|
||||
private static final long serialVersionUID = -2724977231484285467L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Message.class);
|
||||
|
||||
public static final String TYPE = "message";
|
||||
|
||||
private String subject;
|
||||
private String body;
|
||||
private List<InventoryVector> parents;
|
||||
private List<Attachment> files;
|
||||
|
||||
private Message(Builder builder) {
|
||||
subject = builder.subject;
|
||||
body = builder.body;
|
||||
parents = Collections.unmodifiableList(builder.parents);
|
||||
files = Collections.unmodifiableList(builder.files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public String getSubject() {
|
||||
return subject;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public List<InventoryVector> getParents() {
|
||||
return parents;
|
||||
}
|
||||
|
||||
public List<Attachment> 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 {
|
||||
int size = 3;
|
||||
if (!files.isEmpty()) {
|
||||
size++;
|
||||
}
|
||||
if (!parents.isEmpty()) {
|
||||
size++;
|
||||
}
|
||||
packer.packMapHeader(size);
|
||||
packer.packString("");
|
||||
packer.packString("message");
|
||||
packer.packString("subject");
|
||||
packer.packString(subject);
|
||||
packer.packString("body");
|
||||
packer.packString(body);
|
||||
if (!files.isEmpty()) {
|
||||
packer.packString("files");
|
||||
packer.packArrayHeader(files.size());
|
||||
for (Attachment file : files) {
|
||||
packer.packMapHeader(4);
|
||||
packer.packString("name");
|
||||
packer.packString(file.getName());
|
||||
packer.packString("data");
|
||||
packer.packBinaryHeader(file.getData().length);
|
||||
packer.writePayload(file.getData());
|
||||
packer.packString("type");
|
||||
packer.packString(file.getType());
|
||||
packer.packString("disposition");
|
||||
packer.packString(file.getDisposition().name());
|
||||
}
|
||||
}
|
||||
if (!parents.isEmpty()) {
|
||||
packer.packString("parents");
|
||||
packer.packArrayHeader(parents.size());
|
||||
for (InventoryVector parent : parents) {
|
||||
packer.packBinaryHeader(parent.getHash().length);
|
||||
packer.writePayload(parent.getHash());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String subject;
|
||||
private String body;
|
||||
private List<InventoryVector> parents = new LinkedList<>();
|
||||
private List<Attachment> files = new LinkedList<>();
|
||||
|
||||
public Builder subject(String subject) {
|
||||
this.subject = subject;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder body(String body) {
|
||||
this.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParent(Plaintext parent) {
|
||||
parents.add(parent.getInventoryVector());
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addParent(InventoryVector iv) {
|
||||
parents.add(iv);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addFile(File file, Attachment.Disposition disposition) {
|
||||
try {
|
||||
files.add(new Attachment.Builder()
|
||||
.name(file.getName())
|
||||
.disposition(disposition)
|
||||
.type(URLConnection.guessContentTypeFromStream(new FileInputStream(file)))
|
||||
.data(Files.readAllBytes(file.toPath()))
|
||||
.build());
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addFile(Attachment file) {
|
||||
files.add(file);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExtendedEncoding build() {
|
||||
return new ExtendedEncoding(new Message(this));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Unpacker implements ExtendedEncoding.Unpacker<Message> {
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message unpack(MessageUnpacker unpacker, int size) {
|
||||
Message.Builder builder = new Message.Builder();
|
||||
try {
|
||||
for (int i = 0; i < size; i++) {
|
||||
String key = unpacker.unpackString();
|
||||
switch (key) {
|
||||
case "subject":
|
||||
builder.subject(unpacker.unpackString());
|
||||
break;
|
||||
case "body":
|
||||
builder.body(unpacker.unpackString());
|
||||
break;
|
||||
case "parents":
|
||||
builder.parents = unpackParents(unpacker);
|
||||
break;
|
||||
case "files":
|
||||
builder.files = unpackFiles(unpacker);
|
||||
break;
|
||||
default:
|
||||
LOG.error("Unexpected data with key: " + key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
return new Message(builder);
|
||||
}
|
||||
|
||||
private static List<InventoryVector> unpackParents(MessageUnpacker unpacker) throws IOException {
|
||||
int size = unpacker.unpackArrayHeader();
|
||||
List<InventoryVector> parents = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
int binarySize = unpacker.unpackBinaryHeader();
|
||||
parents.add(new InventoryVector(unpacker.readPayload(binarySize)));
|
||||
}
|
||||
return parents;
|
||||
}
|
||||
|
||||
private static List<Attachment> unpackFiles(MessageUnpacker unpacker) throws IOException {
|
||||
int size = unpacker.unpackArrayHeader();
|
||||
List<Attachment> files = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Attachment.Builder attachment = new Attachment.Builder();
|
||||
int mapSize = unpacker.unpackMapHeader();
|
||||
for (int j = 0; j < mapSize; j++) {
|
||||
String key = unpacker.unpackString();
|
||||
switch (key) {
|
||||
case "name":
|
||||
attachment.name(unpacker.unpackString());
|
||||
break;
|
||||
case "data":
|
||||
int binarySize = unpacker.unpackBinaryHeader();
|
||||
attachment.data(unpacker.readPayload(binarySize));
|
||||
break;
|
||||
case "type":
|
||||
attachment.type(unpacker.unpackString());
|
||||
break;
|
||||
case "disposition":
|
||||
String disposition = unpacker.unpackString();
|
||||
switch (disposition) {
|
||||
case "inline":
|
||||
attachment.inline();
|
||||
break;
|
||||
case "attachment":
|
||||
attachment.attachment();
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unknown disposition: " + disposition);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG.debug("Unknown file info '" + key + "' with data: " + unpacker.unpackValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
files.add(attachment.build());
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,131 @@
|
||||
package ch.dissem.bitmessage.entity.valueobject.extended;
|
||||
|
||||
import ch.dissem.bitmessage.entity.Plaintext;
|
||||
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import org.msgpack.core.MessagePacker;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Extended encoding type 'vote'. Specification still outstanding, so this will need some work.
|
||||
*/
|
||||
public class Vote implements ExtendedEncoding.ExtendedType {
|
||||
private static final long serialVersionUID = -8427038604209964837L;
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Vote.class);
|
||||
|
||||
public static final String TYPE = "vote";
|
||||
|
||||
private InventoryVector msgId;
|
||||
private String vote;
|
||||
|
||||
private Vote(Builder builder) {
|
||||
msgId = builder.msgId;
|
||||
vote = builder.vote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public InventoryVector getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public String getVote() {
|
||||
return vote;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Vote vote1 = (Vote) o;
|
||||
return Objects.equals(msgId, vote1.msgId) &&
|
||||
Objects.equals(vote, vote1.vote);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(msgId, vote);
|
||||
}
|
||||
|
||||
public void pack(MessagePacker packer) throws IOException {
|
||||
packer.packMapHeader(3);
|
||||
packer.packString("");
|
||||
packer.packString("vote");
|
||||
packer.packString("msgId");
|
||||
packer.packBinaryHeader(msgId.getHash().length);
|
||||
packer.writePayload(msgId.getHash());
|
||||
packer.packString("vote");
|
||||
packer.packString(vote);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private InventoryVector msgId;
|
||||
private String vote;
|
||||
|
||||
public ExtendedEncoding up(Plaintext message) {
|
||||
msgId = message.getInventoryVector();
|
||||
vote = "1";
|
||||
return new ExtendedEncoding(new Vote(this));
|
||||
}
|
||||
|
||||
public ExtendedEncoding down(Plaintext message) {
|
||||
msgId = message.getInventoryVector();
|
||||
vote = "1";
|
||||
return new ExtendedEncoding(new Vote(this));
|
||||
}
|
||||
|
||||
public Builder msgId(InventoryVector iv) {
|
||||
this.msgId = iv;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder vote(String vote) {
|
||||
this.vote = vote;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExtendedEncoding build() {
|
||||
return new ExtendedEncoding(new Vote(this));
|
||||
}
|
||||
}
|
||||
|
||||
public static class Unpacker implements ExtendedEncoding.Unpacker<Vote> {
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vote unpack(MessageUnpacker unpacker, int size) {
|
||||
Vote.Builder builder = new Vote.Builder();
|
||||
try {
|
||||
for (int i = 0; i < size; i++) {
|
||||
String key = unpacker.unpackString();
|
||||
switch (key) {
|
||||
case "msgId":
|
||||
int binarySize = unpacker.unpackBinaryHeader();
|
||||
builder.msgId(new InventoryVector(unpacker.readPayload(binarySize)));
|
||||
break;
|
||||
case "vote":
|
||||
builder.vote(unpacker.unpackString());
|
||||
break;
|
||||
default:
|
||||
LOG.error("Unexpected data with key: " + key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
return new Vote(builder);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
package ch.dissem.bitmessage.factory;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.ExtendedEncoding;
|
||||
import ch.dissem.bitmessage.entity.valueobject.extended.Message;
|
||||
import ch.dissem.bitmessage.entity.valueobject.extended.Vote;
|
||||
import ch.dissem.bitmessage.exception.ApplicationException;
|
||||
import org.msgpack.core.MessagePack;
|
||||
import org.msgpack.core.MessageUnpacker;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
/**
|
||||
* Factory that creates {@link ExtendedEncoding} objects from byte arrays. You can register your own types by adding a
|
||||
* {@link ExtendedEncoding.Unpacker} using {@link #registerFactory(ExtendedEncoding.Unpacker)}.
|
||||
*/
|
||||
public class ExtendedEncodingFactory {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExtendedEncodingFactory.class);
|
||||
private static final ExtendedEncodingFactory INSTANCE = new ExtendedEncodingFactory();
|
||||
private Map<String, ExtendedEncoding.Unpacker<?>> factories = new HashMap<>();
|
||||
|
||||
private ExtendedEncodingFactory() {
|
||||
registerFactory(new Message.Unpacker());
|
||||
registerFactory(new Vote.Unpacker());
|
||||
}
|
||||
|
||||
public void registerFactory(ExtendedEncoding.Unpacker<?> factory) {
|
||||
factories.put(factory.getType(), factory);
|
||||
}
|
||||
|
||||
|
||||
public ExtendedEncoding unzip(byte[] zippedData) {
|
||||
try (InflaterInputStream unzipper = new InflaterInputStream(new ByteArrayInputStream(zippedData))) {
|
||||
MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(unzipper);
|
||||
int mapSize = unpacker.unpackMapHeader();
|
||||
String key = unpacker.unpackString();
|
||||
if (!"".equals(key)) {
|
||||
LOG.error("Unexpected content: " + key);
|
||||
return null;
|
||||
}
|
||||
String type = unpacker.unpackString();
|
||||
ExtendedEncoding.Unpacker<?> factory = factories.get(type);
|
||||
return new ExtendedEncoding(factory.unpack(unpacker, mapSize - 1));
|
||||
} catch (IOException e) {
|
||||
throw new ApplicationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static ExtendedEncodingFactory getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
@ -16,6 +16,11 @@
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
/**
|
||||
* A helper class for working with byte arrays interpreted as unsigned big endian integers.
|
||||
* This is one part due to the fact that Java doesn't support unsigned numbers, and another
|
||||
|
File diff suppressed because one or more lines are too long
@ -17,9 +17,9 @@
|
||||
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.entity.valueobject.extended.Message;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
import ch.dissem.bitmessage.utils.TestBase;
|
||||
import ch.dissem.bitmessage.utils.TestUtils;
|
||||
@ -31,7 +31,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
||||
import static ch.dissem.bitmessage.entity.Plaintext.Type.MSG;
|
||||
import static ch.dissem.bitmessage.utils.Singleton.cryptography;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SerializationTest extends TestBase {
|
||||
@ -104,7 +103,7 @@ public class SerializationTest extends TestBase {
|
||||
Plaintext p1 = new Plaintext.Builder(MSG)
|
||||
.from(TestUtils.loadIdentity("BM-2cSqjfJ8xK6UUn5Rw3RpdGQ9RsDkBhWnS8"))
|
||||
.to(TestUtils.loadContact())
|
||||
.message(new ExtendedEncoding.Builder().message()
|
||||
.message(new Message.Builder()
|
||||
.subject("Subject")
|
||||
.body("Message")
|
||||
.build())
|
||||
@ -154,7 +153,7 @@ public class SerializationTest extends TestBase {
|
||||
public void ensureNetworkMessageIsSerializedAndDeserializedCorrectly() throws Exception {
|
||||
ArrayList<InventoryVector> ivs = new ArrayList<>(50000);
|
||||
for (int i = 0; i < 50000; i++) {
|
||||
ivs.add(new InventoryVector(cryptography().randomBytes(32)));
|
||||
ivs.add(TestUtils.randomInventoryVector());
|
||||
}
|
||||
|
||||
Inv inv = new Inv.Builder().inventory(ivs).build();
|
||||
|
@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
||||
import ch.dissem.bitmessage.entity.payload.V4Pubkey;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||
import ch.dissem.bitmessage.factory.Factory;
|
||||
@ -28,6 +29,7 @@ import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
@ -35,6 +37,8 @@ import static org.junit.Assert.assertEquals;
|
||||
* If there's ever a need for this in production code, it should be rewritten to be more efficient.
|
||||
*/
|
||||
public class TestUtils {
|
||||
public static final Random RANDOM = new Random();
|
||||
|
||||
public static byte[] int16(int number) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Encode.int16(number, out);
|
||||
@ -59,6 +63,12 @@ public class TestUtils {
|
||||
return out.toByteArray();
|
||||
}
|
||||
|
||||
public static InventoryVector randomInventoryVector() {
|
||||
byte[] bytes = new byte[32];
|
||||
RANDOM.nextBytes(bytes);
|
||||
return new InventoryVector(bytes);
|
||||
}
|
||||
|
||||
public static InputStream getResource(String resourceName) {
|
||||
return TestUtils.class.getClassLoader().getResourceAsStream(resourceName);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||
import ch.dissem.bitmessage.utils.TestUtils;
|
||||
import ch.dissem.bitmessage.utils.UnixTime;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -162,7 +163,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
||||
@Test
|
||||
public void testSave() throws Exception {
|
||||
Plaintext message = new Plaintext.Builder(MSG)
|
||||
.IV(new InventoryVector(cryptography().randomBytes(32)))
|
||||
.IV(TestUtils.randomInventoryVector())
|
||||
.from(identity)
|
||||
.to(contactA)
|
||||
.message("Subject", "Message")
|
||||
@ -185,7 +186,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
||||
public void testUpdate() throws Exception {
|
||||
List<Plaintext> messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
||||
Plaintext message = messages.get(0);
|
||||
message.setInventoryVector(new InventoryVector(cryptography().randomBytes(32)));
|
||||
message.setInventoryVector(TestUtils.randomInventoryVector());
|
||||
repo.save(message);
|
||||
|
||||
messages = repo.findMessages(Plaintext.Status.DRAFT, contactA);
|
||||
@ -206,7 +207,7 @@ public class JdbcMessageRepositoryTest extends TestBase {
|
||||
@Test
|
||||
public void ensureUnacknowledgedMessagesAreFoundForResend() throws Exception {
|
||||
Plaintext message = new Plaintext.Builder(MSG)
|
||||
.IV(new InventoryVector(cryptography().randomBytes(32)))
|
||||
.IV(TestUtils.randomInventoryVector())
|
||||
.from(identity)
|
||||
.to(contactA)
|
||||
.message("Subject", "Message")
|
||||
|
Loading…
Reference in New Issue
Block a user