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,18 +39,18 @@ 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>
* <li>inventory</li>
* <li>nodeRegistry</li>
* <li>networkHandler</li>
* <li>messageRepo</li>
* <li>streams</li>
* <li>addressRepo</li>
* <li>inventory</li>
* <li>nodeRegistry</li>
* <li>networkHandler</li>
* <li>messageRepo</li>
* <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,8 +164,39 @@ public class BitmessageContext {
public void addContact(BitmessageAddress contact) {
ctx.getAddressRepo().save(contact);
// TODO: search pubkey in inventory
ctx.requestPubkey(contact);
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 {

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,8 +128,12 @@ public class Plaintext implements Streamable {
Encode.varInt(ack.length, out);
out.write(ack);
if (includeSignature) {
Encode.varInt(signature.length, out);
out.write(signature);
if (signature == null) {
Encode.varInt(0, out);
} else {
Encode.varInt(signature.length, out);
out.write(signature);
}
}
}
@ -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();
@ -160,7 +161,7 @@ public class Connection implements Runnable {
ctx.getInventory().storeObject(objectMessage);
} catch (InsufficientProofOfWorkException e) {
try {
File f = new File(System.getProperty("user.home")+"/jabit.error/" + objectMessage.getInventoryVector() + ".inv");
File f = new File(System.getProperty("user.home") + "/jabit.error/" + objectMessage.getInventoryVector() + ".inv");
f.createNewFile();
objectMessage.write(new FileOutputStream(f));
} catch (IOException e1) {

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

@ -1,21 +1,23 @@
CREATE TABLE Message (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
sender VARCHAR(40) NOT NULL,
recipient VARCHAR(40) NOT NULL,
data BLOB NOT NULL,
sent BIGINT,
received BIGINT,
status VARCHAR(20) NOT NULL,
id BIGINT AUTO_INCREMENT PRIMARY KEY,
sender VARCHAR(40) NOT NULL,
recipient VARCHAR(40) NOT NULL,
data BLOB NOT NULL,
sent BIGINT,
received BIGINT,
status VARCHAR(20) NOT NULL,
FOREIGN KEY (sender) REFERENCES Address (address),
FOREIGN KEY (recipient) REFERENCES Address (address)
);
CREATE TABLE Label (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
label VARCHAR(255) NOT NULL,
color INT,
ord BIGINT,
id BIGINT AUTO_INCREMENT PRIMARY KEY,
label VARCHAR(255) NOT NULL,
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);