Implemented sending messages (and fixed a few bugs on the way)

This closes issue #3
This commit is contained in:
Christian Basler 2015-05-29 13:17:00 +02:00
parent 3d618ffeb4
commit 274c16b748
29 changed files with 357 additions and 138 deletions

View File

@ -9,6 +9,9 @@ import ch.dissem.bitmessage.repository.JdbcAddressRepository;
import ch.dissem.bitmessage.repository.JdbcInventory;
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
import ch.dissem.bitmessage.repository.JdbcNodeRegistry;
import ch.dissem.bitmessage.utils.Strings;
import org.flywaydb.core.internal.util.logging.slf4j.Slf4jLog;
import org.flywaydb.core.internal.util.logging.slf4j.Slf4jLogCreator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -113,6 +116,7 @@ public class Application {
switch (command) {
case "a":
addIdentity();
identities = ctx.addresses().getIdentities();
break;
case "b":
return;
@ -164,6 +168,7 @@ public class Application {
switch (command) {
case "a":
addContact();
contacts = ctx.addresses().getContacts();
break;
case "b":
return;
@ -201,12 +206,18 @@ public class Application {
System.out.println(address.getAddress());
System.out.println("Stream: " + address.getStream());
System.out.println("Version: " + address.getVersion());
if (address.getPrivateKey() == null) {
if (address.getPubkey() != null) {
System.out.println("Public key available");
} else {
System.out.println("Public key still missing");
}
}
}
private void messages() {
String command;
List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.NEW);
List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED);
do {
System.out.println();
int i = 0;
@ -247,6 +258,8 @@ public class Application {
System.out.println();
System.out.println(message.getText());
System.out.println();
System.out.println("Labels: "+ message.getLabels());
System.out.println();
String command;
do {
System.out.println("r) reply");
@ -269,14 +282,79 @@ public class Application {
private void compose() {
System.out.println();
System.out.println("TODO");
// TODO
BitmessageAddress from = selectAddress(true);
BitmessageAddress to = selectAddress(false);
compose(from, to, null);
}
private BitmessageAddress selectAddress(boolean id) {
List<BitmessageAddress> identities = (id ? ctx.addresses().getIdentities() : ctx.addresses().getContacts());
while (identities.size() == 0) {
addIdentity();
identities = ctx.addresses().getIdentities();
}
if (identities.size() == 1) {
return identities.get(0);
}
String command;
do {
System.out.println();
if (id) {
System.out.println("From:");
} else {
System.out.println("To:");
}
int i = 0;
for (BitmessageAddress identity : identities) {
i++;
System.out.print(i + ") ");
if (identity.getAlias() != null) {
System.out.println(identity.getAlias() + " (" + identity.getAddress() + ")");
} else {
System.out.println(identity.getAddress());
}
}
System.out.println("b) back");
command = nextCommand();
switch (command) {
case "b":
return null;
default:
try {
int index = Integer.parseInt(command) - 1;
if (identities.get(index) != null) {
return identities.get(index);
}
} catch (NumberFormatException e) {
System.out.println("Unknown command. Please try again.");
}
}
} while (!"b".equals(command));
return null;
}
private void compose(BitmessageAddress from, BitmessageAddress to, String subject) {
System.out.println();
System.out.println("TODO");
// TODO
System.out.println("From: " + from);
System.out.println("To: " + to);
if (subject != null) {
System.out.println("Subject: " + subject);
} else {
System.out.print("Subject: ");
subject = nextCommand();
}
System.out.println("Message:");
StringBuilder message = new StringBuilder();
String line;
do {
line = nextCommand();
message.append(line).append('\n');
} while (line.length() > 0 || !yesNo("Send message?"));
ctx.send(from, to, subject, message.toString());
}
private boolean yesNo(String question) {

View File

@ -20,9 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.Plaintext.Encoding;
import ch.dissem.bitmessage.entity.payload.GetPubkey;
import ch.dissem.bitmessage.entity.payload.Msg;
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
import ch.dissem.bitmessage.ports.*;
@ -32,6 +30,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.TreeSet;
@ -40,7 +39,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
/**
* Use this class if you want to create a Bitmessage client.
* <p>
* <p/>
* You'll need the Builder to create a BitmessageContext, and set the following properties:
* <ul>
* <li>addressRepo</li>
@ -51,7 +50,7 @@ import static ch.dissem.bitmessage.utils.UnixTime.DAY;
* <li>streams</li>
* </ul>
* The default implementations in the different module builds can be used.
* <p>
* <p/>
* The port defaults to 8444 (the default Bitmessage port)
*/
public class BitmessageContext {
@ -81,6 +80,7 @@ public class BitmessageContext {
features
));
ctx.getAddressRepo().save(identity);
// TODO: this should happen in a separate thread
ctx.sendPubkey(identity, identity.getStream());
return identity;
}
@ -93,6 +93,7 @@ public class BitmessageContext {
if (from.getPrivateKey() == null) {
throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key.");
}
// TODO: all this should happen in a separate thread
Plaintext msg = new Plaintext.Builder()
.from(from)
.to(to)
@ -100,10 +101,15 @@ public class BitmessageContext {
.message(subject, message)
.build();
if (to.getPubkey() == null) {
tryToFindMatchingPubkey(to);
}
if (to.getPubkey() == null) {
LOG.info("Public key is missing from recipient. Requesting.");
requestPubkey(from, to);
msg.setStatus(PUBKEY_REQUESTED);
ctx.getMessageRepository().save(msg);
} else {
LOG.info("Sending message.");
msg.setStatus(DOING_PROOF_OF_WORK);
ctx.getMessageRepository().save(msg);
ctx.send(
@ -130,13 +136,12 @@ public class BitmessageContext {
);
}
private void send(long stream, long version, ObjectPayload payload, long timeToLive) {
private void send(long stream, ObjectPayload payload, long timeToLive) {
try {
long expires = UnixTime.now(+timeToLive);
LOG.info("Expires at " + expires);
ObjectMessage object = new ObjectMessage.Builder()
.stream(stream)
.version(version)
.expiresTime(expires)
.payload(payload)
.build();
@ -159,9 +164,40 @@ public class BitmessageContext {
public void addContact(BitmessageAddress contact) {
ctx.getAddressRepo().save(contact);
// TODO: search pubkey in inventory
tryToFindMatchingPubkey(contact);
if (contact.getPubkey() == null) {
ctx.requestPubkey(contact);
}
}
private void tryToFindMatchingPubkey(BitmessageAddress address) {
for (ObjectMessage object : ctx.getInventory().getObjects(address.getStream(), address.getVersion(), ObjectType.PUBKEY)) {
try {
Pubkey pubkey = (Pubkey) object.getPayload();
if (address.getVersion() == 4) {
V4Pubkey v4Pubkey = (V4Pubkey) pubkey;
if (Arrays.equals(address.getTag(), v4Pubkey.getTag())) {
v4Pubkey.decrypt(address.getPubkeyDecryptionKey());
if (object.isSignatureValid(v4Pubkey)) {
address.setPubkey(v4Pubkey);
ctx.getAddressRepo().save(address);
break;
} else {
LOG.debug("Found pubkey for " + address + " but signature is invalid");
}
}
} else {
if (Arrays.equals(pubkey.getRipe(), address.getRipe())) {
address.setPubkey(pubkey);
ctx.getAddressRepo().save(address);
break;
}
}
} catch (Exception e) {
LOG.debug(e.getMessage(), e);
}
}
}
public interface Listener {
void receive(Plaintext plaintext);

View File

@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.ObjectMessage;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.payload.*;
import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.exception.DecryptionFailedException;
import ch.dissem.bitmessage.ports.NetworkHandler;
import org.slf4j.Logger;
@ -28,9 +29,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import static ch.dissem.bitmessage.entity.Plaintext.Status.DOING_PROOF_OF_WORK;
import static ch.dissem.bitmessage.entity.Plaintext.Status.NEW;
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
import static ch.dissem.bitmessage.entity.Plaintext.Status.*;
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
class DefaultMessageListener implements NetworkHandler.MessageListener {
@ -107,6 +106,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
msg.setStatus(SENT);
ctx.getMessageRepository().save(msg);
}
ctx.getAddressRepo().save(address);
}
} catch (DecryptionFailedException ignore) {
LOG.debug(ignore.getMessage(), ignore);
@ -119,7 +119,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
msg.getPlaintext().setTo(identity);
object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey());
msg.getPlaintext().setStatus(NEW);
msg.getPlaintext().setStatus(RECEIVED);
msg.getPlaintext().addLabels(ctx.getMessageRepository().getLabels(Label.Type.INBOX, Label.Type.UNREAD));
ctx.getMessageRepository().save(msg.getPlaintext());
listener.receive(msg.getPlaintext());
break;

View File

@ -145,17 +145,16 @@ public class InternalContext {
LOG.info("Expires at " + expires);
ObjectMessage object = new ObjectMessage.Builder()
.stream(to.getStream())
.version(to.getVersion())
.expiresTime(expires)
.payload(payload)
.build();
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
if (object.isSigned()) {
object.sign(from.getPrivateKey());
}
if (object instanceof Encrypted) {
if (payload instanceof Encrypted) {
object.encrypt(to.getPubkey());
}
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
inventory.storeObject(object);
networkHandler.offer(object.getInventoryVector());
} catch (IOException e) {
@ -169,7 +168,6 @@ public class InternalContext {
LOG.info("Expires at " + expires);
ObjectMessage response = new ObjectMessage.Builder()
.stream(targetStream)
.version(identity.getVersion())
.expiresTime(expires)
.payload(identity.getPubkey())
.build();
@ -196,7 +194,6 @@ public class InternalContext {
LOG.info("Expires at " + expires);
ObjectMessage response = new ObjectMessage.Builder()
.stream(contact.getStream())
.version(contact.getVersion())
.expiresTime(expires)
.payload(new GetPubkey(contact))
.build();

View File

@ -50,7 +50,7 @@ public class ObjectMessage implements MessagePayload {
nonce = builder.nonce;
expiresTime = builder.expiresTime;
objectType = builder.objectType;
version = builder.version;
version = builder.payload.getVersion();
stream = builder.streamNumber;
payload = builder.payload;
}
@ -177,7 +177,6 @@ public class ObjectMessage implements MessagePayload {
private byte[] nonce;
private long expiresTime;
private long objectType = -1;
private long version = -1;
private long streamNumber;
private ObjectPayload payload;
@ -204,11 +203,6 @@ public class ObjectMessage implements MessagePayload {
return this;
}
public Builder version(long version) {
this.version = version;
return this;
}
public Builder stream(long streamNumber) {
this.streamNumber = streamNumber;
return this;

View File

@ -16,7 +16,6 @@
package ch.dissem.bitmessage.entity;
import ch.dissem.bitmessage.entity.payload.Pubkey;
import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.factory.Factory;
import ch.dissem.bitmessage.utils.Decode;
@ -129,10 +128,14 @@ public class Plaintext implements Streamable {
Encode.varInt(ack.length, out);
out.write(ack);
if (includeSignature) {
if (signature == null) {
Encode.varInt(0, out);
} else {
Encode.varInt(signature.length, out);
out.write(signature);
}
}
}
@Override
public void write(OutputStream out) throws IOException {
@ -188,6 +191,40 @@ public class Plaintext implements Streamable {
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Plaintext plaintext = (Plaintext) o;
return Objects.equals(encoding, plaintext.encoding) &&
Objects.equals(from, plaintext.from) &&
Arrays.equals(message, plaintext.message) &&
Arrays.equals(ack, plaintext.ack) &&
Arrays.equals(to.getRipe(), plaintext.to.getRipe()) &&
Arrays.equals(signature, plaintext.signature) &&
Objects.equals(status, plaintext.status) &&
Objects.equals(sent, plaintext.sent) &&
Objects.equals(received, plaintext.received) &&
Objects.equals(labels, plaintext.labels);
}
@Override
public int hashCode() {
return Objects.hash(from, encoding, message, ack, to, signature, status, sent, received, labels);
}
public void addLabels(Label... labels) {
if (labels != null) {
Collections.addAll(this.labels, labels);
}
}
public void addLabels(Collection<Label> labels) {
if (labels != null) {
this.labels.addAll(labels);
}
}
public enum Encoding {
IGNORE(0), TRIVIAL(1), SIMPLE(2);
@ -203,15 +240,13 @@ public class Plaintext implements Streamable {
}
public enum Status {
DRAFT,
// For sent messages
PUBKEY_REQUESTED,
DOING_PROOF_OF_WORK,
SENT,
SENT_ACKNOWLEDGED,
// For received messages
NEW,
READ
RECEIVED
}
public static final class Builder {
@ -233,7 +268,7 @@ public class Plaintext implements Streamable {
private long sent;
private long received;
private Status status;
private Set<Label> labels = new TreeSet<>();
private Set<Label> labels = new HashSet<>();
public Builder() {
}
@ -365,26 +400,4 @@ public class Plaintext implements Streamable {
return new Plaintext(this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Plaintext plaintext = (Plaintext) o;
return Objects.equals(encoding, plaintext.encoding) &&
Objects.equals(from, plaintext.from) &&
Arrays.equals(message, plaintext.message) &&
Arrays.equals(ack, plaintext.ack) &&
Arrays.equals(to.getRipe(), plaintext.to.getRipe()) &&
Arrays.equals(signature, plaintext.signature) &&
Objects.equals(status, plaintext.status) &&
Objects.equals(sent, plaintext.sent) &&
Objects.equals(received, plaintext.received) &&
Objects.equals(labels, plaintext.labels);
}
@Override
public int hashCode() {
return Objects.hash(from, encoding, message, ack, to, signature, status, sent, received, labels);
}
}

View File

@ -31,7 +31,8 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted {
protected CryptoBox encrypted;
protected Plaintext plaintext;
protected Broadcast(long stream, CryptoBox encrypted, Plaintext plaintext) {
protected Broadcast(long version, long stream, CryptoBox encrypted, Plaintext plaintext) {
super(version);
this.stream = stream;
this.encrypted = encrypted;
this.plaintext = plaintext;

View File

@ -184,7 +184,7 @@ public class CryptoBox implements Streamable {
}
public Builder curveType(int curveType) {
if (curveType != 0x2CA) LOG.error("Unexpected curve type " + curveType);
if (curveType != 0x2CA) LOG.debug("Unexpected curve type " + curveType);
this.curveType = curveType;
return this;
}

View File

@ -31,13 +31,14 @@ public class GenericPayload extends ObjectPayload {
private long stream;
private byte[] data;
public GenericPayload(long stream, byte[] data) {
public GenericPayload(long version, long stream, byte[] data) {
super(version);
this.stream = stream;
this.data = data;
}
public static GenericPayload read(InputStream is, long stream, int length) throws IOException {
return new GenericPayload(stream, Decode.bytes(is, length));
public static GenericPayload read(long version, InputStream is, long stream, int length) throws IOException {
return new GenericPayload(version, stream, Decode.bytes(is, length));
}
@Override

View File

@ -31,6 +31,7 @@ public class GetPubkey extends ObjectPayload {
private byte[] ripeTag;
public GetPubkey(BitmessageAddress address) {
super(address.getVersion());
this.stream = address.getStream();
if (address.getVersion() < 4)
this.ripeTag = address.getRipe();
@ -38,13 +39,14 @@ public class GetPubkey extends ObjectPayload {
this.ripeTag = address.getTag();
}
private GetPubkey(long stream, long version, byte[] ripeOrTag) {
private GetPubkey(long version, long stream, byte[] ripeOrTag) {
super(version);
this.stream = stream;
this.ripeTag = ripeOrTag;
}
public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException {
return new GetPubkey(stream, version, Decode.bytes(is, length));
return new GetPubkey(version, stream, Decode.bytes(is, length));
}
/**

View File

@ -33,11 +33,13 @@ public class Msg extends ObjectPayload implements Encrypted {
private Plaintext plaintext;
private Msg(long stream, CryptoBox encrypted) {
super(1);
this.stream = stream;
this.encrypted = encrypted;
}
public Msg(Plaintext plaintext) {
super(1);
this.stream = plaintext.getStream();
this.plaintext = plaintext;
}

View File

@ -26,10 +26,21 @@ import java.io.OutputStream;
* The payload of an 'object' command. This is shared by the network.
*/
public abstract class ObjectPayload implements Streamable {
private final long version;
protected ObjectPayload(long version) {
this.version = version;
}
public abstract ObjectType getType();
public abstract long getStream();
public long getVersion() {
return version;
}
public boolean isSigned() {
return false;
}

View File

@ -29,12 +29,14 @@ import static ch.dissem.bitmessage.utils.Security.sha512;
public abstract class Pubkey extends ObjectPayload {
public final static long LATEST_VERSION = 4;
protected Pubkey(long version) {
super(version);
}
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) {
return ripemd160(sha512(publicSigningKey, publicEncryptionKey));
}
public abstract long getVersion();
public abstract byte[] getSigningKey();
public abstract byte[] getEncryptionKey();

View File

@ -32,10 +32,12 @@ public class V2Pubkey extends Pubkey {
protected byte[] publicSigningKey; // 64 Bytes
protected byte[] publicEncryptionKey; // 64 Bytes
protected V2Pubkey() {
protected V2Pubkey(long version) {
super(version);
}
private V2Pubkey(Builder builder) {
private V2Pubkey(long version, Builder builder) {
super(version);
stream = builder.streamNumber;
behaviorBitfield = builder.behaviorBitfield;
publicSigningKey = add0x04(builder.publicSigningKey);
@ -118,7 +120,7 @@ public class V2Pubkey extends Pubkey {
}
public V2Pubkey build() {
return new V2Pubkey(this);
return new V2Pubkey(2, this);
}
}
}

View File

@ -33,7 +33,8 @@ public class V3Pubkey extends V2Pubkey {
long extraBytes;
byte[] signature;
protected V3Pubkey(Builder builder) {
protected V3Pubkey(long version, Builder builder) {
super(version);
stream = builder.streamNumber;
behaviorBitfield = builder.behaviorBitfield;
publicSigningKey = add0x04(builder.publicSigningKey);
@ -95,6 +96,24 @@ public class V3Pubkey extends V2Pubkey {
this.signature = signature;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
V3Pubkey pubkey = (V3Pubkey) o;
return Objects.equals(nonceTrialsPerByte, pubkey.nonceTrialsPerByte) &&
Objects.equals(extraBytes, pubkey.extraBytes) &&
stream == pubkey.stream &&
behaviorBitfield == pubkey.behaviorBitfield &&
Arrays.equals(publicSigningKey, pubkey.publicSigningKey) &&
Arrays.equals(publicEncryptionKey, pubkey.publicEncryptionKey);
}
@Override
public int hashCode() {
return Objects.hash(nonceTrialsPerByte, extraBytes);
}
public static class Builder {
private long streamNumber;
private int behaviorBitfield;
@ -143,25 +162,7 @@ public class V3Pubkey extends V2Pubkey {
}
public V3Pubkey build() {
return new V3Pubkey(this);
return new V3Pubkey(3, this);
}
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
V3Pubkey pubkey = (V3Pubkey) o;
return Objects.equals(nonceTrialsPerByte, pubkey.nonceTrialsPerByte) &&
Objects.equals(extraBytes, pubkey.extraBytes) &&
stream == pubkey.stream &&
behaviorBitfield == pubkey.behaviorBitfield &&
Arrays.equals(publicSigningKey, pubkey.publicSigningKey) &&
Arrays.equals(publicEncryptionKey, pubkey.publicEncryptionKey);
}
@Override
public int hashCode() {
return Objects.hash(nonceTrialsPerByte, extraBytes);
}
}

View File

@ -25,12 +25,12 @@ import java.io.OutputStream;
* Broadcasts are version 4 or 5.
*/
public class V4Broadcast extends Broadcast {
protected V4Broadcast(long stream, CryptoBox encrypted) {
super(stream, encrypted, null);
protected V4Broadcast(long version, long stream, CryptoBox encrypted) {
super(version, stream, encrypted, null);
}
public static V4Broadcast read(InputStream in, long stream, int length) throws IOException {
return new V4Broadcast(stream, CryptoBox.read(in, length));
return new V4Broadcast(4, stream, CryptoBox.read(in, length));
}
@Override

View File

@ -39,12 +39,14 @@ public class V4Pubkey extends Pubkey implements Encrypted {
private V3Pubkey decrypted;
private V4Pubkey(long stream, byte[] tag, CryptoBox encrypted) {
super(4);
this.stream = stream;
this.tag = tag;
this.encrypted = encrypted;
}
public V4Pubkey(V3Pubkey decrypted) {
super(4);
this.decrypted = decrypted;
this.stream = decrypted.stream;
this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe());

View File

@ -29,7 +29,7 @@ public class V5Broadcast extends V4Broadcast {
private byte[] tag;
private V5Broadcast(long stream, byte[] tag, CryptoBox encrypted) {
super(stream, encrypted);
super(5, stream, encrypted);
this.tag = tag;
}

View File

@ -16,13 +16,17 @@
package ch.dissem.bitmessage.entity.valueobject;
import java.util.Objects;
public class Label {
private Object id;
private String label;
private Type type;
private int color;
public Label(String label, int color) {
public Label(String label, Type type, int color) {
this.label = label;
this.type = type;
this.color = color;
}
@ -45,4 +49,33 @@ public class Label {
public Object getId() {
return id;
}
public Type getType() {
return type;
}
public void setId(Object id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Label label1 = (Label) o;
return Objects.equals(label, label1.label);
}
@Override
public int hashCode() {
return Objects.hash(label);
}
public enum Type {
INBOX,
DRAFTS,
SENT,
UNREAD,
TRASH
}
}

View File

@ -124,7 +124,7 @@ public class Factory {
}
// fallback: just store the message - we don't really care what it is
// LOG.info("Unexpected object type: " + objectType);
return GenericPayload.read(stream, streamNumber, length);
return GenericPayload.read(version, stream, streamNumber, length);
}
private static ObjectPayload parseGetPubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
@ -146,7 +146,7 @@ public class Factory {
private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
Pubkey pubkey = readPubkey(version, streamNumber, stream, length, true);
return pubkey != null ? pubkey : GenericPayload.read(stream, streamNumber, length);
return pubkey != null ? pubkey : GenericPayload.read(version, stream, streamNumber, length);
}
private static ObjectPayload parseMsg(long version, long streamNumber, InputStream stream, int length) throws IOException {
@ -161,7 +161,7 @@ public class Factory {
return V5Broadcast.read(stream, streamNumber, length);
default:
LOG.debug("Encountered unknown broadcast version " + version);
return GenericPayload.read(stream, streamNumber, length);
return GenericPayload.read(version, stream, streamNumber, length);
}
}
}

View File

@ -95,14 +95,13 @@ class V3MessageFactory {
payload = Factory.getObjectPayload(objectType, version, stream, dataStream, data.length);
} catch (IOException e) {
LOG.trace("Could not parse object payload - using generic payload instead", e);
payload = new GenericPayload(stream, data);
payload = new GenericPayload(version, stream, data);
}
return new ObjectMessage.Builder()
.nonce(nonce)
.expiresTime(expiresTime)
.objectType(objectType)
.version(version)
.stream(stream)
.payload(payload)
.build();

View File

@ -24,7 +24,9 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
import java.util.List;
public interface MessageRepository {
List<String> getLabels();
List<Label> getLabels();
List<Label> getLabels(Label.Type... types);
List<Plaintext> findMessages(Label label);

View File

@ -19,7 +19,8 @@ package ch.dissem.bitmessage.utils;
import ch.dissem.bitmessage.entity.payload.ObjectType;
/**
* Created by chris on 13.04.15.
* Some utilities to handle strings.
* TODO: Probably this should be split in a GUI related and an SQL related utility class.
*/
public class Strings {
public static StringBuilder join(byte[]... objects) {
@ -49,6 +50,15 @@ public class Strings {
return streamList;
}
public static StringBuilder join(Enum... types) {
StringBuilder streamList = new StringBuilder();
for (int i = 0; i < types.length; i++) {
if (i > 0) streamList.append(", ");
streamList.append('\'').append(types[i].name()).append('\'');
}
return streamList;
}
public static StringBuilder join(Object... objects) {
StringBuilder streamList = new StringBuilder();
for (int i = 0; i < objects.length; i++) {

View File

@ -37,12 +37,12 @@ import static org.junit.Assert.assertTrue;
public class EncryptionTest {
@Test
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException {
GenericPayload before = new GenericPayload(1, Security.randomBytes(100));
GenericPayload before = new GenericPayload(0, 1, Security.randomBytes(100));
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
GenericPayload after = GenericPayload.read(cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 1, 100);
GenericPayload after = GenericPayload.read(0, cryptoBox.decrypt(privateKey.getPrivateEncryptionKey()), 1, 100);
assertEquals(before, after);
}

View File

@ -48,7 +48,6 @@ public class SignatureTest {
ObjectMessage objectMessage = new ObjectMessage.Builder()
.objectType(ObjectType.PUBKEY)
.stream(1)
.version(1)
.payload(privateKey.getPubkey())
.build();
objectMessage.sign(privateKey);

View File

@ -73,7 +73,7 @@ public class SecurityTest {
.nonce(new byte[8])
.expiresTime(UnixTime.now(+2 * DAY)) // 5 minutes
.objectType(0)
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
.build();
Security.checkProofOfWork(objectMessage, 1000, 1000);
}
@ -84,7 +84,7 @@ public class SecurityTest {
.nonce(new byte[8])
.expiresTime(UnixTime.now(+2 * DAY))
.objectType(0)
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
.build();
Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000);
Security.checkProofOfWork(objectMessage, 1000, 1000);

View File

@ -120,6 +120,7 @@ public class Connection implements Runnable {
+ msg.getPayload().getCommand());
}
}
if (socket.isClosed()) state = DISCONNECTED;
} catch (SocketTimeoutException ignore) {
if (state == ACTIVE) {
sendQueue();

View File

@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
import ch.dissem.bitmessage.entity.Plaintext;
import ch.dissem.bitmessage.entity.valueobject.Label;
import ch.dissem.bitmessage.ports.MessageRepository;
import ch.dissem.bitmessage.utils.Strings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -37,13 +38,41 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
private InternalContext ctx;
@Override
public List<String> getLabels() {
List<String> result = new LinkedList<>();
public List<Label> getLabels() {
List<Label> result = new LinkedList<>();
try {
Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT label FROM Label ORDER BY ord");
ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label ORDER BY ord");
while (rs.next()) {
result.add(rs.getString("label"));
result.add(getLabel(rs));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
}
return result;
}
private Label getLabel(ResultSet rs) throws SQLException {
String typeName = rs.getString("type");
Label.Type type = null;
if (typeName != null) {
type = Label.Type.valueOf(typeName);
}
Label label = new Label(rs.getString("label"), type, rs.getInt("color"));
label.setId(rs.getLong("id"));
return label;
}
@Override
public List<Label> getLabels(Label.Type... types) {
List<Label> result = new LinkedList<>();
try {
Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE type IN (" + Strings.join(types) +
") ORDER BY ord");
while (rs.next()) {
result.add(getLabel(rs));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);
@ -94,9 +123,9 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
List<Label> result = new ArrayList<>();
try {
Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT label, color FROM Label WHERE id IN (SELECT label_id FROM Message_Label WHERE message_id=" + messageId + ")");
ResultSet rs = stmt.executeQuery("SELECT id, label, type, color FROM Label WHERE id IN (SELECT label_id FROM Message_Label WHERE message_id=" + messageId + ")");
while (rs.next()) {
result.add(new Label(rs.getString("label"), rs.getInt("color")));
result.add(getLabel(rs));
}
} catch (SQLException e) {
LOG.error(e.getMessage(), e);

View File

@ -14,8 +14,10 @@ CREATE TABLE Message (
CREATE TABLE Label (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
label VARCHAR(255) NOT NULL,
color INT,
type VARCHAR(20),
color INT NOT NULL DEFAULT X'FF000000',
ord BIGINT,
CONSTRAINT UC_label UNIQUE (label),
CONSTRAINT UC_order UNIQUE (ord)
);
@ -29,7 +31,8 @@ CREATE TABLE Message_Label (
FOREIGN KEY (label_id) REFERENCES Label (id)
);
INSERT INTO Label(label, ord) VALUES ('Inbox', 0);
INSERT INTO Label(label, ord) VALUES ('Sent', 10);
INSERT INTO Label(label, ord) VALUES ('Drafts', 20);
INSERT INTO Label(label, ord) VALUES ('Trash', 100);
INSERT INTO Label(label, type, color, ord) VALUES ('Inbox', 'INBOX', X'FF0000FF', 0);
INSERT INTO Label(label, type, color, ord) VALUES ('Drafts', 'DRAFTS', X'FFFF9900', 10);
INSERT INTO Label(label, type, color, ord) VALUES ('Sent', 'SENT', X'FFFFFF00', 20);
INSERT INTO Label(label, type, ord) VALUES ('Unread', 'UNREAD', 90);
INSERT INTO Label(label, type, ord) VALUES ('Trash', 'TRASH', 100);