Implemented sending messages (and fixed a few bugs on the way)
This closes issue #3
This commit is contained in:
parent
3d618ffeb4
commit
274c16b748
@ -9,6 +9,9 @@ import ch.dissem.bitmessage.repository.JdbcAddressRepository;
|
|||||||
import ch.dissem.bitmessage.repository.JdbcInventory;
|
import ch.dissem.bitmessage.repository.JdbcInventory;
|
||||||
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
|
import ch.dissem.bitmessage.repository.JdbcMessageRepository;
|
||||||
import ch.dissem.bitmessage.repository.JdbcNodeRegistry;
|
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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -113,6 +116,7 @@ public class Application {
|
|||||||
switch (command) {
|
switch (command) {
|
||||||
case "a":
|
case "a":
|
||||||
addIdentity();
|
addIdentity();
|
||||||
|
identities = ctx.addresses().getIdentities();
|
||||||
break;
|
break;
|
||||||
case "b":
|
case "b":
|
||||||
return;
|
return;
|
||||||
@ -164,6 +168,7 @@ public class Application {
|
|||||||
switch (command) {
|
switch (command) {
|
||||||
case "a":
|
case "a":
|
||||||
addContact();
|
addContact();
|
||||||
|
contacts = ctx.addresses().getContacts();
|
||||||
break;
|
break;
|
||||||
case "b":
|
case "b":
|
||||||
return;
|
return;
|
||||||
@ -201,12 +206,18 @@ public class Application {
|
|||||||
System.out.println(address.getAddress());
|
System.out.println(address.getAddress());
|
||||||
System.out.println("Stream: " + address.getStream());
|
System.out.println("Stream: " + address.getStream());
|
||||||
System.out.println("Version: " + address.getVersion());
|
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() {
|
private void messages() {
|
||||||
String command;
|
String command;
|
||||||
List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.NEW);
|
List<Plaintext> messages = ctx.messages().findMessages(Plaintext.Status.RECEIVED);
|
||||||
do {
|
do {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
@ -247,6 +258,8 @@ public class Application {
|
|||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println(message.getText());
|
System.out.println(message.getText());
|
||||||
System.out.println();
|
System.out.println();
|
||||||
|
System.out.println("Labels: "+ message.getLabels());
|
||||||
|
System.out.println();
|
||||||
String command;
|
String command;
|
||||||
do {
|
do {
|
||||||
System.out.println("r) reply");
|
System.out.println("r) reply");
|
||||||
@ -269,14 +282,79 @@ public class Application {
|
|||||||
|
|
||||||
private void compose() {
|
private void compose() {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println("TODO");
|
BitmessageAddress from = selectAddress(true);
|
||||||
// TODO
|
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) {
|
private void compose(BitmessageAddress from, BitmessageAddress to, String subject) {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
System.out.println("TODO");
|
System.out.println("From: " + from);
|
||||||
// TODO
|
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) {
|
private boolean yesNo(String question) {
|
||||||
|
@ -20,9 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
|
|||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext.Encoding;
|
import ch.dissem.bitmessage.entity.Plaintext.Encoding;
|
||||||
import ch.dissem.bitmessage.entity.payload.GetPubkey;
|
import ch.dissem.bitmessage.entity.payload.*;
|
||||||
import ch.dissem.bitmessage.entity.payload.Msg;
|
|
||||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
import ch.dissem.bitmessage.entity.payload.Pubkey.Feature;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
import ch.dissem.bitmessage.entity.valueobject.PrivateKey;
|
||||||
import ch.dissem.bitmessage.ports.*;
|
import ch.dissem.bitmessage.ports.*;
|
||||||
@ -32,6 +30,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.TreeSet;
|
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.
|
* 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:
|
* You'll need the Builder to create a BitmessageContext, and set the following properties:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>addressRepo</li>
|
* <li>addressRepo</li>
|
||||||
* <li>inventory</li>
|
* <li>inventory</li>
|
||||||
* <li>nodeRegistry</li>
|
* <li>nodeRegistry</li>
|
||||||
* <li>networkHandler</li>
|
* <li>networkHandler</li>
|
||||||
* <li>messageRepo</li>
|
* <li>messageRepo</li>
|
||||||
* <li>streams</li>
|
* <li>streams</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* The default implementations in the different module builds can be used.
|
* The default implementations in the different module builds can be used.
|
||||||
* <p>
|
* <p/>
|
||||||
* The port defaults to 8444 (the default Bitmessage port)
|
* The port defaults to 8444 (the default Bitmessage port)
|
||||||
*/
|
*/
|
||||||
public class BitmessageContext {
|
public class BitmessageContext {
|
||||||
@ -81,6 +80,7 @@ public class BitmessageContext {
|
|||||||
features
|
features
|
||||||
));
|
));
|
||||||
ctx.getAddressRepo().save(identity);
|
ctx.getAddressRepo().save(identity);
|
||||||
|
// TODO: this should happen in a separate thread
|
||||||
ctx.sendPubkey(identity, identity.getStream());
|
ctx.sendPubkey(identity, identity.getStream());
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
@ -93,6 +93,7 @@ public class BitmessageContext {
|
|||||||
if (from.getPrivateKey() == null) {
|
if (from.getPrivateKey() == null) {
|
||||||
throw new IllegalArgumentException("'From' must be an identity, i.e. have a private key.");
|
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()
|
Plaintext msg = new Plaintext.Builder()
|
||||||
.from(from)
|
.from(from)
|
||||||
.to(to)
|
.to(to)
|
||||||
@ -100,10 +101,15 @@ public class BitmessageContext {
|
|||||||
.message(subject, message)
|
.message(subject, message)
|
||||||
.build();
|
.build();
|
||||||
if (to.getPubkey() == null) {
|
if (to.getPubkey() == null) {
|
||||||
|
tryToFindMatchingPubkey(to);
|
||||||
|
}
|
||||||
|
if (to.getPubkey() == null) {
|
||||||
|
LOG.info("Public key is missing from recipient. Requesting.");
|
||||||
requestPubkey(from, to);
|
requestPubkey(from, to);
|
||||||
msg.setStatus(PUBKEY_REQUESTED);
|
msg.setStatus(PUBKEY_REQUESTED);
|
||||||
ctx.getMessageRepository().save(msg);
|
ctx.getMessageRepository().save(msg);
|
||||||
} else {
|
} else {
|
||||||
|
LOG.info("Sending message.");
|
||||||
msg.setStatus(DOING_PROOF_OF_WORK);
|
msg.setStatus(DOING_PROOF_OF_WORK);
|
||||||
ctx.getMessageRepository().save(msg);
|
ctx.getMessageRepository().save(msg);
|
||||||
ctx.send(
|
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 {
|
try {
|
||||||
long expires = UnixTime.now(+timeToLive);
|
long expires = UnixTime.now(+timeToLive);
|
||||||
LOG.info("Expires at " + expires);
|
LOG.info("Expires at " + expires);
|
||||||
ObjectMessage object = new ObjectMessage.Builder()
|
ObjectMessage object = new ObjectMessage.Builder()
|
||||||
.stream(stream)
|
.stream(stream)
|
||||||
.version(version)
|
|
||||||
.expiresTime(expires)
|
.expiresTime(expires)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.build();
|
.build();
|
||||||
@ -159,8 +164,39 @@ public class BitmessageContext {
|
|||||||
|
|
||||||
public void addContact(BitmessageAddress contact) {
|
public void addContact(BitmessageAddress contact) {
|
||||||
ctx.getAddressRepo().save(contact);
|
ctx.getAddressRepo().save(contact);
|
||||||
// TODO: search pubkey in inventory
|
tryToFindMatchingPubkey(contact);
|
||||||
ctx.requestPubkey(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 {
|
public interface Listener {
|
||||||
|
@ -20,6 +20,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
|
|||||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.entity.payload.*;
|
import ch.dissem.bitmessage.entity.payload.*;
|
||||||
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
import ch.dissem.bitmessage.exception.DecryptionFailedException;
|
||||||
import ch.dissem.bitmessage.ports.NetworkHandler;
|
import ch.dissem.bitmessage.ports.NetworkHandler;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -28,9 +29,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Status.DOING_PROOF_OF_WORK;
|
import static ch.dissem.bitmessage.entity.Plaintext.Status.*;
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Status.NEW;
|
|
||||||
import static ch.dissem.bitmessage.entity.Plaintext.Status.SENT;
|
|
||||||
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
import static ch.dissem.bitmessage.utils.UnixTime.DAY;
|
||||||
|
|
||||||
class DefaultMessageListener implements NetworkHandler.MessageListener {
|
class DefaultMessageListener implements NetworkHandler.MessageListener {
|
||||||
@ -107,6 +106,7 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
|
|||||||
msg.setStatus(SENT);
|
msg.setStatus(SENT);
|
||||||
ctx.getMessageRepository().save(msg);
|
ctx.getMessageRepository().save(msg);
|
||||||
}
|
}
|
||||||
|
ctx.getAddressRepo().save(address);
|
||||||
}
|
}
|
||||||
} catch (DecryptionFailedException ignore) {
|
} catch (DecryptionFailedException ignore) {
|
||||||
LOG.debug(ignore.getMessage(), ignore);
|
LOG.debug(ignore.getMessage(), ignore);
|
||||||
@ -119,7 +119,8 @@ class DefaultMessageListener implements NetworkHandler.MessageListener {
|
|||||||
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
|
msg.decrypt(identity.getPrivateKey().getPrivateEncryptionKey());
|
||||||
msg.getPlaintext().setTo(identity);
|
msg.getPlaintext().setTo(identity);
|
||||||
object.isSignatureValid(msg.getPlaintext().getFrom().getPubkey());
|
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());
|
ctx.getMessageRepository().save(msg.getPlaintext());
|
||||||
listener.receive(msg.getPlaintext());
|
listener.receive(msg.getPlaintext());
|
||||||
break;
|
break;
|
||||||
|
@ -145,17 +145,16 @@ public class InternalContext {
|
|||||||
LOG.info("Expires at " + expires);
|
LOG.info("Expires at " + expires);
|
||||||
ObjectMessage object = new ObjectMessage.Builder()
|
ObjectMessage object = new ObjectMessage.Builder()
|
||||||
.stream(to.getStream())
|
.stream(to.getStream())
|
||||||
.version(to.getVersion())
|
|
||||||
.expiresTime(expires)
|
.expiresTime(expires)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.build();
|
.build();
|
||||||
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
|
|
||||||
if (object.isSigned()) {
|
if (object.isSigned()) {
|
||||||
object.sign(from.getPrivateKey());
|
object.sign(from.getPrivateKey());
|
||||||
}
|
}
|
||||||
if (object instanceof Encrypted) {
|
if (payload instanceof Encrypted) {
|
||||||
object.encrypt(to.getPubkey());
|
object.encrypt(to.getPubkey());
|
||||||
}
|
}
|
||||||
|
Security.doProofOfWork(object, proofOfWorkEngine, nonceTrialsPerByte, extraBytes);
|
||||||
inventory.storeObject(object);
|
inventory.storeObject(object);
|
||||||
networkHandler.offer(object.getInventoryVector());
|
networkHandler.offer(object.getInventoryVector());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -169,7 +168,6 @@ public class InternalContext {
|
|||||||
LOG.info("Expires at " + expires);
|
LOG.info("Expires at " + expires);
|
||||||
ObjectMessage response = new ObjectMessage.Builder()
|
ObjectMessage response = new ObjectMessage.Builder()
|
||||||
.stream(targetStream)
|
.stream(targetStream)
|
||||||
.version(identity.getVersion())
|
|
||||||
.expiresTime(expires)
|
.expiresTime(expires)
|
||||||
.payload(identity.getPubkey())
|
.payload(identity.getPubkey())
|
||||||
.build();
|
.build();
|
||||||
@ -196,7 +194,6 @@ public class InternalContext {
|
|||||||
LOG.info("Expires at " + expires);
|
LOG.info("Expires at " + expires);
|
||||||
ObjectMessage response = new ObjectMessage.Builder()
|
ObjectMessage response = new ObjectMessage.Builder()
|
||||||
.stream(contact.getStream())
|
.stream(contact.getStream())
|
||||||
.version(contact.getVersion())
|
|
||||||
.expiresTime(expires)
|
.expiresTime(expires)
|
||||||
.payload(new GetPubkey(contact))
|
.payload(new GetPubkey(contact))
|
||||||
.build();
|
.build();
|
||||||
|
@ -50,7 +50,7 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
nonce = builder.nonce;
|
nonce = builder.nonce;
|
||||||
expiresTime = builder.expiresTime;
|
expiresTime = builder.expiresTime;
|
||||||
objectType = builder.objectType;
|
objectType = builder.objectType;
|
||||||
version = builder.version;
|
version = builder.payload.getVersion();
|
||||||
stream = builder.streamNumber;
|
stream = builder.streamNumber;
|
||||||
payload = builder.payload;
|
payload = builder.payload;
|
||||||
}
|
}
|
||||||
@ -177,7 +177,6 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
private byte[] nonce;
|
private byte[] nonce;
|
||||||
private long expiresTime;
|
private long expiresTime;
|
||||||
private long objectType = -1;
|
private long objectType = -1;
|
||||||
private long version = -1;
|
|
||||||
private long streamNumber;
|
private long streamNumber;
|
||||||
private ObjectPayload payload;
|
private ObjectPayload payload;
|
||||||
|
|
||||||
@ -204,11 +203,6 @@ public class ObjectMessage implements MessagePayload {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder version(long version) {
|
|
||||||
this.version = version;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Builder stream(long streamNumber) {
|
public Builder stream(long streamNumber) {
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
return this;
|
return this;
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.entity;
|
package ch.dissem.bitmessage.entity;
|
||||||
|
|
||||||
import ch.dissem.bitmessage.entity.payload.Pubkey;
|
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
import ch.dissem.bitmessage.factory.Factory;
|
import ch.dissem.bitmessage.factory.Factory;
|
||||||
import ch.dissem.bitmessage.utils.Decode;
|
import ch.dissem.bitmessage.utils.Decode;
|
||||||
@ -129,8 +128,12 @@ public class Plaintext implements Streamable {
|
|||||||
Encode.varInt(ack.length, out);
|
Encode.varInt(ack.length, out);
|
||||||
out.write(ack);
|
out.write(ack);
|
||||||
if (includeSignature) {
|
if (includeSignature) {
|
||||||
Encode.varInt(signature.length, out);
|
if (signature == null) {
|
||||||
out.write(signature);
|
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 {
|
public enum Encoding {
|
||||||
IGNORE(0), TRIVIAL(1), SIMPLE(2);
|
IGNORE(0), TRIVIAL(1), SIMPLE(2);
|
||||||
|
|
||||||
@ -203,15 +240,13 @@ public class Plaintext implements Streamable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum Status {
|
public enum Status {
|
||||||
|
DRAFT,
|
||||||
// For sent messages
|
// For sent messages
|
||||||
PUBKEY_REQUESTED,
|
PUBKEY_REQUESTED,
|
||||||
DOING_PROOF_OF_WORK,
|
DOING_PROOF_OF_WORK,
|
||||||
SENT,
|
SENT,
|
||||||
SENT_ACKNOWLEDGED,
|
SENT_ACKNOWLEDGED,
|
||||||
|
RECEIVED
|
||||||
// For received messages
|
|
||||||
NEW,
|
|
||||||
READ
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class Builder {
|
public static final class Builder {
|
||||||
@ -233,7 +268,7 @@ public class Plaintext implements Streamable {
|
|||||||
private long sent;
|
private long sent;
|
||||||
private long received;
|
private long received;
|
||||||
private Status status;
|
private Status status;
|
||||||
private Set<Label> labels = new TreeSet<>();
|
private Set<Label> labels = new HashSet<>();
|
||||||
|
|
||||||
public Builder() {
|
public Builder() {
|
||||||
}
|
}
|
||||||
@ -365,26 +400,4 @@ public class Plaintext implements Streamable {
|
|||||||
return new Plaintext(this);
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,8 @@ public abstract class Broadcast extends ObjectPayload implements Encrypted {
|
|||||||
protected CryptoBox encrypted;
|
protected CryptoBox encrypted;
|
||||||
protected Plaintext plaintext;
|
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.stream = stream;
|
||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
this.plaintext = plaintext;
|
this.plaintext = plaintext;
|
||||||
|
@ -184,7 +184,7 @@ public class CryptoBox implements Streamable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder curveType(int curveType) {
|
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;
|
this.curveType = curveType;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -31,13 +31,14 @@ public class GenericPayload extends ObjectPayload {
|
|||||||
private long stream;
|
private long stream;
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
public GenericPayload(long stream, byte[] data) {
|
public GenericPayload(long version, long stream, byte[] data) {
|
||||||
|
super(version);
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GenericPayload read(InputStream is, long stream, int length) throws IOException {
|
public static GenericPayload read(long version, InputStream is, long stream, int length) throws IOException {
|
||||||
return new GenericPayload(stream, Decode.bytes(is, length));
|
return new GenericPayload(version, stream, Decode.bytes(is, length));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,6 +31,7 @@ public class GetPubkey extends ObjectPayload {
|
|||||||
private byte[] ripeTag;
|
private byte[] ripeTag;
|
||||||
|
|
||||||
public GetPubkey(BitmessageAddress address) {
|
public GetPubkey(BitmessageAddress address) {
|
||||||
|
super(address.getVersion());
|
||||||
this.stream = address.getStream();
|
this.stream = address.getStream();
|
||||||
if (address.getVersion() < 4)
|
if (address.getVersion() < 4)
|
||||||
this.ripeTag = address.getRipe();
|
this.ripeTag = address.getRipe();
|
||||||
@ -38,13 +39,14 @@ public class GetPubkey extends ObjectPayload {
|
|||||||
this.ripeTag = address.getTag();
|
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.stream = stream;
|
||||||
this.ripeTag = ripeOrTag;
|
this.ripeTag = ripeOrTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GetPubkey read(InputStream is, long stream, int length, long version) throws IOException {
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,11 +33,13 @@ public class Msg extends ObjectPayload implements Encrypted {
|
|||||||
private Plaintext plaintext;
|
private Plaintext plaintext;
|
||||||
|
|
||||||
private Msg(long stream, CryptoBox encrypted) {
|
private Msg(long stream, CryptoBox encrypted) {
|
||||||
|
super(1);
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Msg(Plaintext plaintext) {
|
public Msg(Plaintext plaintext) {
|
||||||
|
super(1);
|
||||||
this.stream = plaintext.getStream();
|
this.stream = plaintext.getStream();
|
||||||
this.plaintext = plaintext;
|
this.plaintext = plaintext;
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,21 @@ import java.io.OutputStream;
|
|||||||
* The payload of an 'object' command. This is shared by the network.
|
* The payload of an 'object' command. This is shared by the network.
|
||||||
*/
|
*/
|
||||||
public abstract class ObjectPayload implements Streamable {
|
public abstract class ObjectPayload implements Streamable {
|
||||||
|
private final long version;
|
||||||
|
|
||||||
|
protected ObjectPayload(long version) {
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public abstract ObjectType getType();
|
public abstract ObjectType getType();
|
||||||
|
|
||||||
public abstract long getStream();
|
public abstract long getStream();
|
||||||
|
|
||||||
|
public long getVersion() {
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSigned() {
|
public boolean isSigned() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -29,12 +29,14 @@ import static ch.dissem.bitmessage.utils.Security.sha512;
|
|||||||
public abstract class Pubkey extends ObjectPayload {
|
public abstract class Pubkey extends ObjectPayload {
|
||||||
public final static long LATEST_VERSION = 4;
|
public final static long LATEST_VERSION = 4;
|
||||||
|
|
||||||
|
protected Pubkey(long version) {
|
||||||
|
super(version);
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) {
|
public static byte[] getRipe(byte[] publicSigningKey, byte[] publicEncryptionKey) {
|
||||||
return ripemd160(sha512(publicSigningKey, publicEncryptionKey));
|
return ripemd160(sha512(publicSigningKey, publicEncryptionKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract long getVersion();
|
|
||||||
|
|
||||||
public abstract byte[] getSigningKey();
|
public abstract byte[] getSigningKey();
|
||||||
|
|
||||||
public abstract byte[] getEncryptionKey();
|
public abstract byte[] getEncryptionKey();
|
||||||
|
@ -32,10 +32,12 @@ public class V2Pubkey extends Pubkey {
|
|||||||
protected byte[] publicSigningKey; // 64 Bytes
|
protected byte[] publicSigningKey; // 64 Bytes
|
||||||
protected byte[] publicEncryptionKey; // 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;
|
stream = builder.streamNumber;
|
||||||
behaviorBitfield = builder.behaviorBitfield;
|
behaviorBitfield = builder.behaviorBitfield;
|
||||||
publicSigningKey = add0x04(builder.publicSigningKey);
|
publicSigningKey = add0x04(builder.publicSigningKey);
|
||||||
@ -118,7 +120,7 @@ public class V2Pubkey extends Pubkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public V2Pubkey build() {
|
public V2Pubkey build() {
|
||||||
return new V2Pubkey(this);
|
return new V2Pubkey(2, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,8 @@ public class V3Pubkey extends V2Pubkey {
|
|||||||
long extraBytes;
|
long extraBytes;
|
||||||
byte[] signature;
|
byte[] signature;
|
||||||
|
|
||||||
protected V3Pubkey(Builder builder) {
|
protected V3Pubkey(long version, Builder builder) {
|
||||||
|
super(version);
|
||||||
stream = builder.streamNumber;
|
stream = builder.streamNumber;
|
||||||
behaviorBitfield = builder.behaviorBitfield;
|
behaviorBitfield = builder.behaviorBitfield;
|
||||||
publicSigningKey = add0x04(builder.publicSigningKey);
|
publicSigningKey = add0x04(builder.publicSigningKey);
|
||||||
@ -95,6 +96,24 @@ public class V3Pubkey extends V2Pubkey {
|
|||||||
this.signature = signature;
|
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 {
|
public static class Builder {
|
||||||
private long streamNumber;
|
private long streamNumber;
|
||||||
private int behaviorBitfield;
|
private int behaviorBitfield;
|
||||||
@ -143,25 +162,7 @@ public class V3Pubkey extends V2Pubkey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public V3Pubkey build() {
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -25,12 +25,12 @@ import java.io.OutputStream;
|
|||||||
* Broadcasts are version 4 or 5.
|
* Broadcasts are version 4 or 5.
|
||||||
*/
|
*/
|
||||||
public class V4Broadcast extends Broadcast {
|
public class V4Broadcast extends Broadcast {
|
||||||
protected V4Broadcast(long stream, CryptoBox encrypted) {
|
protected V4Broadcast(long version, long stream, CryptoBox encrypted) {
|
||||||
super(stream, encrypted, null);
|
super(version, stream, encrypted, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static V4Broadcast read(InputStream in, long stream, int length) throws IOException {
|
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
|
@Override
|
||||||
|
@ -39,12 +39,14 @@ public class V4Pubkey extends Pubkey implements Encrypted {
|
|||||||
private V3Pubkey decrypted;
|
private V3Pubkey decrypted;
|
||||||
|
|
||||||
private V4Pubkey(long stream, byte[] tag, CryptoBox encrypted) {
|
private V4Pubkey(long stream, byte[] tag, CryptoBox encrypted) {
|
||||||
|
super(4);
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.encrypted = encrypted;
|
this.encrypted = encrypted;
|
||||||
}
|
}
|
||||||
|
|
||||||
public V4Pubkey(V3Pubkey decrypted) {
|
public V4Pubkey(V3Pubkey decrypted) {
|
||||||
|
super(4);
|
||||||
this.decrypted = decrypted;
|
this.decrypted = decrypted;
|
||||||
this.stream = decrypted.stream;
|
this.stream = decrypted.stream;
|
||||||
this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe());
|
this.tag = BitmessageAddress.calculateTag(4, decrypted.getStream(), decrypted.getRipe());
|
||||||
|
@ -29,7 +29,7 @@ public class V5Broadcast extends V4Broadcast {
|
|||||||
private byte[] tag;
|
private byte[] tag;
|
||||||
|
|
||||||
private V5Broadcast(long stream, byte[] tag, CryptoBox encrypted) {
|
private V5Broadcast(long stream, byte[] tag, CryptoBox encrypted) {
|
||||||
super(stream, encrypted);
|
super(5, stream, encrypted);
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,13 +16,17 @@
|
|||||||
|
|
||||||
package ch.dissem.bitmessage.entity.valueobject;
|
package ch.dissem.bitmessage.entity.valueobject;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Label {
|
public class Label {
|
||||||
private Object id;
|
private Object id;
|
||||||
private String label;
|
private String label;
|
||||||
|
private Type type;
|
||||||
private int color;
|
private int color;
|
||||||
|
|
||||||
public Label(String label, int color) {
|
public Label(String label, Type type, int color) {
|
||||||
this.label = label;
|
this.label = label;
|
||||||
|
this.type = type;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,4 +49,33 @@ public class Label {
|
|||||||
public Object getId() {
|
public Object getId() {
|
||||||
return id;
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ public class Factory {
|
|||||||
}
|
}
|
||||||
// fallback: just store the message - we don't really care what it is
|
// fallback: just store the message - we don't really care what it is
|
||||||
// LOG.info("Unexpected object type: " + objectType);
|
// 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 {
|
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 {
|
private static ObjectPayload parsePubkey(long version, long streamNumber, InputStream stream, int length) throws IOException {
|
||||||
Pubkey pubkey = readPubkey(version, streamNumber, stream, length, true);
|
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 {
|
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);
|
return V5Broadcast.read(stream, streamNumber, length);
|
||||||
default:
|
default:
|
||||||
LOG.debug("Encountered unknown broadcast version " + version);
|
LOG.debug("Encountered unknown broadcast version " + version);
|
||||||
return GenericPayload.read(stream, streamNumber, length);
|
return GenericPayload.read(version, stream, streamNumber, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,14 +95,13 @@ class V3MessageFactory {
|
|||||||
payload = Factory.getObjectPayload(objectType, version, stream, dataStream, data.length);
|
payload = Factory.getObjectPayload(objectType, version, stream, dataStream, data.length);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.trace("Could not parse object payload - using generic payload instead", 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()
|
return new ObjectMessage.Builder()
|
||||||
.nonce(nonce)
|
.nonce(nonce)
|
||||||
.expiresTime(expiresTime)
|
.expiresTime(expiresTime)
|
||||||
.objectType(objectType)
|
.objectType(objectType)
|
||||||
.version(version)
|
|
||||||
.stream(stream)
|
.stream(stream)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.build();
|
.build();
|
||||||
|
@ -24,7 +24,9 @@ import ch.dissem.bitmessage.entity.valueobject.Label;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface MessageRepository {
|
public interface MessageRepository {
|
||||||
List<String> getLabels();
|
List<Label> getLabels();
|
||||||
|
|
||||||
|
List<Label> getLabels(Label.Type... types);
|
||||||
|
|
||||||
List<Plaintext> findMessages(Label label);
|
List<Plaintext> findMessages(Label label);
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ package ch.dissem.bitmessage.utils;
|
|||||||
import ch.dissem.bitmessage.entity.payload.ObjectType;
|
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 class Strings {
|
||||||
public static StringBuilder join(byte[]... objects) {
|
public static StringBuilder join(byte[]... objects) {
|
||||||
@ -49,6 +50,15 @@ public class Strings {
|
|||||||
return streamList;
|
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) {
|
public static StringBuilder join(Object... objects) {
|
||||||
StringBuilder streamList = new StringBuilder();
|
StringBuilder streamList = new StringBuilder();
|
||||||
for (int i = 0; i < objects.length; i++) {
|
for (int i = 0; i < objects.length; i++) {
|
||||||
|
@ -37,12 +37,12 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class EncryptionTest {
|
public class EncryptionTest {
|
||||||
@Test
|
@Test
|
||||||
public void ensureDecryptedDataIsSameAsBeforeEncryption() throws IOException, DecryptionFailedException {
|
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);
|
PrivateKey privateKey = new PrivateKey(false, 1, 1000, 1000);
|
||||||
CryptoBox cryptoBox = new CryptoBox(before, privateKey.getPubkey().getEncryptionKey());
|
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);
|
assertEquals(before, after);
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,6 @@ public class SignatureTest {
|
|||||||
ObjectMessage objectMessage = new ObjectMessage.Builder()
|
ObjectMessage objectMessage = new ObjectMessage.Builder()
|
||||||
.objectType(ObjectType.PUBKEY)
|
.objectType(ObjectType.PUBKEY)
|
||||||
.stream(1)
|
.stream(1)
|
||||||
.version(1)
|
|
||||||
.payload(privateKey.getPubkey())
|
.payload(privateKey.getPubkey())
|
||||||
.build();
|
.build();
|
||||||
objectMessage.sign(privateKey);
|
objectMessage.sign(privateKey);
|
||||||
|
@ -73,7 +73,7 @@ public class SecurityTest {
|
|||||||
.nonce(new byte[8])
|
.nonce(new byte[8])
|
||||||
.expiresTime(UnixTime.now(+2 * DAY)) // 5 minutes
|
.expiresTime(UnixTime.now(+2 * DAY)) // 5 minutes
|
||||||
.objectType(0)
|
.objectType(0)
|
||||||
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
|
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
|
||||||
.build();
|
.build();
|
||||||
Security.checkProofOfWork(objectMessage, 1000, 1000);
|
Security.checkProofOfWork(objectMessage, 1000, 1000);
|
||||||
}
|
}
|
||||||
@ -84,7 +84,7 @@ public class SecurityTest {
|
|||||||
.nonce(new byte[8])
|
.nonce(new byte[8])
|
||||||
.expiresTime(UnixTime.now(+2 * DAY))
|
.expiresTime(UnixTime.now(+2 * DAY))
|
||||||
.objectType(0)
|
.objectType(0)
|
||||||
.payload(GenericPayload.read(new ByteArrayInputStream(new byte[0]), 1, 0))
|
.payload(GenericPayload.read(0, new ByteArrayInputStream(new byte[0]), 1, 0))
|
||||||
.build();
|
.build();
|
||||||
Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000);
|
Security.doProofOfWork(objectMessage, new MultiThreadedPOWEngine(), 1000, 1000);
|
||||||
Security.checkProofOfWork(objectMessage, 1000, 1000);
|
Security.checkProofOfWork(objectMessage, 1000, 1000);
|
||||||
|
@ -120,6 +120,7 @@ public class Connection implements Runnable {
|
|||||||
+ msg.getPayload().getCommand());
|
+ msg.getPayload().getCommand());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (socket.isClosed()) state = DISCONNECTED;
|
||||||
} catch (SocketTimeoutException ignore) {
|
} catch (SocketTimeoutException ignore) {
|
||||||
if (state == ACTIVE) {
|
if (state == ACTIVE) {
|
||||||
sendQueue();
|
sendQueue();
|
||||||
@ -160,7 +161,7 @@ public class Connection implements Runnable {
|
|||||||
ctx.getInventory().storeObject(objectMessage);
|
ctx.getInventory().storeObject(objectMessage);
|
||||||
} catch (InsufficientProofOfWorkException e) {
|
} catch (InsufficientProofOfWorkException e) {
|
||||||
try {
|
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();
|
f.createNewFile();
|
||||||
objectMessage.write(new FileOutputStream(f));
|
objectMessage.write(new FileOutputStream(f));
|
||||||
} catch (IOException e1) {
|
} catch (IOException e1) {
|
||||||
|
@ -21,6 +21,7 @@ import ch.dissem.bitmessage.entity.BitmessageAddress;
|
|||||||
import ch.dissem.bitmessage.entity.Plaintext;
|
import ch.dissem.bitmessage.entity.Plaintext;
|
||||||
import ch.dissem.bitmessage.entity.valueobject.Label;
|
import ch.dissem.bitmessage.entity.valueobject.Label;
|
||||||
import ch.dissem.bitmessage.ports.MessageRepository;
|
import ch.dissem.bitmessage.ports.MessageRepository;
|
||||||
|
import ch.dissem.bitmessage.utils.Strings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -37,13 +38,41 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
|
|||||||
private InternalContext ctx;
|
private InternalContext ctx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getLabels() {
|
public List<Label> getLabels() {
|
||||||
List<String> result = new LinkedList<>();
|
List<Label> result = new LinkedList<>();
|
||||||
try {
|
try {
|
||||||
Statement stmt = getConnection().createStatement();
|
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()) {
|
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) {
|
} catch (SQLException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
@ -94,9 +123,9 @@ public class JdbcMessageRepository extends JdbcHelper implements MessageReposito
|
|||||||
List<Label> result = new ArrayList<>();
|
List<Label> result = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Statement stmt = getConnection().createStatement();
|
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()) {
|
while (rs.next()) {
|
||||||
result.add(new Label(rs.getString("label"), rs.getInt("color")));
|
result.add(getLabel(rs));
|
||||||
}
|
}
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
LOG.error(e.getMessage(), e);
|
LOG.error(e.getMessage(), e);
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
CREATE TABLE Message (
|
CREATE TABLE Message (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
sender VARCHAR(40) NOT NULL,
|
sender VARCHAR(40) NOT NULL,
|
||||||
recipient VARCHAR(40) NOT NULL,
|
recipient VARCHAR(40) NOT NULL,
|
||||||
data BLOB NOT NULL,
|
data BLOB NOT NULL,
|
||||||
sent BIGINT,
|
sent BIGINT,
|
||||||
received BIGINT,
|
received BIGINT,
|
||||||
status VARCHAR(20) NOT NULL,
|
status VARCHAR(20) NOT NULL,
|
||||||
|
|
||||||
FOREIGN KEY (sender) REFERENCES Address (address),
|
FOREIGN KEY (sender) REFERENCES Address (address),
|
||||||
FOREIGN KEY (recipient) REFERENCES Address (address)
|
FOREIGN KEY (recipient) REFERENCES Address (address)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE Label (
|
CREATE TABLE Label (
|
||||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||||
label VARCHAR(255) NOT NULL,
|
label VARCHAR(255) NOT NULL,
|
||||||
color INT,
|
type VARCHAR(20),
|
||||||
ord BIGINT,
|
color INT NOT NULL DEFAULT X'FF000000',
|
||||||
|
ord BIGINT,
|
||||||
|
|
||||||
CONSTRAINT UC_label UNIQUE (label),
|
CONSTRAINT UC_label UNIQUE (label),
|
||||||
CONSTRAINT UC_order UNIQUE (ord)
|
CONSTRAINT UC_order UNIQUE (ord)
|
||||||
);
|
);
|
||||||
@ -29,7 +31,8 @@ CREATE TABLE Message_Label (
|
|||||||
FOREIGN KEY (label_id) REFERENCES Label (id)
|
FOREIGN KEY (label_id) REFERENCES Label (id)
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO Label(label, ord) VALUES ('Inbox', 0);
|
INSERT INTO Label(label, type, color, ord) VALUES ('Inbox', 'INBOX', X'FF0000FF', 0);
|
||||||
INSERT INTO Label(label, ord) VALUES ('Sent', 10);
|
INSERT INTO Label(label, type, color, ord) VALUES ('Drafts', 'DRAFTS', X'FFFF9900', 10);
|
||||||
INSERT INTO Label(label, ord) VALUES ('Drafts', 20);
|
INSERT INTO Label(label, type, color, ord) VALUES ('Sent', 'SENT', X'FFFFFF00', 20);
|
||||||
INSERT INTO Label(label, ord) VALUES ('Trash', 100);
|
INSERT INTO Label(label, type, ord) VALUES ('Unread', 'UNREAD', 90);
|
||||||
|
INSERT INTO Label(label, type, ord) VALUES ('Trash', 'TRASH', 100);
|
||||||
|
Loading…
Reference in New Issue
Block a user