diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java index 37675ac399cd4465b7c5215e8141984bc7d89b7f..c908d3e4710ea8e76f2d664d1cb0d8252107ca6c 100644 --- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java +++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java @@ -17,8 +17,9 @@ import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; @@ -66,6 +67,9 @@ public interface DatabaseComponent { */ void addSecrets(Collection<TemporarySecret> secrets) throws DbException; + /** Adds a transport to the database. */ + void addTransport(TransportId t) throws DbException; + /** * Generates an acknowledgement for the given contact. Returns null if * there are no messages to acknowledge. @@ -98,18 +102,32 @@ public interface DatabaseComponent { */ Offer generateOffer(ContactId c, int maxMessages) throws DbException; + /** + * Generates a subscription ack for the given contact. Returns null if no + * ack is due. + */ + SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException; + /** * Generates a subscription update for the given contact. Returns null if - * an update is not due. + * no update is due. */ SubscriptionUpdate generateSubscriptionUpdate(ContactId c) throws DbException; /** - * Generates a transport update for the given contact. Returns null if an - * update is not due. + * Generates a batch of transport acks for the given contact. Returns null + * if no acks are due. */ - TransportUpdate generateTransportUpdate(ContactId c) throws DbException; + Collection<TransportAck> generateTransportAcks(ContactId c) + throws DbException; + + /** + * Generates a batch of transport updates for the given contact. Returns + * null if no updates are due. + */ + Collection<TransportUpdate> generateTransportUpdates(ContactId c) + throws DbException; /** Returns the configuration for the given transport. */ TransportConfig getConfig(TransportId t) throws DbException; @@ -120,9 +138,6 @@ public interface DatabaseComponent { /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; - /** Returns all local transports. */ - Collection<Transport> getLocalTransports() throws DbException; - /** Returns the headers of all messages in the given group. */ Collection<MessageHeader> getMessageHeaders(GroupId g) throws DbException; @@ -168,7 +183,7 @@ public interface DatabaseComponent { void mergeLocalProperties(TransportId t, TransportProperties p) throws DbException; - /** Processes an acknowledgement from the given contact. */ + /** Processes an ack from the given contact. */ void receiveAck(ContactId c, Ack a) throws DbException; /** Processes a message from the given contact. */ @@ -184,10 +199,17 @@ public interface DatabaseComponent { */ Request receiveOffer(ContactId c, Offer o) throws DbException; + /** Processes a subscription ack from the given contact. */ + void receiveSubscriptionAck(ContactId c, SubscriptionAck a) + throws DbException; + /** Processes a subscription update from the given contact. */ void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) throws DbException; + /** Processes a transport ack from the given contact. */ + void receiveTransportAck(ContactId c, TransportAck a) throws DbException; + /** Processes a transport update from the given contact. */ void receiveTransportUpdate(ContactId c, TransportUpdate t) throws DbException; diff --git a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java deleted file mode 100644 index 501eb886d36ee5edcaf3129431ce9054d1c3ca09..0000000000000000000000000000000000000000 --- a/briar-api/src/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java +++ /dev/null @@ -1,27 +0,0 @@ -package net.sf.briar.api.db.event; - -import java.util.Collection; - -import net.sf.briar.api.ContactId; -import net.sf.briar.api.protocol.Transport; - -/** An event that is broadcast when a contact's transports are updated. */ -public class RemoteTransportsUpdatedEvent extends DatabaseEvent { - - private final ContactId contactId; - private final Collection<Transport> transports; - - public RemoteTransportsUpdatedEvent(ContactId contactId, - Collection<Transport> transports) { - this.contactId = contactId; - this.transports = transports; - } - - public ContactId getContactId() { - return contactId; - } - - public Collection<Transport> getTransports() { - return transports; - } -} diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java similarity index 66% rename from briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java rename to briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java index c60c23b83b1000df6d6b7d933bbb8f2822abfb2e..782b11cfe7f2cbbadd68cf61dc25a3106e069cf0 100644 --- a/briar-api/src/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java +++ b/briar-api/src/net/sf/briar/api/db/event/TransportsUpdatedEvent.java @@ -4,6 +4,6 @@ package net.sf.briar.api.db.event; * An event that is broadcast when the local transport properties are * updated. */ -public class LocalTransportsUpdatedEvent extends DatabaseEvent { +public class TransportsUpdatedEvent extends DatabaseEvent { } diff --git a/briar-api/src/net/sf/briar/api/protocol/Ack.java b/briar-api/src/net/sf/briar/api/protocol/Ack.java index 4608fee5306cdb8e95254b0e928d3eab63fd2cda..8a39f38a98080eb42798f549813250c3188b0676 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Ack.java +++ b/briar-api/src/net/sf/briar/api/protocol/Ack.java @@ -2,9 +2,17 @@ package net.sf.briar.api.protocol; import java.util.Collection; -/** A packet acknowledging receipt of one or more messages. */ -public interface Ack { +/** A packet acknowledging receipt of one or more {@link Message}s. */ +public class Ack { - /** Returns the IDs of the acknowledged messages. */ - Collection<MessageId> getMessageIds(); + private final Collection<MessageId> acked; + + public Ack(Collection<MessageId> acked) { + this.acked = acked; + } + + /** Returns the identifiers of the acknowledged messages. */ + public Collection<MessageId> getMessageIds() { + return acked; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Author.java b/briar-api/src/net/sf/briar/api/protocol/Author.java index 52266b9621ca4de098d7bb10f50ab48412b399f1..1532155c32ccb733c09b21d207fcb4e8bd9c7698 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Author.java +++ b/briar-api/src/net/sf/briar/api/protocol/Author.java @@ -1,17 +1,43 @@ package net.sf.briar.api.protocol; -/** A pseudonymous author of messages. */ -public interface Author { +/** A pseudonymous author of {@link Message}s. */ +public class Author { + + private final AuthorId id; + private final String name; + private final byte[] publicKey; + + public Author(AuthorId id, String name, byte[] publicKey) { + this.id = id; + this.name = name; + this.publicKey = publicKey; + } /** Returns the author's unique identifier. */ - AuthorId getId(); + public AuthorId getId() { + return id; + } /** Returns the author's name. */ - String getName(); + public String getName() { + return name; + } /** * Returns the public key that is used to verify messages signed by the * author. */ - byte[] getPublicKey(); + public byte[] getPublicKey() { + return publicKey; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof Author && id.equals(((Author) o).id); + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java b/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java index 24b211197de2621114d48342bee859e14a776c26..59c21df227662d45ef9c8fc1bd9399ed3b190384 100644 --- a/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java +++ b/briar-api/src/net/sf/briar/api/protocol/AuthorFactory.java @@ -5,6 +5,4 @@ import java.io.IOException; public interface AuthorFactory { Author createAuthor(String name, byte[] publicKey) throws IOException; - - Author createAuthor(AuthorId id, String name, byte[] publicKey); } diff --git a/briar-api/src/net/sf/briar/api/protocol/AuthorId.java b/briar-api/src/net/sf/briar/api/protocol/AuthorId.java index 578c89c2accf4374de24d59bfcd7541808d79f24..3d572d5adaa52397f2b300f67d25228d4b277e98 100644 --- a/briar-api/src/net/sf/briar/api/protocol/AuthorId.java +++ b/briar-api/src/net/sf/briar/api/protocol/AuthorId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Type-safe wrapper for a byte array that uniquely identifies an author. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies an + * {@link Author}. + */ public class AuthorId extends UniqueId { public AuthorId(byte[] id) { diff --git a/briar-api/src/net/sf/briar/api/protocol/GroupId.java b/briar-api/src/net/sf/briar/api/protocol/GroupId.java index 532941867942b4bb02f4e8a6ed828820c7cbb20f..b530f8c2f5ab5217a5bbd22fe7a95edb1f4c15ab 100644 --- a/briar-api/src/net/sf/briar/api/protocol/GroupId.java +++ b/briar-api/src/net/sf/briar/api/protocol/GroupId.java @@ -3,8 +3,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; /** - * Type-safe wrapper for a byte array that uniquely identifies a group to which - * users may subscribe. + * Type-safe wrapper for a byte array that uniquely identifies a {@link Group}. */ public class GroupId extends UniqueId { diff --git a/briar-api/src/net/sf/briar/api/protocol/Message.java b/briar-api/src/net/sf/briar/api/protocol/Message.java index 23b0e745ade49284dc5858773fcfce4f98d6cf38..c5dca006efe5419b42c19eae0794848b9dc32b0d 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Message.java +++ b/briar-api/src/net/sf/briar/api/protocol/Message.java @@ -6,21 +6,27 @@ public interface Message { MessageId getId(); /** - * Returns the message's parent, or null if this is the first message in a - * thread. + * Returns the identifier of the message's parent, or null if this is the + * first message in a thread. */ MessageId getParent(); - /** Returns the group to which the message belongs. */ + /** + * Returns the identifier of the {@link Group} to which the message + * belongs, or null if this is a private message. + */ GroupId getGroup(); - /** Returns the message's author. */ + /** + * Returns the identifier of the message's {@link Author}, or null if this + * is an anonymous message. + */ AuthorId getAuthor(); /** Returns the message's subject line. */ String getSubject(); - /** Returns the timestamp created by the message's author. */ + /** Returns the timestamp created by the message's {@link Author}. */ long getTimestamp(); /** Returns the serialised message. */ diff --git a/briar-api/src/net/sf/briar/api/protocol/MessageId.java b/briar-api/src/net/sf/briar/api/protocol/MessageId.java index e540f769ea820de7abd40dbb776075af264be3dd..88cdb1c3128aec40ebbbb0a91461331002cc4f7b 100644 --- a/briar-api/src/net/sf/briar/api/protocol/MessageId.java +++ b/briar-api/src/net/sf/briar/api/protocol/MessageId.java @@ -2,7 +2,10 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -/** Type-safe wrapper for a byte array that uniquely identifies a message. */ +/** + * Type-safe wrapper for a byte array that uniquely identifies a + * {@link Message}. + */ public class MessageId extends UniqueId { public MessageId(byte[] id) { diff --git a/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java b/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java index 59313414fb099dda4456d866d43db9ca249c8d70..f99b3487546d98c0e26ee8d9e896b3647ff9aae4 100644 --- a/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java +++ b/briar-api/src/net/sf/briar/api/protocol/MessageVerifier.java @@ -2,6 +2,7 @@ package net.sf.briar.api.protocol; import java.security.GeneralSecurityException; +/** Verifies the signatures on an {@link UnverifiedMessage}. */ public interface MessageVerifier { Message verifyMessage(UnverifiedMessage m) throws GeneralSecurityException; diff --git a/briar-api/src/net/sf/briar/api/protocol/Offer.java b/briar-api/src/net/sf/briar/api/protocol/Offer.java index 0d12875ca5fe5c2306120849cbf9d005b6f43369..d3be3b55e1872e06df98079db79376a82f2d8d41 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Offer.java +++ b/briar-api/src/net/sf/briar/api/protocol/Offer.java @@ -2,9 +2,17 @@ package net.sf.briar.api.protocol; import java.util.Collection; -/** A packet offering the recipient some messages. */ -public interface Offer { +/** A packet offering the recipient one or more {@link Messages}. */ +public class Offer { - /** Returns the message IDs contained in the offer. */ - Collection<MessageId> getMessageIds(); + private final Collection<MessageId> offered; + + public Offer(Collection<MessageId> offered) { + this.offered = offered; + } + + /** Returns the identifiers of the offered messages. */ + public Collection<MessageId> getMessageIds() { + return offered; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java b/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java deleted file mode 100644 index 9c3c51116df7fcb34d8361fcddf202126014224f..0000000000000000000000000000000000000000 --- a/briar-api/src/net/sf/briar/api/protocol/PacketFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.util.BitSet; -import java.util.Collection; -import java.util.Map; - -public interface PacketFactory { - - Ack createAck(Collection<MessageId> acked); - - Offer createOffer(Collection<MessageId> offered); - - Request createRequest(BitSet requested, int length); - - SubscriptionUpdate createSubscriptionUpdate(Map<GroupId, GroupId> holes, - Map<Group, Long> subs, long expiry, long timestamp); - - TransportUpdate createTransportUpdate(Collection<Transport> transports, - long timestamp); -} diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java index a2aa0d79a03ca2ac2639622a984b1cbcbe6aaa48..5dffe995c7ffe984b4ea3655e9aed1cc1dd92690 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolConstants.java @@ -11,9 +11,6 @@ public interface ProtocolConstants { */ int MAX_PACKET_LENGTH = MIN_CONNECTION_LENGTH / 2; - /** The maximum number of transports a node may support. */ - int MAX_TRANSPORTS = 25; - /** The maximum number of properties per transport. */ int MAX_PROPERTIES_PER_TRANSPORT = 100; diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java index 24f4c8aac8c7ceb927d61ea6d25dad0aa929a186..4712c83457b714a5809d3740a1fb070374a81310 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolReader.java @@ -18,9 +18,15 @@ public interface ProtocolReader { boolean hasRequest() throws IOException; Request readRequest() throws IOException; + boolean hasSubscriptionAck() throws IOException; + SubscriptionAck readSubscriptionAck() throws IOException; + boolean hasSubscriptionUpdate() throws IOException; SubscriptionUpdate readSubscriptionUpdate() throws IOException; + boolean hasTransportAck() throws IOException; + TransportAck readTransportAck() throws IOException; + boolean hasTransportUpdate() throws IOException; TransportUpdate readTransportUpdate() throws IOException; } diff --git a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java index 4ddd4c4fb9ff40970d2ae138fb4b5bc3fc6ef186..4637fdffc5a43d3a6f4c1d735221b0dfcd7ac21d 100644 --- a/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java +++ b/briar-api/src/net/sf/briar/api/protocol/ProtocolWriter.java @@ -16,8 +16,12 @@ public interface ProtocolWriter { void writeRequest(Request r) throws IOException; + void writeSubscriptionAck(SubscriptionAck a) throws IOException; + void writeSubscriptionUpdate(SubscriptionUpdate s) throws IOException; + void writeTransportAck(TransportAck a) throws IOException; + void writeTransportUpdate(TransportUpdate t) throws IOException; void flush() throws IOException; diff --git a/briar-api/src/net/sf/briar/api/protocol/Request.java b/briar-api/src/net/sf/briar/api/protocol/Request.java index 242e59bbd23d19a558c5d03350ad602ec17b954c..7e5d86fdd5e248f7b3e891a2c41787038695fd27 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Request.java +++ b/briar-api/src/net/sf/briar/api/protocol/Request.java @@ -2,15 +2,30 @@ package net.sf.briar.api.protocol; import java.util.BitSet; -/** A packet requesting some or all of the messages from an offer. */ -public interface Request { +/** + * A packet requesting some or all of the {@link Message}s from an + * {@link Offer}. + */ +public class Request { + + private final BitSet requested; + private final int length; + + public Request(BitSet requested, int length) { + this.requested = requested; + this.length = length; + } /** * Returns a sequence of bits corresponding to the sequence of messages in * the offer, where the i^th bit is set if the i^th message should be sent. */ - BitSet getBitmap(); + public BitSet getBitmap() { + return requested; + } /** Returns the length of the bitmap in bits. */ - int getLength(); + public int getLength() { + return length; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java b/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java new file mode 100644 index 0000000000000000000000000000000000000000..ba2c41a7cf807bcb4f85ea33eb793ccecbd0beb3 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/protocol/SubscriptionAck.java @@ -0,0 +1,16 @@ +package net.sf.briar.api.protocol; + +/** A packet acknowledging a {@link SubscriptionUpdate}. */ +public class SubscriptionAck { + + final long version; + + public SubscriptionAck(long version) { + this.version = version; + } + + /** Returns the version number of the acknowledged update. */ + public long getVersionNumber() { + return version; + } +} diff --git a/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java b/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java index 30e0e56397efa1007875f45b40c0ccd3f9f5aa93..8b7c1e8733f427a11f1c23c98e1a4f85a02774d2 100644 --- a/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java +++ b/briar-api/src/net/sf/briar/api/protocol/SubscriptionUpdate.java @@ -1,25 +1,28 @@ package net.sf.briar.api.protocol; -import java.util.Map; +import java.util.Collection; /** A packet updating the sender's subscriptions. */ -public interface SubscriptionUpdate { +public class SubscriptionUpdate { - /** Returns the holes contained in the update. */ - Map<GroupId, GroupId> getHoles(); + private final Collection<Group> subs; + private final long version; - /** Returns the subscriptions contained in the update. */ - Map<Group, Long> getSubscriptions(); + public SubscriptionUpdate(Collection<Group> subs, long version) { + this.subs = subs; + this.version = version; + } /** - * Returns the expiry time of the contact's database. Messages that are - * older than the expiry time must not be sent to the contact. + * Returns the groups to which the sender subscribes, and which the sender + * has made visible to the recipient. */ - long getExpiryTime(); + public Collection<Group> getGroups() { + return subs; + } - /** - * Returns the update's timestamp. Updates that are older than the newest - * update received from the same contact must be ignored. - */ - long getTimestamp(); + /** Returns the update's version number. */ + public long getVersionNumber() { + return version; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Transport.java b/briar-api/src/net/sf/briar/api/protocol/Transport.java deleted file mode 100644 index a8ea4a31517ba155ebe1e3d1e0de9c7739d40dd1..0000000000000000000000000000000000000000 --- a/briar-api/src/net/sf/briar/api/protocol/Transport.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.sf.briar.api.protocol; - -import java.util.Map; -import java.util.TreeMap; - -public class Transport { - - private final TransportId id; - private final TreeMap<String, String> properties; - - public Transport(TransportId id, Map<String, String> p) { - this.id = id; - properties = new TreeMap<String, String>(p); - } - - public Transport(TransportId id) { - this.id = id; - properties = new TreeMap<String, String>(); - } - - public TransportId getId() { - return id; - } - - public Map<String, String> getProperties() { - return properties; - } - - @Override - public int hashCode() { - return id.hashCode() ^ properties.hashCode(); - } - - @Override - public boolean equals(Object o) { - if(o instanceof Transport) { - Transport t = (Transport) o; - return id.equals(t.id) && properties.equals(t.properties); - } - return false; - } -} diff --git a/briar-api/src/net/sf/briar/api/protocol/TransportAck.java b/briar-api/src/net/sf/briar/api/protocol/TransportAck.java new file mode 100644 index 0000000000000000000000000000000000000000..11a9dfdc7fb3640281a0e31f2d1f0ec5cd127914 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/protocol/TransportAck.java @@ -0,0 +1,23 @@ +package net.sf.briar.api.protocol; + +/** A packet acknowledging a {@link TransportUpdate}. */ +public class TransportAck { + + private final TransportId id; + private final long version; + + public TransportAck(TransportId id, long version) { + this.id = id; + this.version = version; + } + + /** Returns the identifier of the updated transport. */ + public TransportId getId() { + return id; + } + + /** Returns the version number of the acknowledged update. */ + public long getVersionNumber() { + return version; + } +} diff --git a/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java b/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java index 48615ccd7e72531f730fad5fce3492333d68f569..b36c32eaa997f067c3e4d18b2fe70ae1e572952e 100644 --- a/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java +++ b/briar-api/src/net/sf/briar/api/protocol/TransportUpdate.java @@ -1,16 +1,33 @@ package net.sf.briar.api.protocol; -import java.util.Collection; +import net.sf.briar.api.TransportProperties; /** A packet updating the sender's transport properties. */ -public interface TransportUpdate { +public class TransportUpdate { - /** Returns the transports contained in the update. */ - Collection<Transport> getTransports(); + private final TransportId id; + private final TransportProperties properties; + private final long version; - /** - * Returns the update's timestamp. Updates that are older than the newest - * update received from the same contact must be ignored. - */ - long getTimestamp(); + public TransportUpdate(TransportId id, TransportProperties properties, + long version) { + this.id = id; + this.properties = properties; + this.version = version; + } + + /** Returns the identifier of the updated transport. */ + public TransportId getId() { + return id; + } + + /** Returns the transport's updated properties. */ + public TransportProperties getProperties() { + return properties; + } + + /** Returns the update's version number. */ + public long getVersionNumber() { + return version; + } } diff --git a/briar-api/src/net/sf/briar/api/protocol/Types.java b/briar-api/src/net/sf/briar/api/protocol/Types.java index 71ec94a397986bcc80fc43798c360248c36373f9..248d6731b61d2a116de2f40c9ca2496a22447225 100644 --- a/briar-api/src/net/sf/briar/api/protocol/Types.java +++ b/briar-api/src/net/sf/briar/api/protocol/Types.java @@ -9,7 +9,8 @@ public interface Types { int MESSAGE = 4; int OFFER = 5; int REQUEST = 6; - int SUBSCRIPTION_UPDATE = 7; - int TRANSPORT = 8; - int TRANSPORT_UPDATE = 9; + int SUBSCRIPTION_ACK = 7; + int SUBSCRIPTION_UPDATE = 8; + int TRANSPORT_ACK = 9; + int TRANSPORT_UPDATE = 10; } diff --git a/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java b/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java index 8fd391d2f84b185442f4a15982a67de47b17919b..89e30e743835baa0e82c8849c4648e156a47b571 100644 --- a/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java +++ b/briar-api/src/net/sf/briar/api/protocol/UnverifiedMessage.java @@ -1,28 +1,61 @@ package net.sf.briar.api.protocol; +/** A {@link Message} that has not yet had its signatures verified. */ public interface UnverifiedMessage { + /** + * Returns the identifier of the message's parent, or null if this is the + * first message in a thread. + */ MessageId getParent(); + /** + * Returns the {@link Group} to which the message belongs, or null if this + * is a private message. + */ Group getGroup(); + /** + * Returns the message's {@link Author}, or null if this is an anonymous + * message. + */ Author getAuthor(); + /** Returns the message's subject line. */ String getSubject(); + /** Returns the timestamp created by the message's {@link Author}. */ long getTimestamp(); + /** Returns the serialised message. */ byte[] getSerialised(); + /** + * Returns the author's signature, or null if this is an anonymous message. + */ byte[] getAuthorSignature(); + /** + * Returns the group's signature, or null if this is a private message or + * a message belonging to an unrestricted group. + */ byte[] getGroupSignature(); + /** Returns the offset of the message body within the serialised message. */ int getBodyStart(); + /** Returns the length of the message body in bytes. */ int getBodyLength(); + /** + * Returns the length in bytes of the data covered by the author's + * signature. + */ int getLengthSignedByAuthor(); + /** + * Returns the length in bytes of the data covered by the group's + * signature. + */ int getLengthSignedByGroup(); } \ No newline at end of file diff --git a/briar-api/src/net/sf/briar/api/serial/Reader.java b/briar-api/src/net/sf/briar/api/serial/Reader.java index e0610feb838b4fe14e5031e69f1241b29a7d5a04..17146a950d34cc9fa603c1a30cec89ca7ae03d5e 100644 --- a/briar-api/src/net/sf/briar/api/serial/Reader.java +++ b/briar-api/src/net/sf/briar/api/serial/Reader.java @@ -18,7 +18,7 @@ public interface Reader { void addConsumer(Consumer c); void removeConsumer(Consumer c); - void addStructReader(int id, StructReader<?> o); + void addStructReader(int id, StructReader<?> r); void removeStructReader(int id); boolean hasBoolean() throws IOException; diff --git a/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar b/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar index 5bce0d4a2ef4d4c9b686b182f0102d904ff38f00..d9bd9fc00289586406f4630f739f0b2a261d29a6 100644 Binary files a/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar and b/briar-core/libs/source/silvertunnel.org-netlib-0.14-briar-source.jar differ diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index c7a9a51c453342f8fee536f46ee22aee868c496d..229e912b63d6059df561e6dac5340c504d309e5a 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -15,8 +15,11 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; import net.sf.briar.api.transport.TemporarySecret; @@ -73,7 +76,7 @@ interface Database<T> { /** * Adds a new contact to the database and returns an ID for the contact. * <p> - * Locking: contact write, subscription write, transport write. + * Locking: contact write, subscription write. */ ContactId addContact(T txn) throws DbException; @@ -85,8 +88,8 @@ interface Database<T> { void addContactTransport(T txn, ContactTransport ct) throws DbException; /** - * Returns false if the given message is already in the database. Otherwise - * stores the message and returns true. + * Stores the given message, or returns false if the message is already in + * the database. * <p> * Locking: message write. */ @@ -108,8 +111,8 @@ interface Database<T> { throws DbException; /** - * Returns false if the given message is already in the database. Otherwise - * stores the message and returns true. + * Stores the given message, or returns false if the message is already in + * the database. * <p> * Locking: contact read, message write. */ @@ -132,18 +135,16 @@ interface Database<T> { void addSubscription(T txn, Group g) throws DbException; /** - * Records the given contact's subscription to the given group starting at - * the given time. + * Adds a new transport to the database. * <p> - * Locking: contact read, subscription write. + * Locking: transport write. */ - void addSubscription(T txn, ContactId c, Group g, long start) - throws DbException; + void addTransport(T txn, TransportId t) throws DbException; /** * Makes the given group visible to the given contact. * <p> - * Locking: contact read, subscription write. + * Locking: contact write, subscription write. */ void addVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -177,23 +178,13 @@ interface Database<T> { boolean containsSubscription(T txn, GroupId g) throws DbException; /** - * Returns true if the user has been subscribed to the given group since - * the given time. - * <p> - * Locking: subscription read. - */ - boolean containsSubscription(T txn, GroupId g, long time) - throws DbException; - - /** - * Returns true if the user is subscribed to the given group, the group is - * visible to the given contact, and the subscription has existed since the - * given time. + * Returns true if the user subscribes to the given group and the + * subscription is visible to the given contact. * <p> * Locking: contact read, subscription read. */ - boolean containsVisibleSubscription(T txn, GroupId g, ContactId c, - long time) throws DbException; + boolean containsVisibleSubscription(T txn, ContactId c, GroupId g) + throws DbException; /** * Returns the configuration for the given transport. @@ -249,13 +240,6 @@ interface Database<T> { TransportProperties getLocalProperties(T txn, TransportId t) throws DbException; - /** - * Returns all local transports. - * <p> - * Locking: transport read. - */ - Collection<Transport> getLocalTransports(T txn) throws DbException; - /** * Returns the message identified by the given ID, in serialised form. * <p> @@ -402,53 +386,55 @@ interface Database<T> { Collection<Group> getSubscriptions(T txn, ContactId c) throws DbException; /** - * Returns the time at which the local transports were last modified. + * Returns a subscription ack for the given contact, or null if no ack is + * due. * <p> - * Locking: transport read. + * Locking: contact read, subscription write. */ - long getTransportsModified(T txn) throws DbException; + SubscriptionAck getSubscriptionAck(T txn, ContactId c) throws DbException; /** - * Returns the time at which a transport update was last sent to the given - * contact. + * Returns a subscription update for the given contact, or null if no + * update is due. * <p> - * Locking: contact read, transport read. + * Locking: contact read, subscription write. */ - long getTransportsSent(T txn, ContactId c) throws DbException; + SubscriptionUpdate getSubscriptionUpdate(T txn, ContactId c) + throws DbException; /** - * Returns the number of unread messages in each subscribed group. + * Returns a collection of transport acks for the given contact, or null if + * no acks are due. * <p> - * Locking: message read, messageFlag read, subscription read. + * Locking: contact read, transport write. */ - Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException; + Collection<TransportAck> getTransportAcks(T txn, ContactId c) + throws DbException; /** - * Returns the contacts to which the given group is visible. + * Returns a collection of transport updates for the given contact, or + * null if no updates are due. * <p> - * Locking: contact read, subscription read. + * Locking: contact read, transport write. */ - Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException; + Collection<TransportUpdate> getTransportUpdates(T txn, ContactId c) + throws DbException; /** - * Returns any holes covering unsubscriptions that are visible to the given - * contact, occurred strictly before the given timestamp, and have not yet - * been acknowledged. + * Returns the version number of the + /** + * Returns the number of unread messages in each subscribed group. * <p> - * Locking: contact read, subscription read. + * Locking: message read, messageFlag read, subscription read. */ - Map<GroupId, GroupId> getVisibleHoles(T txn, ContactId c, long timestamp) - throws DbException; + Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException; /** - * Returns any subscriptions that are visible to the given contact, - * occurred strictly before the given timestamp, and have not yet been - * acknowledged. + * Returns the contacts to which the given group is visible. * <p> * Locking: contact read, subscription read. */ - Map<Group, Long> getVisibleSubscriptions(T txn, ContactId c, long timestamp) - throws DbException; + Collection<ContactId> getVisibility(T txn, GroupId g) throws DbException; /** * Returns true if any messages are sendable to the given contact. @@ -523,25 +509,22 @@ interface Database<T> { * Unsubscribes from the given group. Any messages belonging to the group * are deleted from the database. * <p> - * Locking: contact read, message write, messageFlag write, + * Locking: contact write, message write, messageFlag write, * messageStatus write, subscription write. */ void removeSubscription(T txn, GroupId g) throws DbException; /** - * Removes any subscriptions for the given contact with IDs between the - * given IDs. If both of the given IDs are null, all subscriptions are - * removed. If only the first is null, all subscriptions with IDs less than - * the second ID are removed. If onlt the second is null, all subscriptions - * with IDs greater than the first are removed. + * Removes a transport (and all associated state) from the database. + * <p> + * Locking: contact read, transport write. */ - void removeSubscriptions(T txn, ContactId c, GroupId start, GroupId end) - throws DbException; + void removeTransport(T txn, TransportId t) throws DbException; /** * Makes the given group invisible to the given contact. * <p> - * Locking: contact read, subscription write. + * Locking: contact write, subscription write. */ void removeVisibility(T txn, ContactId c, GroupId g) throws DbException; @@ -557,7 +540,7 @@ interface Database<T> { /** * Sets the given contact's database expiry time. * <p> - * Locking: contact read, subscription write. + * Locking: contact write. */ void setExpiryTime(T txn, ContactId c, long expiry) throws DbException; @@ -576,6 +559,17 @@ interface Database<T> { */ boolean setRead(T txn, MessageId m, boolean read) throws DbException; + /** + * Updates the remote transport properties for the given contact and the + * given transport, replacing any existing properties, unless an update + * with an equal or higher version number has already been received from + * the contact. + * <p> + * Locking: contact read, transport write. + */ + void setRemoteProperties(T txn, ContactId c, TransportUpdate t) + throws DbException; + /** * Sets the sendability score of the given message. * <p> @@ -612,45 +606,30 @@ interface Database<T> { throws DbException; /** - * Records the time of the latest subscription update acknowledged by the - * given contact. + * Updates the groups to which the given contact subscribes, unless an + * update with an equal or higher version number has already been received + * from the contact. * <p> * Locking: contact read, subscription write. */ - void setSubscriptionsAcked(T txn, ContactId c, long timestamp) + void setSubscriptions(T txn, ContactId c, SubscriptionUpdate s) throws DbException; /** - * Records the time of the latest subscription update received from the - * given contact. + * Records a subscription ack from the given contact for the given version + * unless the contact has already acked an equal or higher version. * <p> * Locking: contact read, subscription write. */ - void setSubscriptionsReceived(T txn, ContactId c, long timestamp) + void setSubscriptionUpdateAcked(T txn, ContactId c, long version) throws DbException; /** - * Sets the transports for the given contact, replacing any existing - * transports unless the existing transports have a newer timestamp. - * <p> - * Locking: contact read, transport write. - */ - void setTransports(T txn, ContactId c, Collection<Transport> transports, - long timestamp) throws DbException; - - /** - * Records the time at which the local transports were last modified. - * <p> - * Locking: contact read, transport write. - */ - void setTransportsModified(T txn, long timestamp) throws DbException; - - /** - * Records the time at which a transport update was last sent to the given - * contact. + * Records a transport ack from the give contact for the given version + * unless the contact has already acked an equal or higher version. * <p> * Locking: contact read, transport write. */ - void setTransportsSent(T txn, ContactId c, long timestamp) - throws DbException; + void setTransportUpdateAcked(T txn, ContactId c, TransportId t, + long version) throws DbException; } diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index d9333bcd2bf4e0fb31d204539ba61f87530acc5a..d1308b3b1acb7bfd6ab746c8cf50a48c120a76b6 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -5,7 +5,6 @@ import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP; import static net.sf.briar.db.DatabaseConstants.CRITICAL_FREE_SPACE; import static net.sf.briar.db.DatabaseConstants.MAX_BYTES_BETWEEN_SPACE_CHECKS; import static net.sf.briar.db.DatabaseConstants.MAX_MS_BETWEEN_SPACE_CHECKS; -import static net.sf.briar.db.DatabaseConstants.MAX_UPDATE_INTERVAL; import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE; import java.io.IOException; @@ -17,7 +16,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Logger; @@ -36,12 +34,11 @@ import net.sf.briar.api.db.event.ContactAddedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; -import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; +import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; @@ -50,10 +47,10 @@ import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; @@ -98,7 +95,6 @@ DatabaseCleaner.Callback { private final Database<T> db; private final DatabaseCleaner cleaner; private final ShutdownManager shutdown; - private final PacketFactory packetFactory; private final Clock clock; private final Collection<DatabaseListener> listeners = @@ -114,12 +110,10 @@ DatabaseCleaner.Callback { @Inject DatabaseComponentImpl(Database<T> db, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory, - Clock clock) { + ShutdownManager shutdown, Clock clock) { this.db = db; this.cleaner = cleaner; this.shutdown = shutdown; - this.packetFactory = packetFactory; this.clock = clock; } @@ -173,23 +167,13 @@ DatabaseCleaner.Callback { try { subscriptionLock.writeLock().lock(); try { - transportLock.writeLock().lock(); + T txn = db.startTransaction(); try { - windowLock.writeLock().lock(); - try { - T txn = db.startTransaction(); - try { - c = db.addContact(txn); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - windowLock.writeLock().unlock(); - } - } finally { - transportLock.writeLock().unlock(); + c = db.addContact(txn); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { subscriptionLock.writeLock().unlock(); @@ -243,12 +227,9 @@ DatabaseCleaner.Callback { T txn = db.startTransaction(); try { // Don't store the message if the user has - // unsubscribed from the group or the message - // predates the subscription - if(db.containsSubscription(txn, m.getGroup(), - m.getTimestamp())) { + // unsubscribed from the group + if(db.containsSubscription(txn, m.getGroup())) added = storeGroupMessage(txn, m, null); - } db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -418,11 +399,27 @@ DatabaseCleaner.Callback { } } + public void addTransport(TransportId t) throws DbException { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + db.addTransport(txn, t); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + } + /** - * If the given message is already in the database, returns false. * Otherwise stores the message and marks it as new or seen with respect to * the given contact, depending on whether the message is outgoing or - * incoming, respectively. + * incoming, respectively; or returns false if the message is already in + * the database. * <p> * Locking: contact read, message write, messageStatus write. */ @@ -478,7 +475,7 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createAck(acked); + return new Ack(acked); } public Collection<byte[]> generateBatch(ContactId c, int maxLength) @@ -627,80 +624,94 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createOffer(offered); + return new Offer(offered); } - public SubscriptionUpdate generateSubscriptionUpdate(ContactId c) + public SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException { - Map<GroupId, GroupId> holes; - Map<Group, Long> subs; - long expiry, timestamp; contactLock.readLock().lock(); try { - subscriptionLock.readLock().lock(); + subscriptionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - timestamp = clock.currentTimeMillis() - 1; - holes = db.getVisibleHoles(txn, c, timestamp); - subs = db.getVisibleSubscriptions(txn, c, timestamp); - expiry = db.getExpiryTime(txn); + SubscriptionAck a = db.getSubscriptionAck(txn, c); db.commitTransaction(txn); + return a; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - subscriptionLock.readLock().unlock(); + subscriptionLock.writeLock().unlock(); } } finally { contactLock.readLock().unlock(); } - return packetFactory.createSubscriptionUpdate(holes, subs, expiry, - timestamp); } - private boolean updateIsDue(long sent) { - long now = clock.currentTimeMillis(); - return now - sent >= MAX_UPDATE_INTERVAL; - } - - public TransportUpdate generateTransportUpdate(ContactId c) + public SubscriptionUpdate generateSubscriptionUpdate(ContactId c) throws DbException { - boolean due; - Collection<Transport> transports; - long timestamp; contactLock.readLock().lock(); try { - transportLock.readLock().lock(); + subscriptionLock.writeLock().lock(); try { T txn = db.startTransaction(); try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - // Work out whether an update is due - long modified = db.getTransportsModified(txn); - long sent = db.getTransportsSent(txn, c); - due = modified >= sent || updateIsDue(sent); + SubscriptionUpdate s = db.getSubscriptionUpdate(txn, c); db.commitTransaction(txn); + return s; } catch(DbException e) { db.abortTransaction(txn); throw e; } } finally { - transportLock.readLock().unlock(); + subscriptionLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + + public Collection<TransportAck> generateTransportAcks(ContactId c) + throws DbException { + contactLock.readLock().lock(); + try { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + Collection<TransportAck> acks = db.getTransportAcks(txn, c); + db.commitTransaction(txn); + return acks; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); } - if(!due) return null; + } finally { + contactLock.readLock().unlock(); + } + } + + public Collection<TransportUpdate> generateTransportUpdates(ContactId c) + throws DbException { + contactLock.readLock().lock(); + try { transportLock.writeLock().lock(); try { T txn = db.startTransaction(); try { - transports = db.getLocalTransports(txn); - timestamp = clock.currentTimeMillis(); - db.setTransportsSent(txn, c, timestamp); + Collection<TransportUpdate> updates = + db.getTransportUpdates(txn, c); db.commitTransaction(txn); + return updates; } catch(DbException e) { db.abortTransaction(txn); throw e; @@ -711,7 +722,6 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createTransportUpdate(transports, timestamp); } public TransportConfig getConfig(TransportId t) throws DbException { @@ -766,23 +776,6 @@ DatabaseCleaner.Callback { } } - public Collection<Transport> getLocalTransports() throws DbException { - transportLock.readLock().lock(); - try { - T txn = db.startTransaction(); - try { - Collection<Transport> transports = db.getLocalTransports(txn); - db.commitTransaction(txn); - return transports; - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - transportLock.readLock().unlock(); - } - } - public Collection<MessageHeader> getMessageHeaders(GroupId g) throws DbException { messageLock.readLock().lock(); @@ -1022,7 +1015,6 @@ DatabaseCleaner.Callback { try { if(!p.equals(db.getLocalProperties(txn, t))) { db.mergeLocalProperties(txn, t, p); - db.setTransportsModified(txn, clock.currentTimeMillis()); changed = true; } db.commitTransaction(txn); @@ -1034,7 +1026,7 @@ DatabaseCleaner.Callback { transportLock.writeLock().unlock(); } // Call the listeners outside the lock - if(changed) callListeners(new LocalTransportsUpdatedEvent()); + if(changed) callListeners(new TransportsUpdatedEvent()); } public void receiveAck(ContactId c, Ack a) throws DbException { @@ -1114,8 +1106,7 @@ DatabaseCleaner.Callback { throws DbException { GroupId g = m.getGroup(); if(g == null) return storePrivateMessage(txn, m, c, true); - if(!db.containsVisibleSubscription(txn, g, c, m.getTimestamp())) - return false; + if(!db.containsVisibleSubscription(txn, c, g)) return false; return storeGroupMessage(txn, m, c); } @@ -1161,12 +1152,36 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - return packetFactory.createRequest(request, offered.size()); + return new Request(request, offered.size()); + } + + public void receiveSubscriptionAck(ContactId c, SubscriptionAck a) + throws DbException { + contactLock.readLock().lock(); + try { + subscriptionLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + long version = a.getVersionNumber(); + db.setSubscriptionUpdateAcked(txn, c, version); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + subscriptionLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } } public void receiveSubscriptionUpdate(ContactId c, SubscriptionUpdate s) throws DbException { - // Update the contact's subscriptions contactLock.readLock().lock(); try { subscriptionLock.writeLock().lock(); @@ -1175,17 +1190,7 @@ DatabaseCleaner.Callback { try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - Map<GroupId, GroupId> holes = s.getHoles(); - for(Entry<GroupId, GroupId> e : holes.entrySet()) { - GroupId start = e.getKey(), end = e.getValue(); - db.removeSubscriptions(txn, c, start, end); - } - Map<Group, Long> subs = s.getSubscriptions(); - for(Entry<Group, Long> e : subs.entrySet()) { - db.addSubscription(txn, c, e.getKey(), e.getValue()); - } - db.setExpiryTime(txn, c, s.getExpiryTime()); - db.setSubscriptionsReceived(txn, c, s.getTimestamp()); + db.setSubscriptions(txn, c, s); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -1197,15 +1202,36 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - // Call the listeners outside the lock - callListeners(new SubscriptionsUpdatedEvent( - Collections.singletonList(c))); + } + + public void receiveTransportAck(ContactId c, TransportAck a) + throws DbException { + contactLock.readLock().lock(); + try { + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if(!db.containsContact(txn, c)) + throw new NoSuchContactException(); + TransportId t = a.getId(); + long version = a.getVersionNumber(); + db.setTransportUpdateAcked(txn, c, t, version); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } } public void receiveTransportUpdate(ContactId c, TransportUpdate t) throws DbException { - Collection<Transport> transports; - // Update the contact's transport properties contactLock.readLock().lock(); try { transportLock.writeLock().lock(); @@ -1214,8 +1240,7 @@ DatabaseCleaner.Callback { try { if(!db.containsContact(txn, c)) throw new NoSuchContactException(); - transports = t.getTransports(); - db.setTransports(txn, c, transports, t.getTimestamp()); + db.setRemoteProperties(txn, c, t); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -1227,8 +1252,6 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - // Call the listeners outside the lock - callListeners(new RemoteTransportsUpdatedEvent(c, transports)); } public void removeContact(ContactId c) throws DbException { @@ -1398,8 +1421,7 @@ DatabaseCleaner.Callback { public void setVisibility(GroupId g, Collection<ContactId> visible) throws DbException { - List<ContactId> affected = new ArrayList<ContactId>(); - contactLock.readLock().lock(); + contactLock.writeLock().lock(); try { subscriptionLock.writeLock().lock(); try { @@ -1413,13 +1435,8 @@ DatabaseCleaner.Callback { for(ContactId c : db.getContacts(txn)) { boolean then = oldVisible.contains(c); boolean now = visible.contains(c); - if(!then && now) { - db.addVisibility(txn, c, g); - affected.add(c); - } else if(then && !now) { - db.removeVisibility(txn, c, g); - affected.add(c); - } + if(!then && now) db.addVisibility(txn, c, g); + else if(then && !now) db.removeVisibility(txn, c, g); } db.commitTransaction(txn); } catch(DbException e) { @@ -1430,12 +1447,7 @@ DatabaseCleaner.Callback { subscriptionLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); - } - // Call the listeners outside the lock - if(!affected.isEmpty()) { - affected = Collections.unmodifiableList(affected); - callListeners(new SubscriptionsUpdatedEvent(affected)); + contactLock.writeLock().unlock(); } } @@ -1459,7 +1471,7 @@ DatabaseCleaner.Callback { public void unsubscribe(GroupId g) throws DbException { Collection<ContactId> affected = null; - contactLock.readLock().lock(); + contactLock.writeLock().lock(); try { messageLock.writeLock().lock(); try { @@ -1493,7 +1505,7 @@ DatabaseCleaner.Callback { messageLock.writeLock().unlock(); } } finally { - contactLock.readLock().unlock(); + contactLock.writeLock().unlock(); } // Call the listeners outside the lock if(affected != null && !affected.isEmpty()) diff --git a/briar-core/src/net/sf/briar/db/DatabaseModule.java b/briar-core/src/net/sf/briar/db/DatabaseModule.java index 6ed65d9b8d1f01422014908aa49e708b4211d15c..ce5cdcf6bc2a13815921eb30f95520721b5f6087 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseModule.java +++ b/briar-core/src/net/sf/briar/db/DatabaseModule.java @@ -8,7 +8,6 @@ import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseConfig; import net.sf.briar.api.db.DatabaseExecutor; import net.sf.briar.api.lifecycle.ShutdownManager; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.util.BoundedExecutor; import com.google.inject.AbstractModule; @@ -41,15 +40,14 @@ public class DatabaseModule extends AbstractModule { } @Provides - Database<Connection> getDatabase(Clock clock, DatabaseConfig config) { - return new H2Database(clock, config); + Database<Connection> getDatabase(DatabaseConfig config) { + return new H2Database(config); } @Provides @Singleton DatabaseComponent getDatabaseComponent(Database<Connection> db, - DatabaseCleaner cleaner, ShutdownManager shutdown, - PacketFactory packetFactory, Clock clock) { + DatabaseCleaner cleaner, ShutdownManager shutdown, Clock clock) { return new DatabaseComponentImpl<Connection>(db, cleaner, shutdown, - packetFactory, clock); + clock); } } diff --git a/briar-core/src/net/sf/briar/db/H2Database.java b/briar-core/src/net/sf/briar/db/H2Database.java index 4c00ec8c22e5637f9a8fca78fd4546a88018ccf9..471b67910638892d27015de4b6843f3983295450 100644 --- a/briar-core/src/net/sf/briar/db/H2Database.java +++ b/briar-core/src/net/sf/briar/db/H2Database.java @@ -30,8 +30,8 @@ class H2Database extends JdbcDatabase { private final long maxSize; @Inject - H2Database(Clock clock, DatabaseConfig config) { - super(clock, HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE); + H2Database(DatabaseConfig config) { + super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE); home = new File(config.getDataDirectory(), "db"); url = "jdbc:h2:split:" + home.getPath() + ";CIPHER=AES;DB_CLOSE_ON_EXIT=false"; diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index d5a4f5544df7fc26e0073d1462107b2d1122fae4..708595a0b22adef9b0baeec6b61d7807105d7017 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -27,7 +27,6 @@ import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; -import net.sf.briar.api.clock.Clock; import net.sf.briar.api.db.DbClosedException; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; @@ -36,8 +35,11 @@ import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.ContactTransport; import net.sf.briar.api.transport.TemporarySecret; import net.sf.briar.util.FileUtils; @@ -48,19 +50,14 @@ import net.sf.briar.util.FileUtils; */ abstract class JdbcDatabase implements Database<Connection> { - private static final String CREATE_SUBSCRIPTIONS = - "CREATE TABLE subscriptions" - + " (groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (groupId))"; - + // Locking: contact private static final String CREATE_CONTACTS = "CREATE TABLE contacts" + " (contactId COUNTER," + + " expiry BIGINT NOT NULL DEFAULT 0," // FIXME: Move this + " PRIMARY KEY (contactId))"; + // Locking: message private static final String CREATE_MESSAGES = "CREATE TABLE messages" + " (messageId HASH NOT NULL," @@ -77,9 +74,10 @@ abstract class JdbcDatabase implements Database<Connection> { + " contactId INT," // Null for group messages + " PRIMARY KEY (messageId)," + " FOREIGN KEY (groupId)" - + " REFERENCES subscriptions (groupId)" + + " REFERENCES groups (groupId)" + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; private static final String INDEX_MESSAGES_BY_PARENT = @@ -94,58 +92,28 @@ abstract class JdbcDatabase implements Database<Connection> { private static final String INDEX_MESSAGES_BY_SENDABILITY = "CREATE INDEX messagesBySendability ON messages (sendability)"; - private static final String CREATE_VISIBILITIES = - "CREATE TABLE visibilities" - + " (contactId INT NOT NULL," - + " groupId HASH," // Null for the head of the linked list - + " nextId HASH," // Null for the tail of the linked list - + " deleted BIGINT NOT NULL," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE," - + " FOREIGN KEY (groupId)" - + " REFERENCES subscriptions (groupId)" - + " ON DELETE CASCADE)"; - - private static final String INDEX_VISIBILITIES_BY_GROUP = - "CREATE INDEX visibilitiesByGroup ON visibilities (groupId)"; - - private static final String INDEX_VISIBILITIES_BY_NEXT = - "CREATE INDEX visibilitiesByNext on visibilities (nextId)"; - + // Locking: contact read, messageStatus private static final String CREATE_MESSAGES_TO_ACK = "CREATE TABLE messagesToAck" + " (messageId HASH NOT NULL," + " contactId INT NOT NULL," + " PRIMARY KEY (messageId, contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_CONTACT_SUBSCRIPTIONS = - "CREATE TABLE contactSubscriptions" - + " (contactId INT NOT NULL," - + " groupId HASH NOT NULL," - + " groupName VARCHAR NOT NULL," - + " groupKey BINARY," // Null for unrestricted groups - + " start BIGINT NOT NULL," - + " PRIMARY KEY (contactId, groupId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; - private static final String CREATE_RATINGS = - "CREATE TABLE ratings" - + " (authorId HASH NOT NULL," - + " rating SMALLINT NOT NULL," - + " PRIMARY KEY (authorId))"; - + // Locking: contact read, message read, messageStatus private static final String CREATE_STATUSES = "CREATE TABLE statuses" + " (messageId HASH NOT NULL," + " contactId INT NOT NULL," + " status SMALLINT NOT NULL," + " PRIMARY KEY (messageId, contactId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " FOREIGN KEY (messageId)" + + " REFERENCES messages (messageId)" + " ON DELETE CASCADE," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; private static final String INDEX_STATUSES_BY_MESSAGE = @@ -154,30 +122,137 @@ abstract class JdbcDatabase implements Database<Connection> { private static final String INDEX_STATUSES_BY_CONTACT = "CREATE INDEX statusesByContact ON statuses (contactId)"; + // Locking: message read, messageFlag + private static final String CREATE_FLAGS = + "CREATE TABLE flags" + + " (messageId HASH NOT NULL," + + " read BOOLEAN NOT NULL," + + " starred BOOLEAN NOT NULL," + + " PRIMARY KEY (messageId)," + + " FOREIGN KEY (messageId)" + + " REFERENCES messages (messageId)" + + " ON DELETE CASCADE)"; + + // Locking: rating + private static final String CREATE_RATINGS = + "CREATE TABLE ratings" + + " (authorId HASH NOT NULL," + + " rating SMALLINT NOT NULL," + + " PRIMARY KEY (authorId))"; + + // Locking: subscription + private static final String CREATE_GROUPS = + "CREATE TABLE groups" + + " (groupId HASH NOT NULL," + + " name VARCHAR NOT NULL," + + " key BINARY," // Null for unrestricted groups + + " PRIMARY KEY (groupId))"; + + // Locking: contact read, subscription + private static final String CREATE_GROUP_VISIBILITIES = + "CREATE TABLE groupVisibilities" + + " (contactId INT NOT NULL," + + " groupId HASH NOT NULL," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (groupId)" + + " REFERENCES groups (groupId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, subscription + private static final String CREATE_CONTACT_GROUPS = + "CREATE TABLE contactGroups" + + " (contactId INT NOT NULL," + + " groupId HASH NOT NULL," // Not a foreign key + + " name VARCHAR NOT NULL," + + " key BINARY," // Null for unrestricted groups + + " PRIMARY KEY (contactId, groupId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, subscription + private static final String CREATE_GROUP_VERSIONS = + "CREATE TABLE groupVersions" + + " (contactId INT NOT NULL," + + " localVersion BIGINT NOT NULL," + + " localAcked BIGINT NOT NULL," + + " remoteVersion BIGINT NOT NULL," + + " remoteAcked BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactid)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: transport + private static final String CREATE_TRANSPORTS = + "CREATE TABLE transports" + + " (transportId HASH NOT NULL," + + " PRIMARY KEY (transportId))"; + + // Locking: transport private static final String CREATE_TRANSPORT_CONFIGS = "CREATE TABLE transportConfigs" + " (transportId HASH NOT NULL," + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; + + " PRIMARY KEY (transportId, key)," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + // Locking: transport private static final String CREATE_TRANSPORT_PROPS = "CREATE TABLE transportProperties" + " (transportId HASH NOT NULL," + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," - + " PRIMARY KEY (transportId, key))"; + + " PRIMARY KEY (transportId, key)," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, transport + private static final String CREATE_TRANSPORT_VERSIONS = + "CREATE TABLE transportVersions" + + " (contactId HASH NOT NULL," + + " transportId HASH NOT NULL," + + " localVersion BIGINT NOT NULL," + + " localAcked BIGINT NOT NULL," + + " PRIMARY KEY (contactId, transportId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + + " ON DELETE CASCADE)"; + // Locking: contact read, transport private static final String CREATE_CONTACT_TRANSPORT_PROPS = "CREATE TABLE contactTransportProperties" + " (contactId INT NOT NULL," - + " transportId HASH NOT NULL," + + " transportId HASH NOT NULL," // Not a foreign key + " key VARCHAR NOT NULL," + " value VARCHAR NOT NULL," + " PRIMARY KEY (contactId, transportId, key)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + // Locking: contact read, transport + private static final String CREATE_CONTACT_TRANSPORT_VERSIONS = + "CREATE TABLE contactTransportVersions" + + " (contactId HASH NOT NULL," + + " transportId HASH NOT NULL," // Not a foreign key + + " remoteVersion BIGINT NOT NULL," + + " remoteAcked BOOLEAN NOT NULL," + + " PRIMARY KEY (contactId, transportId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; + // Locking: contact read, window private static final String CREATE_CONTACT_TRANSPORTS = "CREATE TABLE contactTransports" + " (contactId INT NOT NULL," @@ -187,9 +262,14 @@ abstract class JdbcDatabase implements Database<Connection> { + " latency BIGINT NOT NULL," + " alice BOOLEAN NOT NULL," + " PRIMARY KEY (contactId, transportId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; + // Locking: contact read, window private static final String CREATE_SECRETS = "CREATE TABLE secrets" + " (contactId INT NOT NULL," @@ -200,42 +280,16 @@ abstract class JdbcDatabase implements Database<Connection> { + " centre BIGINT NOT NULL," + " bitmap BINARY NOT NULL," + " PRIMARY KEY (contactId, transportId, period)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_SUBSCRIPTION_TIMES = - "CREATE TABLE subscriptionTimes" - + " (contactId INT NOT NULL," - + " received BIGINT NOT NULL," - + " acked BIGINT NOT NULL," - + " expiry BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_TRANSPORT_TIMESTAMPS = - "CREATE TABLE transportTimestamps" - + " (contactId INT NOT NULL," - + " sent BIGINT NOT NULL," - + " received BIGINT NOT NULL," - + " modified BIGINT NOT NULL," - + " PRIMARY KEY (contactId)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - - private static final String CREATE_FLAGS = - "CREATE TABLE flags" - + " (messageId HASH NOT NULL," - + " read BOOLEAN NOT NULL," - + " starred BOOLEAN NOT NULL," - + " PRIMARY KEY (messageId)," - + " FOREIGN KEY (messageId) REFERENCES messages (messageId)" + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (transportId)" + + " REFERENCES transports (transportId)" + " ON DELETE CASCADE)"; private static final Logger LOG = Logger.getLogger(JdbcDatabase.class.getName()); - private final Clock clock; // Different database libraries use different names for certain types private final String hashType, binaryType, counterType, secretType; @@ -247,9 +301,8 @@ abstract class JdbcDatabase implements Database<Connection> { protected abstract Connection createConnection() throws SQLException; - JdbcDatabase(Clock clock, String hashType, String binaryType, - String counterType, String secretType) { - this.clock = clock; + JdbcDatabase(String hashType, String binaryType, String counterType, + String secretType) { this.hashType = hashType; this.binaryType = binaryType; this.counterType = counterType; @@ -286,30 +339,30 @@ abstract class JdbcDatabase implements Database<Connection> { Statement s = null; try { s = txn.createStatement(); - s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACTS)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES)); s.executeUpdate(INDEX_MESSAGES_BY_PARENT); s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR); s.executeUpdate(INDEX_MESSAGES_BY_TIMESTAMP); s.executeUpdate(INDEX_MESSAGES_BY_SENDABILITY); - s.executeUpdate(insertTypeNames(CREATE_VISIBILITIES)); - s.executeUpdate(INDEX_VISIBILITIES_BY_GROUP); - s.executeUpdate(INDEX_VISIBILITIES_BY_NEXT); s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK)); - s.executeUpdate(insertTypeNames(CREATE_CONTACT_SUBSCRIPTIONS)); - s.executeUpdate(insertTypeNames(CREATE_RATINGS)); s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_CONTACT); + s.executeUpdate(insertTypeNames(CREATE_FLAGS)); + s.executeUpdate(insertTypeNames(CREATE_RATINGS)); + s.executeUpdate(insertTypeNames(CREATE_GROUPS)); + s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS)); + s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_SECRETS)); - s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMES)); - s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS)); - s.executeUpdate(insertTypeNames(CREATE_FLAGS)); s.close(); } catch(SQLException e) { tryToClose(s); @@ -427,7 +480,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - // Create a new contact row + // Create a contact row String sql = "INSERT INTO contacts DEFAULT VALUES"; ps = txn.prepareStatement(sql); int affected = ps.executeUpdate(); @@ -444,31 +497,41 @@ abstract class JdbcDatabase implements Database<Connection> { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - // Create the head-of-list pointer for the visibility list - sql = "INSERT INTO visibilities (contactId, deleted)" - + " VALUES (?, ZERO())"; + // Create a group version row + sql = "INSERT INTO groupVersions (contactId, localVersion," + + " localAcked, remoteVersion, remoteAcked)" + + " VALUES (?, ?, ZERO(), ZERO(), TRUE)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); + ps.setInt(2, 1); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - // Initialise the subscription timestamps - sql = "INSERT INTO subscriptionTimes" - + " (contactId, received, acked, expiry)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + // Create a transport version row for each local transport + sql = "SELECT transportId FROM transports"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); + rs = ps.executeQuery(); + Collection<byte[]> transports = new ArrayList<byte[]>(); + while(rs.next()) transports.add(rs.getBytes(1)); + rs.close(); ps.close(); - // Initialise the transport timestamps - sql = "INSERT INTO transportTimestamps" - + " (contactId, sent, received, modified)" - + " VALUES (?, ZERO(), ZERO(), ZERO())"; + if(transports.isEmpty()) return c; + sql = "INSERT INTO transportVersions" + + " (contactId, transportId, localVersion, localAcked)" + + " VALUES (?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); + ps.setInt(3, 1); + for(byte[] t : transports) { + ps.setBytes(2, t); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != transports.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); + } ps.close(); return c; } catch(SQLException e) { @@ -482,9 +545,8 @@ abstract class JdbcDatabase implements Database<Connection> { throws DbException { PreparedStatement ps = null; try { - String sql = "INSERT INTO contactTransports" - + " (contactId, transportId, epoch, clockDiff, latency," - + " alice)" + String sql = "INSERT INTO contactTransports (contactId," + + " transportId, epoch, clockDiff, latency, alice)" + " VALUES (?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, ct.getContactId().getInt()); @@ -630,15 +692,12 @@ abstract class JdbcDatabase implements Database<Connection> { public void addSubscription(Connection txn, Group g) throws DbException { PreparedStatement ps = null; try { - String sql = "INSERT INTO subscriptions" - + " (groupId, groupName, groupKey, start)" - + " VALUES (?, ?, ?, ?)"; + String sql = "INSERT INTO groups" + + " (groupId, name, key) VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getId().getBytes()); ps.setString(2, g.getName()); ps.setBytes(3, g.getPublicKey()); - long now = clock.currentTimeMillis(); - ps.setLong(4, now); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -653,9 +712,8 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { // Store the new secrets - String sql = "INSERT INTO secrets" - + " (contactId, transportId, period, secret, outgoing," - + " centre, bitmap)" + String sql = "INSERT INTO secrets (contactId, transportId, period," + + " secret, outgoing, centre, bitmap)" + " VALUES (?, ?, ?, ?, ?, ?, ?)"; ps = txn.prepareStatement(sql); for(TemporarySecret s : secrets) { @@ -698,39 +756,45 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void addSubscription(Connection txn, ContactId c, Group g, - long start) throws DbException { + public void addTransport(Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - // Check whether the subscription already exists - String sql = "SELECT NULL FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId = ?"; + // Create a transport row + String sql = "INSERT INTO transports (transportId) VALUES (?)"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + // Create a transport version row for each contact + sql = "SELECT contactId FROM contacts"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getId().getBytes()); rs = ps.executeQuery(); - boolean found = rs.next(); - if(rs.next()) throw new DbStateException(); + Collection<Integer> contacts = new ArrayList<Integer>(); + while(rs.next()) contacts.add(rs.getInt(1)); rs.close(); ps.close(); - if(found) return; - // Add the subscription - sql = "INSERT INTO contactSubscriptions" - + " (contactId, groupId, groupName, groupKey, start)" - + " VALUES (?, ?, ?, ?, ?)"; + if(contacts.isEmpty()) return; + sql = "INSERT INTO transportVersions" + + " (contactId, transportId, localVersion, localAcked)" + + " VALUES (?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getId().getBytes()); - ps.setString(3, g.getName()); - ps.setBytes(4, g.getPublicKey()); - ps.setLong(5, start); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); + ps.setBytes(2, t.getBytes()); + ps.setInt(3, 1); + for(Integer c : contacts) { + ps.setInt(1, c); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != contacts.size()) + throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } @@ -738,71 +802,25 @@ abstract class JdbcDatabase implements Database<Connection> { public void addVisibility(Connection txn, ContactId c, GroupId g) throws DbException { PreparedStatement ps = null; - ResultSet rs = null; try { - // Find the new element's predecessor - byte[] groupId = null, nextId = null; - long deleted = 0L; - String sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId > ?" - + " ORDER BY nextId LIMIT ?"; + String sql = "INSERT INTO groupVisibilities (contactId, groupId)" + + " VALUES (?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - ps.setInt(3, 1); - rs = ps.executeQuery(); - if(!rs.next()) { - // The predecessor has a null nextId so it's at the tail - rs.close(); - ps.close(); - sql = "SELECT groupId, nextId, deleted FROM visibilities" - + " WHERE contactId = ? AND nextId IS NULL"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - } - groupId = rs.getBytes(1); - nextId = rs.getBytes(2); - deleted = rs.getLong(3); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - // Update the predecessor's nextId - if(groupId == null) { - // Inserting at the head of the list - sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId IS NULL"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setInt(2, c.getInt()); - } else { - // Inserting in the middle or at the tail of the list - sql = "UPDATE visibilities SET nextId = ?" - + " WHERE contactId = ? AND groupId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setInt(2, c.getInt()); - ps.setBytes(3, groupId); - } + ps.setBytes(3, g.getBytes()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - // Insert the new element - sql = "INSERT INTO visibilities" - + " (contactId, groupId, nextId, deleted)" - + " VALUES (?, ?, ?, ?)"; + // Bump the subscription version + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - if(nextId == null) ps.setNull(3, BINARY); // At the tail - else ps.setBytes(3, nextId); // In the middle - ps.setLong(4, deleted); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); throw new DbException(e); } @@ -878,7 +896,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT NULL FROM subscriptions WHERE groupId = ?"; + String sql = "SELECT NULL FROM groups WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); @@ -894,16 +912,18 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public boolean containsSubscription(Connection txn, GroupId g, long time) - throws DbException { + public boolean containsVisibleSubscription(Connection txn, ContactId c, + GroupId g) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT NULL FROM subscriptions" - + " WHERE groupId = ? AND start <= ?"; + String sql = "SELECT NULL FROM groups AS g" + + " JOIN groupVisibilities AS gv" + + " ON g.groupId = gv.groupId" + + " WHERE g.groupId = ? AND contactId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); - ps.setLong(2, time); + ps.setInt(2, c.getInt()); rs = ps.executeQuery(); boolean found = rs.next(); if(rs.next()) throw new DbStateException(); @@ -917,35 +937,6 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public boolean containsVisibleSubscription(Connection txn, GroupId g, - ContactId c, long time) throws DbException { - boolean found = false; - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT start FROM subscriptions AS s" - + " JOIN visibilities AS v" - + " ON s.groupId = v.groupId" - + " WHERE s.groupId = ? AND contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); - ps.setInt(2, c.getInt()); - rs = ps.executeQuery(); - if(rs.next()) { - long start = rs.getLong(1); - if(start <= time) found = true; - if(rs.next()) throw new DbStateException(); - } - rs.close(); - ps.close(); - return found; - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public TransportConfig getConfig(Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; @@ -1100,38 +1091,6 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Collection<Transport> getLocalTransports(Connection txn) - throws DbException { - PreparedStatement ps = null; - ResultSet rs = null; - try { - String sql = "SELECT transportId, key, value" - + " FROM transportProperties" - + " ORDER BY transportId"; - ps = txn.prepareStatement(sql); - rs = ps.executeQuery(); - List<Transport> transports = new ArrayList<Transport>(); - TransportId lastId = null; - Transport t = null; - while(rs.next()) { - TransportId id = new TransportId(rs.getBytes(1)); - if(!id.equals(lastId)) { - t = new Transport(id); - transports.add(t); - } - t.getProperties().put(rs.getString(2), rs.getString(3)); - lastId = id; - } - rs.close(); - ps.close(); - return Collections.unmodifiableList(transports); - } catch(SQLException e) { - tryToClose(rs); - tryToClose(ps); - throw new DbException(e); - } - } - public byte[] getMessage(Connection txn, MessageId m) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -1246,19 +1205,18 @@ abstract class JdbcDatabase implements Database<Connection> { if(raw != null) return raw; // Do we have a sendable group message with the given ID? sql = "SELECT length, raw FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" + + " AND cg.contactId = s.contactId" + " WHERE m.messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()"; @@ -1353,18 +1311,17 @@ abstract class JdbcDatabase implements Database<Connection> { return Collections.unmodifiableList(ids); // Do we have any sendable group messages? sql = "SELECT m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1401,7 +1358,7 @@ abstract class JdbcDatabase implements Database<Connection> { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - sql = "SELECT COUNT(messageId) FROM messages" + sql = "SELECT COUNT (messageId) FROM messages" + " WHERE parentId = ? AND groupId = ?" + " AND sendability > ZERO()"; ps = txn.prepareStatement(sql); @@ -1509,11 +1466,12 @@ abstract class JdbcDatabase implements Database<Connection> { TransportProperties p = null; while(rs.next()) { ContactId id = new ContactId(rs.getInt(1)); + String key = rs.getString(2), value = rs.getString(3); if(!id.equals(lastId)) { p = new TransportProperties(); properties.put(id, p); } - p.put(rs.getString(2), rs.getString(3)); + p.put(key, value); } rs.close(); ps.close(); @@ -1614,18 +1572,17 @@ abstract class JdbcDatabase implements Database<Connection> { if(total == maxLength) return Collections.unmodifiableList(ids); // Do we have any sendable group messages? sql = "SELECT length, m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1676,8 +1633,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, groupName, groupKey" - + " FROM subscriptions"; + String sql = "SELECT groupId, name, key FROM groups"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<Group> subs = new ArrayList<Group>(); @@ -1702,8 +1658,7 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, groupName, groupKey" - + " FROM contactSubscriptions" + String sql = "SELECT groupId, name, key FROM contactGroups" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); @@ -1725,125 +1680,178 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public long getTransportsModified(Connection txn) throws DbException { + public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT DISTINCT modified FROM transportTimestamps"; + String sql = "SELECT remoteVersion FROM groupVersions" + + " WHERE contactId = ? AND remoteAcked = FALSE"; ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - if(!rs.next()) throw new DbException(); - long modified = rs.getLong(1); - if(rs.next()) throw new DbException(); + if(!rs.next()) { + rs.close(); + ps.close(); + return null; + } + long version = rs.getLong(1); + if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - return modified; + sql = "UPDATE groupVersions SET remoteAcked = TRUE" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + return new SubscriptionAck(version); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } - public long getTransportsSent(Connection txn, ContactId c) + public SubscriptionUpdate getSubscriptionUpdate(Connection txn, ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT sent FROM transportTimestamps" - + " WHERE contactId = ?"; + String sql = "SELECT g.groupId, name, key, localVersion" + + " FROM groups AS g" + + " JOIN groupVisibilities as gv" + + " ON g.groupId = gv.groupId" + + " JOIN groupVersions AS v" + + " ON gv.contactId = v.contactId" + + " WHERE gv.contactId = ?" + + " AND localVersion > localAcked"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - if(!rs.next()) throw new DbException(); - long sent = rs.getLong(1); - if(rs.next()) throw new DbException(); + List<Group> subs = new ArrayList<Group>(); + long version = 0L; + while(rs.next()) { + byte[] id = rs.getBytes(1); + String name = rs.getString(2); + byte[] key = rs.getBytes(3); + version = rs.getLong(4); + subs.add(new Group(new GroupId(id), name, key)); + } rs.close(); ps.close(); - return sent; + if(subs.isEmpty()) return null; + subs = Collections.unmodifiableList(subs); + return new SubscriptionUpdate(subs, version); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } - public Map<GroupId, Integer> getUnreadMessageCounts(Connection txn) - throws DbException { + public Collection<TransportAck> getTransportAcks(Connection txn, + ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, COUNT(*)" - + " FROM messages AS m" - + " LEFT OUTER JOIN flags AS f" - + " ON m.messageId = f.messageId" - + " WHERE (NOT read) OR (read IS NULL)" - + " GROUP BY groupId"; + String sql = "SELECT transportId, remoteVersion" + + " FROM contactTransportVersions" + + " WHERE contactId = ? AND remoteAcked = FALSE"; ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - Map<GroupId, Integer> counts = new HashMap<GroupId, Integer>(); + List<TransportAck> acks = new ArrayList<TransportAck>(); while(rs.next()) { - GroupId g = new GroupId(rs.getBytes(1)); - counts.put(g, rs.getInt(2)); + TransportId id = new TransportId(rs.getBytes(1)); + acks.add(new TransportAck(id, rs.getLong(2))); } rs.close(); ps.close(); - return Collections.unmodifiableMap(counts); + if(acks.isEmpty()) return null; + sql = "UPDATE contactTransportVersions SET remoteAcked = TRUE" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + for(TransportAck a : acks) { + ps.setBytes(2, a.getId().getBytes()); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != acks.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] < 1) throw new DbStateException(); + } + ps.close(); + return Collections.unmodifiableList(acks); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } - public Collection<ContactId> getVisibility(Connection txn, GroupId g) - throws DbException { + public Collection<TransportUpdate> getTransportUpdates(Connection txn, + ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactId FROM visibilities WHERE groupId = ?"; + String sql = "SELECT transportId, key, value, localVersion" + + " FROM transportProperties AS tp" + + " JOIN transportVersions as tv" + + " ON tp.transportId = tv.transportId" + + " WHERE tv.contactId = ?" + + " AND localVersion > localAcked"; ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); + ps.setInt(1, c.getInt()); rs = ps.executeQuery(); - List<ContactId> visible = new ArrayList<ContactId>(); - while(rs.next()) visible.add(new ContactId(rs.getInt(1))); + List<TransportUpdate> updates = new ArrayList<TransportUpdate>(); + TransportId lastId = null; + TransportProperties p = null; + while(rs.next()) { + TransportId id = new TransportId(rs.getBytes(1)); + String key = rs.getString(2), value = rs.getString(3); + long version = rs.getLong(4); + if(!id.equals(lastId)) { + p = new TransportProperties(); + updates.add(new TransportUpdate(id, p, version)); + } + p.put(key, value); + } rs.close(); ps.close(); - return Collections.unmodifiableList(visible); + if(updates.isEmpty()) return null; + return Collections.unmodifiableList(updates); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } - public Map<GroupId, GroupId> getVisibleHoles(Connection txn, ContactId c, - long timestamp) throws DbException { + public Map<GroupId, Integer> getUnreadMessageCounts(Connection txn) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, nextId FROM visibilities AS v" - + " JOIN subscriptionTimes AS st" - + " ON v.contactId = st.contactId" - + " WHERE v.contactId = ?" - + " AND deleted > acked AND deleted < ?"; + String sql = "SELECT groupId, COUNT(*)" + + " FROM messages AS m" + + " LEFT OUTER JOIN flags AS f" + + " ON m.messageId = f.messageId" + + " WHERE (NOT read) OR (read IS NULL)" + + " GROUP BY groupId"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setLong(2, timestamp); rs = ps.executeQuery(); - Map<GroupId, GroupId> holes = null; + Map<GroupId, Integer> counts = new HashMap<GroupId, Integer>(); while(rs.next()) { - byte[] b = rs.getBytes(1); - GroupId groupId = b == null ? null : new GroupId(b); - b = rs.getBytes(2); - GroupId nextId = b == null ? null : new GroupId(b); - if(holes == null) holes = new HashMap<GroupId, GroupId>(); - holes.put(groupId, nextId); + GroupId g = new GroupId(rs.getBytes(1)); + counts.put(g, rs.getInt(2)); } rs.close(); ps.close(); - if(holes == null) return Collections.emptyMap(); - return Collections.unmodifiableMap(holes); + return Collections.unmodifiableMap(counts); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1851,36 +1859,21 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Map<Group, Long> getVisibleSubscriptions(Connection txn, ContactId c, - long timestamp) throws DbException { + public Collection<ContactId> getVisibility(Connection txn, GroupId g) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT s.groupId, groupName, groupKey, start" - + " FROM subscriptions AS s" - + " JOIN visibilities AS v" - + " ON s.groupId = v.groupId" - + " JOIN subscriptionTimes AS st" - + " ON v.contactId = st.contactId" - + " WHERE v.contactId = ?" - + " AND start > acked AND start < ?"; + String sql = "SELECT contactId FROM groupVisibilities" + + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setLong(2, timestamp); + ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); - Map<Group, Long> subs = null; - while(rs.next()) { - GroupId id = new GroupId(rs.getBytes(1)); - String name = rs.getString(2); - byte[] publicKey = rs.getBytes(3); - long start = rs.getLong(4); - if(subs == null) subs = new HashMap<Group, Long>(); - subs.put(new Group(id, name, publicKey), start); - } + List<ContactId> visible = new ArrayList<ContactId>(); + while(rs.next()) visible.add(new ContactId(rs.getInt(1))); rs.close(); ps.close(); - if(subs == null) return Collections.emptyMap(); - return Collections.unmodifiableMap(subs); + return Collections.unmodifiableList(visible); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1911,18 +1904,17 @@ abstract class JdbcDatabase implements Database<Connection> { if(found) return true; // Do we have any sendable group messages? sql = "SELECT m.messageId FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " JOIN statuses AS s" + " ON m.messageId = s.messageId" - + " AND cs.contactId = s.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" - + " WHERE cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = s.contactId" + + " WHERE cg.contactId = ?" + " AND timestamp >= expiry" + " AND status = ?" + " AND sendability > ZERO()" @@ -1967,12 +1959,13 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); // Increment the connection counter - sql = "UPDATE secrets SET outgoing = outgoing + 1" + sql = "UPDATE secrets SET outgoing = outgoing + ?" + " WHERE contactId = ? AND transportId = ? AND period = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, t.getBytes()); - ps.setLong(3, period); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); + ps.setBytes(3, t.getBytes()); + ps.setLong(4, period); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -2070,83 +2063,59 @@ abstract class JdbcDatabase implements Database<Connection> { public void removeSubscription(Connection txn, GroupId g) throws DbException { - PreparedStatement ps = null, ps1 = null; + PreparedStatement ps = null; ResultSet rs = null; try { - // Remove the group ID from the visibility lists - long now = clock.currentTimeMillis(); - String sql = "SELECT contactId, nextId FROM visibilities" + // Find out which contacts are affected + String sql = "SELECT contactId FROM groupVisibilities" + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); - while(rs.next()) { - int contactId = rs.getInt(1); - byte[] nextId = rs.getBytes(2); - sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; - ps1 = txn.prepareStatement(sql); - if(nextId == null) ps1.setNull(1, BINARY); // At the tail - else ps1.setBytes(1, nextId); // At the head or in the middle - ps1.setLong(2, now); - ps1.setInt(3, contactId); - ps1.setBytes(4, g.getBytes()); - int affected = ps1.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps1.close(); - } + Collection<Integer> visible = new ArrayList<Integer>(); + while(rs.next()) visible.add(rs.getInt(1)); rs.close(); ps.close(); - // Remove the group from the subscriptions table - sql = "DELETE FROM subscriptions WHERE groupId = ?"; + // Delete the group + sql = "DELETE FROM groups WHERE groupId = ?"; ps = txn.prepareStatement(sql); ps.setBytes(1, g.getBytes()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); + if(visible.isEmpty()) return; + // Bump the subscription version for the affected contacts + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, 1); + for(Integer c : visible) { + ps.setInt(2, c); + ps.addBatch(); + } + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != visible.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); + } + ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); - tryToClose(ps1); + tryToClose(rs); throw new DbException(e); } } - public void removeSubscriptions(Connection txn, ContactId c, GroupId start, - GroupId end) throws DbException { + public void removeTransport(Connection txn, TransportId t) + throws DbException { PreparedStatement ps = null; try { - if(start == null && end == null) { - // Delete everything - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - } else if(start == null) { - // Delete everything before end - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, end.getBytes()); - } else if(end == null) { - // Delete everything after start - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ? AND groupId > ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, start.getBytes()); - } else { - // Delete everything between start and end - String sql = "DELETE FROM contactSubscriptions" - + " WHERE contactId = ?" - + " AND groupId > ? AND groupId < ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, start.getBytes()); - ps.setBytes(3, end.getBytes()); - } - ps.executeUpdate(); + String sql = "DELETE FROM transports WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { tryToClose(ps); @@ -2157,21 +2126,8 @@ abstract class JdbcDatabase implements Database<Connection> { public void removeVisibility(Connection txn, ContactId c, GroupId g) throws DbException { PreparedStatement ps = null; - ResultSet rs = null; try { - // Remove the group ID from the linked list - String sql = "SELECT nextId FROM visibilities" - + " WHERE contactId = ? AND groupId = ?"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - ps.setBytes(2, g.getBytes()); - rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - byte[] nextId = rs.getBytes(1); - if(rs.next()) throw new DbStateException(); - rs.close(); - ps.close(); - sql = "DELETE FROM visibilities" + String sql = "DELETE FROM groupVisibilities" + " WHERE contactId = ? AND groupId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); @@ -2179,19 +2135,16 @@ abstract class JdbcDatabase implements Database<Connection> { int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); - sql = "UPDATE visibilities SET nextId = ?, deleted = ?" - + " WHERE contactId = ? AND nextId = ?"; + // Bump the subscription version + sql = "UPDATE groupVersions SET localVersion = localVersion + ?" + + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); - if(nextId == null) ps.setNull(1, BINARY); // At the tail - else ps.setBytes(1, nextId); // At the head or in the middle - ps.setLong(2, clock.currentTimeMillis()); - ps.setInt(3, c.getInt()); - ps.setBytes(4, g.getBytes()); + ps.setInt(1, 1); + ps.setInt(2, c.getInt()); affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); throw new DbException(e); } @@ -2199,12 +2152,29 @@ abstract class JdbcDatabase implements Database<Connection> { public void mergeConfig(Connection txn, TransportId t, TransportConfig c) throws DbException { + // Merge the new configuration with the existing one mergeStringMap(txn, t, c, "transportConfigs"); } public void mergeLocalProperties(Connection txn, TransportId t, TransportProperties p) throws DbException { + // Merge the new properties with the existing ones mergeStringMap(txn, t, p, "transportProperties"); + // Bump the transport version + PreparedStatement ps = null; + try { + String sql = "UPDATE transportVersions" + + " SET localVersion = localVersion + ?" + + " WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, 1); + ps.setBytes(2, t.getBytes()); + ps.executeUpdate(); + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + throw new DbException(e); + } } private void mergeStringMap(Connection txn, TransportId t, @@ -2278,7 +2248,7 @@ abstract class JdbcDatabase implements Database<Connection> { throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE subscriptionTimes SET expiry = ?" + String sql = "UPDATE contacts SET expiry = ?" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, expiry); @@ -2391,6 +2361,85 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public void setRemoteProperties(Connection txn, ContactId c, + TransportUpdate t) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + // Find the existing version, if any + String sql = "SELECT remoteVersion FROM contactTransportVersions" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + rs = ps.executeQuery(); + long version = rs.next() ? rs.getLong(1) : -1L; + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + // Mark the update as needing to be acked + if(version == -1L) { + // The row doesn't exist - create it + sql = "INSERT INTO contactTransportVersions (contactId," + + " transportId, remoteVersion, remoteAcked)" + + " VALUES (?, ?, ?, FALSE)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + ps.setLong(3, t.getVersionNumber()); + int affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + ps.close(); + } else { + // The row exists - update it + sql = "UPDATE contactTransportVersions" + + " SET remoteVersion = ?, remoteAcked = FALSE" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setLong(1, Math.max(version, t.getVersionNumber())); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); + ps.close(); + // Return if the update is obsolete + if(t.getVersionNumber() <= version) return; + } + // Delete the existing properties, if any + sql = "DELETE FROM contactTransportProperties" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + ps.executeUpdate(); + ps.close(); + // Store the new properties, if any + TransportProperties p = t.getProperties(); + if(p.isEmpty()) return; + sql = "INSERT INTO contactTransportProperties" + + " (contactId, transportId, key, value)" + + " VALUES (?, ?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getId().getBytes()); + for(Entry<String, String> e : p.entrySet()) { + ps.setString(1, e.getKey()); + ps.setString(2, e.getValue()); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != p.size()) throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } + ps.close(); + } catch(SQLException e) { + tryToClose(ps); + tryToClose(rs); + throw new DbException(e); + } + } + public void setSendability(Connection txn, MessageId m, int sendability) throws DbException { PreparedStatement ps = null; @@ -2514,16 +2563,15 @@ abstract class JdbcDatabase implements Database<Connection> { ResultSet rs = null; try { String sql = "SELECT NULL FROM messages AS m" - + " JOIN contactSubscriptions AS cs" - + " ON m.groupId = cs.groupId" - + " JOIN visibilities AS v" - + " ON m.groupId = v.groupId" - + " AND cs.contactId = v.contactId" - + " JOIN subscriptionTimes AS st" - + " ON cs.contactId = st.contactId" + + " JOIN contactGroups AS cg" + + " ON m.groupId = cg.groupId" + + " JOIN groupVisibilities AS gv" + + " ON m.groupId = gv.groupId" + + " AND cg.contactId = gv.contactId" + + " JOIN contacts AS c" + + " ON cg.contactId = c.contactId" + " WHERE messageId = ?" - + " AND cs.contactId = ?" - + " AND timestamp >= start" + + " AND cg.contactId = ?" + " AND timestamp >= expiry"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getBytes()); @@ -2551,112 +2599,78 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setSubscriptionsAcked(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE subscriptionTimes SET acked = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected > 1) throw new DbStateException(); - ps.close(); - } catch(SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - - public void setSubscriptionsReceived(Connection txn, ContactId c, - long timestamp) throws DbException { - PreparedStatement ps = null; - try { - String sql = "UPDATE subscriptionTimes SET received = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected > 1) throw new DbStateException(); - ps.close(); - } catch(SQLException e) { - tryToClose(ps); - throw new DbException(e); - } - } - - public void setTransports(Connection txn, ContactId c, - Collection<Transport> transports, long timestamp) - throws DbException { + public void setSubscriptions(Connection txn, ContactId c, + SubscriptionUpdate s) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - // Return if the timestamp isn't fresh - String sql = "SELECT received FROM transportTimestamps" + // Find the existing version + String sql = "SELECT remoteVersion FROM groupVersions" + " WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); rs = ps.executeQuery(); if(!rs.next()) throw new DbStateException(); - long lastTimestamp = rs.getLong(1); + long version = rs.getLong(1); if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - if(lastTimestamp >= timestamp) return; - // Delete any existing transport properties - sql = "DELETE FROM contactTransportProperties WHERE contactId = ?"; + // Mark the update as needing to be acked + sql = "UPDATE groupVersions" + + " SET remoteVersion = ?, remoteAcked = FALSE" + + " WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setLong(1, Math.max(version, s.getVersionNumber())); + ps.setInt(2, c.getInt()); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); + ps.close(); + // Delete the existing subscriptions, if any + sql = "DELETE FROM contactGroups WHERE contactId = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); ps.executeUpdate(); - ps.close(); - // Store the new transport properties - sql = "INSERT INTO contactTransportProperties" - + " (contactId, transportId, key, value)" + // Store the new subscriptions, if any + Collection<Group> subs = s.getGroups(); + if(subs.isEmpty()) return; + sql = "INSERT INTO contactGroups (contactId, groupId, name, key)" + " VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - int batchSize = 0; - for(Transport t : transports) { - ps.setBytes(2, t.getId().getBytes()); - for(Entry<String, String> e1 : t.getProperties().entrySet()) { - ps.setString(3, e1.getKey()); - ps.setString(4, e1.getValue()); - ps.addBatch(); - batchSize++; - } + for(Group g : subs) { + ps.setBytes(2, g.getId().getBytes()); + ps.setString(3, g.getName()); + byte[] key = g.getPublicKey(); + if(key == null) ps.setNull(4, BINARY); + else ps.setBytes(4, key); + ps.addBatch(); } - int[] batchAffected = ps.executeBatch(); - if(batchAffected.length != batchSize) throw new DbStateException(); - for(int i = 0; i < batchAffected.length; i++) { - if(batchAffected[i] != 1) throw new DbStateException(); + int[] affectedBatch = ps.executeBatch(); + if(affectedBatch.length != subs.size()) + throw new DbStateException(); + for(int i = 0; i < affectedBatch.length; i++) { + if(affectedBatch[i] != 1) throw new DbStateException(); } ps.close(); - // Update the timestamp - sql = "UPDATE transportTimestamps SET received = ?" - + " WHERE contactId = ?"; - ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.setInt(2, c.getInt()); - int affected = ps.executeUpdate(); - if(affected != 1) throw new DbStateException(); - ps.close(); } catch(SQLException e) { - tryToClose(rs); tryToClose(ps); + tryToClose(rs); throw new DbException(e); } } - public void setTransportsModified(Connection txn, long timestamp) - throws DbException { + public void setSubscriptionUpdateAcked(Connection txn, ContactId c, + long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE transportTimestamps set modified = ?"; + String sql = "UPDATE groupVersions SET localAcked = ?" + + " WHERE contactId = ? AND localAcked < ?"; ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); - ps.executeUpdate(); + ps.setLong(1, version); + ps.setInt(2, c.getInt()); + ps.setLong(3, version); + int affected = ps.executeUpdate(); + if(affected > 1) throw new DbStateException(); ps.close(); } catch(SQLException e) { tryToClose(ps); @@ -2664,16 +2678,18 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public void setTransportsSent(Connection txn, ContactId c, long timestamp) - throws DbException { + public void setTransportUpdateAcked(Connection txn, ContactId c, + TransportId t, long version) throws DbException { PreparedStatement ps = null; try { - String sql = "UPDATE transportTimestamps SET sent = ?" - + " WHERE contactId = ? AND sent < ?"; + String sql = "UPDATE transportVersions SET localAcked = ?" + + " WHERE contactId = ? AND transportId = ?" + + " AND localAcked < ?"; ps = txn.prepareStatement(sql); - ps.setLong(1, timestamp); + ps.setLong(1, version); ps.setInt(2, c.getInt()); - ps.setLong(3, timestamp); + ps.setBytes(3, t.getBytes()); + ps.setLong(4, version); int affected = ps.executeUpdate(); if(affected > 1) throw new DbStateException(); ps.close(); diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java index 658b3cd401ccc92b65d173071b9aa6a3372614ec..5ec5f0520ce2830c738c1f73e9a1d4c55c93c398 100644 --- a/briar-core/src/net/sf/briar/invitation/Connector.java +++ b/briar-core/src/net/sf/briar/invitation/Connector.java @@ -104,7 +104,7 @@ abstract class Connector extends Thread { protected byte[] receivePublicKeyHash(Reader r) throws IOException { byte[] b = r.readBytes(HASH_LENGTH); - if(b.length != HASH_LENGTH) throw new FormatException(); + if(b.length < HASH_LENGTH) throw new FormatException(); if(LOG.isLoggable(INFO)) LOG.info(pluginName + " received hash"); return b; } diff --git a/briar-core/src/net/sf/briar/protocol/AckImpl.java b/briar-core/src/net/sf/briar/protocol/AckImpl.java deleted file mode 100644 index ea8e92428497aaee77a628a3c3057c31643ee3b6..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/AckImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.MessageId; - -class AckImpl implements Ack { - - private final Collection<MessageId> acked; - - AckImpl(Collection<MessageId> acked) { - this.acked = acked; - } - - public Collection<MessageId> getMessageIds() { - return acked; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/AckReader.java b/briar-core/src/net/sf/briar/protocol/AckReader.java index b9e55fc5e8a06d58b310448523998762ecb4e023..beb073633d0a81fa2bb650d8be51ab0896204b0c 100644 --- a/briar-core/src/net/sf/briar/protocol/AckReader.java +++ b/briar-core/src/net/sf/briar/protocol/AckReader.java @@ -12,7 +12,6 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -21,18 +20,11 @@ import net.sf.briar.api.serial.StructReader; class AckReader implements StructReader<Ack> { - private final PacketFactory packetFactory; - - AckReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Ack readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(ACK); + // Read the message IDs as byte arrays r.setMaxBytesLength(UniqueId.LENGTH); List<Bytes> raw = r.readList(Bytes.class); r.resetMaxBytesLength(); @@ -46,6 +38,6 @@ class AckReader implements StructReader<Ack> { acked.add(new MessageId(b.getBytes())); } // Build and return the ack - return packetFactory.createAck(Collections.unmodifiableList(acked)); + return new Ack(Collections.unmodifiableList(acked)); } } diff --git a/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java index bced33cb328b56422b14bc35ba33e957b31ee862..41fa3d7826ca4bc21db8405ec70bf55ccdcf4ede 100644 --- a/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java +++ b/briar-core/src/net/sf/briar/protocol/AuthorFactoryImpl.java @@ -27,7 +27,7 @@ class AuthorFactoryImpl implements AuthorFactory { } public Author createAuthor(String name, byte[] publicKey) - throws IOException { + throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Writer w = writerFactory.createWriter(out); w.writeStructId(AUTHOR); @@ -36,10 +36,6 @@ class AuthorFactoryImpl implements AuthorFactory { MessageDigest messageDigest = crypto.getMessageDigest(); messageDigest.update(out.toByteArray()); AuthorId id = new AuthorId(messageDigest.digest()); - return new AuthorImpl(id, name, publicKey); - } - - public Author createAuthor(AuthorId id, String name, byte[] publicKey) { - return new AuthorImpl(id, name, publicKey); + return new Author(id, name, publicKey); } } diff --git a/briar-core/src/net/sf/briar/protocol/AuthorImpl.java b/briar-core/src/net/sf/briar/protocol/AuthorImpl.java deleted file mode 100644 index dfaf022c84678e796aa924b60b363ccfe86aca69..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/AuthorImpl.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.sf.briar.protocol; - -import net.sf.briar.api.protocol.Author; -import net.sf.briar.api.protocol.AuthorId; - -class AuthorImpl implements Author { - - private final AuthorId id; - private final String name; - private final byte[] publicKey; - - AuthorImpl(AuthorId id, String name, byte[] publicKey) { - this.id = id; - this.name = name; - this.publicKey = publicKey; - } - - public AuthorId getId() { - return id; - } - - public String getName() { - return name; - } - - public byte[] getPublicKey() { - return publicKey; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/AuthorReader.java b/briar-core/src/net/sf/briar/protocol/AuthorReader.java index 9bc177f7287544cf0ca3a058b829948697ad867d..d8d392503d43b2faf4daa5cd537c9e1f4cb07fac 100644 --- a/briar-core/src/net/sf/briar/protocol/AuthorReader.java +++ b/briar-core/src/net/sf/briar/protocol/AuthorReader.java @@ -9,7 +9,6 @@ import java.io.IOException; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.protocol.Author; -import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.serial.DigestingConsumer; import net.sf.briar.api.serial.Reader; @@ -18,15 +17,12 @@ import net.sf.briar.api.serial.StructReader; class AuthorReader implements StructReader<Author> { private final MessageDigest messageDigest; - private final AuthorFactory authorFactory; - AuthorReader(CryptoComponent crypto, AuthorFactory authorFactory) { + AuthorReader(CryptoComponent crypto) { messageDigest = crypto.getMessageDigest(); - this.authorFactory = authorFactory; } public Author readStruct(Reader r) throws IOException { - // Initialise the consumer DigestingConsumer digesting = new DigestingConsumer(messageDigest); // Read and digest the data r.addConsumer(digesting); @@ -36,6 +32,6 @@ class AuthorReader implements StructReader<Author> { r.removeConsumer(digesting); // Build and return the author AuthorId id = new AuthorId(messageDigest.digest()); - return authorFactory.createAuthor(id, name, publicKey); + return new Author(id, name, publicKey); } } diff --git a/briar-core/src/net/sf/briar/protocol/GroupReader.java b/briar-core/src/net/sf/briar/protocol/GroupReader.java index af1fcfc3c56ea00ce4e5127c71f5161c62934ae4..3cfc9d908c6398649788349b7b850c2befa85fd7 100644 --- a/briar-core/src/net/sf/briar/protocol/GroupReader.java +++ b/briar-core/src/net/sf/briar/protocol/GroupReader.java @@ -23,7 +23,6 @@ class GroupReader implements StructReader<Group> { } public Group readStruct(Reader r) throws IOException { - // Initialise the consumer DigestingConsumer digesting = new DigestingConsumer(messageDigest); // Read and digest the data r.addConsumer(digesting); diff --git a/briar-core/src/net/sf/briar/protocol/MessageReader.java b/briar-core/src/net/sf/briar/protocol/MessageReader.java index 9b2f801a1fb1cff03765412efd1873fd2a7179a9..f86ae4dfa9bea044b957bf1ef0310f3335d91611 100644 --- a/briar-core/src/net/sf/briar/protocol/MessageReader.java +++ b/briar-core/src/net/sf/briar/protocol/MessageReader.java @@ -46,7 +46,7 @@ class MessageReader implements StructReader<UnverifiedMessage> { r.readNull(); } else { byte[] b = r.readBytes(UniqueId.LENGTH); - if(b.length != UniqueId.LENGTH) throw new FormatException(); + if(b.length < UniqueId.LENGTH) throw new FormatException(); parent = new MessageId(b); } // Read the group, if there is one @@ -74,7 +74,7 @@ class MessageReader implements StructReader<UnverifiedMessage> { if(timestamp < 0L) throw new FormatException(); // Read the salt byte[] salt = r.readBytes(SALT_LENGTH); - if(salt.length != SALT_LENGTH) throw new FormatException(); + if(salt.length < SALT_LENGTH) throw new FormatException(); // Read the message body byte[] body = r.readBytes(MAX_BODY_LENGTH); // Record the offset of the body within the message diff --git a/briar-core/src/net/sf/briar/protocol/OfferImpl.java b/briar-core/src/net/sf/briar/protocol/OfferImpl.java deleted file mode 100644 index de892202e64c2fc1fe435767993734920aeda5cf..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/OfferImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Offer; - -class OfferImpl implements Offer { - - private final Collection<MessageId> offered; - - OfferImpl(Collection<MessageId> offered) { - this.offered = offered; - } - - public Collection<MessageId> getMessageIds() { - return offered; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/OfferReader.java b/briar-core/src/net/sf/briar/protocol/OfferReader.java index 32c0bc5bf8d9e11489405f10ccdb51564d033165..62a8d665a83310cfaee15d5f17f0c649f5155073 100644 --- a/briar-core/src/net/sf/briar/protocol/OfferReader.java +++ b/briar-core/src/net/sf/briar/protocol/OfferReader.java @@ -12,7 +12,6 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -21,18 +20,11 @@ import net.sf.briar.api.serial.StructReader; class OfferReader implements StructReader<Offer> { - private final PacketFactory packetFactory; - - OfferReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Offer readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(OFFER); + // Read the message IDs as byte arrays r.setMaxBytesLength(UniqueId.LENGTH); List<Bytes> raw = r.readList(Bytes.class); r.resetMaxBytesLength(); @@ -46,7 +38,6 @@ class OfferReader implements StructReader<Offer> { messages.add(new MessageId(b.getBytes())); } // Build and return the offer - return packetFactory.createOffer(Collections.unmodifiableList( - messages)); + return new Offer(Collections.unmodifiableList(messages)); } } diff --git a/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java deleted file mode 100644 index a8e9e32c204b6535370a3a1f5fc788e3659f099a..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/PacketFactoryImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.BitSet; -import java.util.Collection; -import java.util.Map; - -import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; -import net.sf.briar.api.protocol.Request; -import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportUpdate; - -class PacketFactoryImpl implements PacketFactory { - - public Ack createAck(Collection<MessageId> acked) { - return new AckImpl(acked); - } - - public Offer createOffer(Collection<MessageId> offered) { - return new OfferImpl(offered); - } - - public Request createRequest(BitSet requested, int length) { - return new RequestImpl(requested, length); - } - - public SubscriptionUpdate createSubscriptionUpdate( - Map<GroupId, GroupId> holes, Map<Group, Long> subs, long expiry, - long timestamp) { - return new SubscriptionUpdateImpl(holes, subs, expiry, timestamp); - } - - public TransportUpdate createTransportUpdate( - Collection<Transport> transports, long timestamp) { - return new TransportUpdateImpl(transports, timestamp); - } -} diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolModule.java b/briar-core/src/net/sf/briar/protocol/ProtocolModule.java index 075f4158b9a03116f201f1e29e7b951fc77e8d2d..dfce9b987c6dd777eccb001204b7730877d9adf4 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolModule.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolModule.java @@ -11,11 +11,12 @@ import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageVerifier; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.protocol.VerificationExecutor; @@ -48,7 +49,6 @@ public class ProtocolModule extends AbstractModule { bind(GroupFactory.class).to(GroupFactoryImpl.class); bind(MessageFactory.class).to(MessageFactoryImpl.class); bind(MessageVerifier.class).to(MessageVerifierImpl.class); - bind(PacketFactory.class).to(PacketFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class); // The executor is bounded, so tasks must be independent and short-lived @@ -59,14 +59,13 @@ public class ProtocolModule extends AbstractModule { } @Provides - StructReader<Ack> getAckReader(PacketFactory ackFactory) { - return new AckReader(ackFactory); + StructReader<Ack> getAckReader() { + return new AckReader(); } @Provides - StructReader<Author> getAuthorReader(CryptoComponent crypto, - AuthorFactory authorFactory) { - return new AuthorReader(crypto, authorFactory); + StructReader<Author> getAuthorReader(CryptoComponent crypto) { + return new AuthorReader(crypto); } @Provides @@ -82,24 +81,33 @@ public class ProtocolModule extends AbstractModule { } @Provides - StructReader<Offer> getOfferReader(PacketFactory packetFactory) { - return new OfferReader(packetFactory); + StructReader<Offer> getOfferReader() { + return new OfferReader(); } @Provides - StructReader<Request> getRequestReader(PacketFactory packetFactory) { - return new RequestReader(packetFactory); + StructReader<Request> getRequestReader() { + return new RequestReader(); } @Provides - StructReader<SubscriptionUpdate> getSubscriptionReader( - StructReader<Group> groupReader, PacketFactory packetFactory) { - return new SubscriptionUpdateReader(groupReader, packetFactory); + StructReader<SubscriptionAck> getSubscriptionAckReader() { + return new SubscriptionAckReader(); } @Provides - StructReader<TransportUpdate> getTransportReader( - PacketFactory packetFactory) { - return new TransportUpdateReader(packetFactory); + StructReader<SubscriptionUpdate> getSubscriptionUpdateReader( + StructReader<Group> groupReader) { + return new SubscriptionUpdateReader(groupReader); + } + + @Provides + StructReader<TransportAck> getTransportAckReader() { + return new TransportAckReader(); + } + + @Provides + StructReader<TransportUpdate> getTransportUpdateReader() { + return new TransportUpdateReader(); } } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java index 3ac7649d524bd9abed8dd8940f35b77dd42aae36..fc42c841da2e3cd51a24370c09ec7b317bf150dc 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolReaderFactoryImpl.java @@ -7,7 +7,9 @@ import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.serial.ReaderFactory; @@ -16,6 +18,7 @@ import net.sf.briar.api.serial.StructReader; import com.google.inject.Inject; import com.google.inject.Provider; +// FIXME: Refactor this package to reduce boilerplate class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { private final ReaderFactory readerFactory; @@ -23,8 +26,10 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { private final Provider<StructReader<UnverifiedMessage>> messageProvider; private final Provider<StructReader<Offer>> offerProvider; private final Provider<StructReader<Request>> requestProvider; - private final Provider<StructReader<SubscriptionUpdate>> subscriptionProvider; - private final Provider<StructReader<TransportUpdate>> transportProvider; + private final Provider<StructReader<SubscriptionAck>> subscriptionAckProvider; + private final Provider<StructReader<SubscriptionUpdate>> subscriptionUpdateProvider; + private final Provider<StructReader<TransportAck>> transportAckProvider; + private final Provider<StructReader<TransportUpdate>> transportUpdateProvider; @Inject ProtocolReaderFactoryImpl(ReaderFactory readerFactory, @@ -32,21 +37,26 @@ class ProtocolReaderFactoryImpl implements ProtocolReaderFactory { Provider<StructReader<UnverifiedMessage>> messageProvider, Provider<StructReader<Offer>> offerProvider, Provider<StructReader<Request>> requestProvider, - Provider<StructReader<SubscriptionUpdate>> subscriptionProvider, - Provider<StructReader<TransportUpdate>> transportProvider) { + Provider<StructReader<SubscriptionAck>> subscriptionAckProvider, + Provider<StructReader<SubscriptionUpdate>> subscriptionUpdateProvider, + Provider<StructReader<TransportAck>> transportAckProvider, + Provider<StructReader<TransportUpdate>> transportUpdateProvider) { this.readerFactory = readerFactory; this.ackProvider = ackProvider; this.messageProvider = messageProvider; this.offerProvider = offerProvider; this.requestProvider = requestProvider; - this.subscriptionProvider = subscriptionProvider; - this.transportProvider = transportProvider; + this.subscriptionAckProvider = subscriptionAckProvider; + this.subscriptionUpdateProvider = subscriptionUpdateProvider; + this.transportAckProvider = transportAckProvider; + this.transportUpdateProvider = transportUpdateProvider; } public ProtocolReader createProtocolReader(InputStream in) { return new ProtocolReaderImpl(in, readerFactory, ackProvider.get(), messageProvider.get(), offerProvider.get(), - requestProvider.get(), subscriptionProvider.get(), - transportProvider.get()); + requestProvider.get(), subscriptionAckProvider.get(), + subscriptionUpdateProvider.get(), transportAckProvider.get(), + transportUpdateProvider.get()); } } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java index 9d5850b159db15af6310ade2d2d2ac20123c8647..b13cfe8adffa94796eb6084cf4ee764c035b03cd 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolReaderImpl.java @@ -4,7 +4,9 @@ import static net.sf.briar.api.protocol.Types.ACK; import static net.sf.briar.api.protocol.Types.MESSAGE; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; @@ -14,7 +16,9 @@ import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UnverifiedMessage; import net.sf.briar.api.serial.Reader; @@ -30,15 +34,19 @@ class ProtocolReaderImpl implements ProtocolReader { StructReader<UnverifiedMessage> messageReader, StructReader<Offer> offerReader, StructReader<Request> requestReader, - StructReader<SubscriptionUpdate> subscriptionReader, - StructReader<TransportUpdate> transportReader) { + StructReader<SubscriptionAck> subscriptionAckReader, + StructReader<SubscriptionUpdate> subscriptionUpdateReader, + StructReader<TransportAck> transportAckReader, + StructReader<TransportUpdate> transportUpdateReader) { reader = readerFactory.createReader(in); reader.addStructReader(ACK, ackReader); reader.addStructReader(MESSAGE, messageReader); reader.addStructReader(OFFER, offerReader); reader.addStructReader(REQUEST, requestReader); - reader.addStructReader(SUBSCRIPTION_UPDATE, subscriptionReader); - reader.addStructReader(TRANSPORT_UPDATE, transportReader); + reader.addStructReader(SUBSCRIPTION_ACK, subscriptionAckReader); + reader.addStructReader(SUBSCRIPTION_UPDATE, subscriptionUpdateReader); + reader.addStructReader(TRANSPORT_ACK, transportAckReader); + reader.addStructReader(TRANSPORT_UPDATE, transportUpdateReader); } public boolean eof() throws IOException { @@ -77,6 +85,14 @@ class ProtocolReaderImpl implements ProtocolReader { return reader.readStruct(REQUEST, Request.class); } + public boolean hasSubscriptionAck() throws IOException { + return reader.hasStruct(SUBSCRIPTION_ACK); + } + + public SubscriptionAck readSubscriptionAck() throws IOException { + return reader.readStruct(SUBSCRIPTION_ACK, SubscriptionAck.class); + } + public boolean hasSubscriptionUpdate() throws IOException { return reader.hasStruct(SUBSCRIPTION_UPDATE); } @@ -86,6 +102,14 @@ class ProtocolReaderImpl implements ProtocolReader { SubscriptionUpdate.class); } + public boolean hasTransportAck() throws IOException { + return reader.hasStruct(TRANSPORT_ACK); + } + + public TransportAck readTransportAck() throws IOException { + return reader.readStruct(TRANSPORT_ACK, TransportAck.class); + } + public boolean hasTransportUpdate() throws IOException { return reader.hasStruct(TRANSPORT_UPDATE); } diff --git a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java index 87ddf36eb8b74df59648d7a9a3fb74f79737d17b..633ec17d8833bcea993a235827d14817d0717df9 100644 --- a/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java +++ b/briar-core/src/net/sf/briar/protocol/ProtocolWriterImpl.java @@ -5,24 +5,24 @@ import static net.sf.briar.api.protocol.Types.ACK; import static net.sf.briar.api.protocol.Types.GROUP; import static net.sf.briar.api.protocol.Types.OFFER; import static net.sf.briar.api.protocol.Types.REQUEST; +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; -import static net.sf.briar.api.protocol.Types.TRANSPORT; +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; import java.io.OutputStream; import java.util.BitSet; -import java.util.Map.Entry; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionAck; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportAck; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.serial.SerialComponent; import net.sf.briar.api.serial.Writer; @@ -103,48 +103,40 @@ class ProtocolWriterImpl implements ProtocolWriter { if(flush) out.flush(); } + public void writeSubscriptionAck(SubscriptionAck a) throws IOException { + w.writeStructId(SUBSCRIPTION_ACK); + w.writeInt64(a.getVersionNumber()); + if(flush) out.flush(); + } + public void writeSubscriptionUpdate(SubscriptionUpdate s) throws IOException { w.writeStructId(SUBSCRIPTION_UPDATE); - // Holes - w.writeMapStart(); - for(Entry<GroupId, GroupId> e : s.getHoles().entrySet()) { - w.writeBytes(e.getKey().getBytes()); - w.writeBytes(e.getValue().getBytes()); - } - w.writeMapEnd(); - // Subscriptions - w.writeMapStart(); - for(Entry<Group, Long> e : s.getSubscriptions().entrySet()) { - writeGroup(w, e.getKey()); - w.writeInt64(e.getValue()); + w.writeListStart(); + for(Group g : s.getGroups()) { + w.writeStructId(GROUP); + w.writeString(g.getName()); + byte[] publicKey = g.getPublicKey(); + if(publicKey == null) w.writeNull(); + else w.writeBytes(publicKey); } - w.writeMapEnd(); - // Expiry time - w.writeInt64(s.getExpiryTime()); - // Timestamp - w.writeInt64(s.getTimestamp()); + w.writeListEnd(); + w.writeInt64(s.getVersionNumber()); if(flush) out.flush(); } - private void writeGroup(Writer w, Group g) throws IOException { - w.writeStructId(GROUP); - w.writeString(g.getName()); - byte[] publicKey = g.getPublicKey(); - if(publicKey == null) w.writeNull(); - else w.writeBytes(publicKey); + public void writeTransportAck(TransportAck a) throws IOException { + w.writeStructId(TRANSPORT_ACK); + w.writeBytes(a.getId().getBytes()); + w.writeInt64(a.getVersionNumber()); + if(flush) out.flush(); } public void writeTransportUpdate(TransportUpdate t) throws IOException { w.writeStructId(TRANSPORT_UPDATE); - w.writeListStart(); - for(Transport p : t.getTransports()) { - w.writeStructId(TRANSPORT); - w.writeBytes(p.getId().getBytes()); - w.writeMap(p.getProperties()); - } - w.writeListEnd(); - w.writeInt64(t.getTimestamp()); + w.writeBytes(t.getId().getBytes()); + w.writeMap(t.getProperties()); + w.writeInt64(t.getVersionNumber()); if(flush) out.flush(); } diff --git a/briar-core/src/net/sf/briar/protocol/RequestImpl.java b/briar-core/src/net/sf/briar/protocol/RequestImpl.java deleted file mode 100644 index aeae2cd285571f30c456276b61f76832145479a8..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/RequestImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.BitSet; - -import net.sf.briar.api.protocol.Request; - -class RequestImpl implements Request { - - private final BitSet requested; - private final int length; - - RequestImpl(BitSet requested, int length) { - this.requested = requested; - this.length = length; - } - - public BitSet getBitmap() { - return requested; - } - - public int getLength() { - return length; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/RequestReader.java b/briar-core/src/net/sf/briar/protocol/RequestReader.java index 0514ddc48591fb8276e19beeaf3a5b6e027bc8bb..8bac06ec77fedef112eeff5da94b2ca1d64e0e3d 100644 --- a/briar-core/src/net/sf/briar/protocol/RequestReader.java +++ b/briar-core/src/net/sf/briar/protocol/RequestReader.java @@ -7,7 +7,6 @@ import java.io.IOException; import java.util.BitSet; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; @@ -16,20 +15,14 @@ import net.sf.briar.api.serial.StructReader; class RequestReader implements StructReader<Request> { - private final PacketFactory packetFactory; - - RequestReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - } - public Request readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(REQUEST); + // There may be up to 7 bits of padding at the end of the bitmap int padding = r.readUint7(); if(padding > 7) throw new FormatException(); + // Read the bitmap byte[] bitmap = r.readBytes(MAX_PACKET_LENGTH); r.removeConsumer(counting); // Convert the bitmap into a BitSet @@ -41,6 +34,6 @@ class RequestReader implements StructReader<Request> { if((bitmap[i] & bit) != 0) b.set(i * 8 + j); } } - return packetFactory.createRequest(b, length); + return new Request(b, length); } } diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java b/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java new file mode 100644 index 0000000000000000000000000000000000000000..648fbc7ec1d74ee42080604a85fe73bf1d058ef1 --- /dev/null +++ b/briar-core/src/net/sf/briar/protocol/SubscriptionAckReader.java @@ -0,0 +1,20 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_ACK; + +import java.io.IOException; + +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.SubscriptionAck; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.StructReader; + +class SubscriptionAckReader implements StructReader<SubscriptionAck> { + + public SubscriptionAck readStruct(Reader r) throws IOException { + r.readStructId(SUBSCRIPTION_ACK); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new SubscriptionAck(version); + } +} diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java deleted file mode 100644 index a98b7fee1f819cf25801483b19594f48bc584a54..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateImpl.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Map; - -import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.SubscriptionUpdate; - -class SubscriptionUpdateImpl implements SubscriptionUpdate { - - private final Map<GroupId, GroupId> holes; - private final Map<Group, Long> subs; - private final long expiry, timestamp; - - SubscriptionUpdateImpl(Map<GroupId, GroupId> holes, Map<Group, Long> subs, - long expiry, long timestamp) { - this.holes = holes; - this.subs = subs; - this.expiry = expiry; - this.timestamp = timestamp; - } - - public Map<GroupId, GroupId> getHoles() { - return holes; - } - - public Map<Group, Long> getSubscriptions() { - return subs; - } - - public long getExpiryTime() { - return expiry; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java index e87c20c7fe73d970d3de0b5148f9b13cfe222f3a..35d0e5f42e2c27c47c7962f2c16bb828e1d0ae1b 100644 --- a/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java +++ b/briar-core/src/net/sf/briar/protocol/SubscriptionUpdateReader.java @@ -5,15 +5,12 @@ import static net.sf.briar.api.protocol.Types.GROUP; import static net.sf.briar.api.protocol.Types.SUBSCRIPTION_UPDATE; import java.io.IOException; -import java.util.HashMap; -import java.util.Map; +import java.util.Collections; +import java.util.List; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Group; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.CountingConsumer; import net.sf.briar.api.serial.Reader; @@ -22,46 +19,25 @@ import net.sf.briar.api.serial.StructReader; class SubscriptionUpdateReader implements StructReader<SubscriptionUpdate> { private final StructReader<Group> groupReader; - private final PacketFactory packetFactory; - SubscriptionUpdateReader(StructReader<Group> groupReader, - PacketFactory packetFactory) { + SubscriptionUpdateReader(StructReader<Group> groupReader) { this.groupReader = groupReader; - this.packetFactory = packetFactory; } public SubscriptionUpdate readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(SUBSCRIPTION_UPDATE); - // Holes - Map<GroupId, GroupId> holes = new HashMap<GroupId, GroupId>(); - r.setMaxBytesLength(UniqueId.LENGTH); - r.readMapStart(); - while(!r.hasMapEnd()) { - byte[] start = r.readBytes(); - if(start.length != UniqueId.LENGTH) throw new FormatException(); - byte[] end = r.readBytes(); - if(end.length != UniqueId.LENGTH)throw new FormatException(); - holes.put(new GroupId(start), new GroupId(end)); - } - r.readMapEnd(); - r.resetMaxBytesLength(); - // Subscriptions + // Read the subscriptions r.addStructReader(GROUP, groupReader); - Map<Group, Long> subs = r.readMap(Group.class, Long.class); + List<Group> subs = r.readList(Group.class); r.removeStructReader(GROUP); - // Expiry time - long expiry = r.readInt64(); - if(expiry < 0L) throw new FormatException(); - // Timestamp - long timestamp = r.readInt64(); - if(timestamp < 0L) throw new FormatException(); + // Read the version number + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); r.removeConsumer(counting); // Build and return the subscription update - return packetFactory.createSubscriptionUpdate(holes, subs, expiry, - timestamp); + subs = Collections.unmodifiableList(subs); + return new SubscriptionUpdate(subs, version); } } diff --git a/briar-core/src/net/sf/briar/protocol/TransportAckReader.java b/briar-core/src/net/sf/briar/protocol/TransportAckReader.java new file mode 100644 index 0000000000000000000000000000000000000000..6a7c730d450c2b2610fd710086ad37467627a23a --- /dev/null +++ b/briar-core/src/net/sf/briar/protocol/TransportAckReader.java @@ -0,0 +1,24 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.Types.TRANSPORT_ACK; + +import java.io.IOException; + +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.TransportAck; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.StructReader; + +class TransportAckReader implements StructReader<TransportAck> { + + public TransportAck readStruct(Reader r) throws IOException { + r.readStructId(TRANSPORT_ACK); + byte[] b = r.readBytes(UniqueId.LENGTH); + if(b.length < UniqueId.LENGTH) throw new FormatException(); + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); + return new TransportAck(new TransportId(b), version); + } +} diff --git a/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java b/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java deleted file mode 100644 index b69a2bce777c3288e46bbaf67588d2fa48abd842..0000000000000000000000000000000000000000 --- a/briar-core/src/net/sf/briar/protocol/TransportUpdateImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Collection; - -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportUpdate; - -class TransportUpdateImpl implements TransportUpdate { - - private final Collection<Transport> transports; - private final long timestamp; - - TransportUpdateImpl(Collection<Transport> transports, - long timestamp) { - this.transports = transports; - this.timestamp = timestamp; - } - - public Collection<Transport> getTransports() { - return transports; - } - - public long getTimestamp() { - return timestamp; - } -} diff --git a/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java b/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java index d95c7d8c1c4262d1ef021a90c219d793abdd9139..ee7c4e5cc655bcd15fa45f43d61de00eab0becea 100644 --- a/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java +++ b/briar-core/src/net/sf/briar/protocol/TransportUpdateReader.java @@ -3,19 +3,13 @@ package net.sf.briar.protocol; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS; -import static net.sf.briar.api.protocol.Types.TRANSPORT; import static net.sf.briar.api.protocol.Types.TRANSPORT_UPDATE; import java.io.IOException; -import java.util.Collection; -import java.util.HashSet; import java.util.Map; -import java.util.Set; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; -import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.TransportProperties; import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UniqueId; @@ -26,50 +20,25 @@ import net.sf.briar.api.serial.StructReader; class TransportUpdateReader implements StructReader<TransportUpdate> { - private final PacketFactory packetFactory; - private final StructReader<Transport> transportReader; - - TransportUpdateReader(PacketFactory packetFactory) { - this.packetFactory = packetFactory; - transportReader = new TransportReader(); - } - public TransportUpdate readStruct(Reader r) throws IOException { - // Initialise the consumer Consumer counting = new CountingConsumer(MAX_PACKET_LENGTH); - // Read the data r.addConsumer(counting); r.readStructId(TRANSPORT_UPDATE); - r.addStructReader(TRANSPORT, transportReader); - Collection<Transport> transports = r.readList(Transport.class); - r.removeStructReader(TRANSPORT); - if(transports.size() > MAX_TRANSPORTS) throw new FormatException(); - long timestamp = r.readInt64(); + // Read the transport ID + byte[] b = r.readBytes(UniqueId.LENGTH); + if(b.length < UniqueId.LENGTH) throw new FormatException(); + TransportId id = new TransportId(b); + // Read the transport properties + r.setMaxStringLength(MAX_PROPERTY_LENGTH); + Map<String, String> m = r.readMap(String.class, String.class); + r.resetMaxStringLength(); + if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) + throw new FormatException(); + // Read the version number + long version = r.readInt64(); + if(version < 0L) throw new FormatException(); r.removeConsumer(counting); - // Check for duplicate IDs - Set<TransportId> ids = new HashSet<TransportId>(); - for(Transport t : transports) { - if(!ids.add(t.getId())) throw new FormatException(); - } // Build and return the transport update - return packetFactory.createTransportUpdate(transports, timestamp); - } - - private static class TransportReader implements StructReader<Transport> { - - public Transport readStruct(Reader r) throws IOException { - r.readStructId(TRANSPORT); - // Read the ID - byte[] b = r.readBytes(UniqueId.LENGTH); - if(b.length != UniqueId.LENGTH) throw new FormatException(); - TransportId id = new TransportId(b); - // Read the properties - r.setMaxStringLength(MAX_PROPERTY_LENGTH); - Map<String, String> m = r.readMap(String.class, String.class); - r.resetMaxStringLength(); - if(m.size() > MAX_PROPERTIES_PER_TRANSPORT) - throw new FormatException(); - return new Transport(id, m); - } + return new TransportUpdate(id, new TransportProperties(m), version); } } diff --git a/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java b/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java index 24d084bc85be643da7a029f37dcea331aaa187ee..221dd002759075080f23e29acd729a78c64624c4 100644 --- a/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/duplex/DuplexConnection.java @@ -27,10 +27,10 @@ import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; +import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Message; @@ -55,6 +55,7 @@ import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.util.ByteUtils; +// FIXME: Read and write subscription and transport acks abstract class DuplexConnection implements DatabaseListener { private static final Logger LOG = @@ -132,7 +133,7 @@ abstract class DuplexConnection implements DatabaseListener { if(affected.contains(contactId)) { dbExecutor.execute(new GenerateSubscriptionUpdate()); } - } else if(e instanceof LocalTransportsUpdatedEvent) { + } else if(e instanceof TransportsUpdatedEvent) { dbExecutor.execute(new GenerateTransportUpdate()); } } @@ -556,8 +557,9 @@ abstract class DuplexConnection implements DatabaseListener { public void run() { try { - TransportUpdate t = db.generateTransportUpdate(contactId); - if(t != null) writerTasks.add(new WriteTransportUpdate(t)); + Collection<TransportUpdate> t = + db.generateTransportUpdates(contactId); + if(t != null) writerTasks.add(new WriteTransportUpdates(t)); } catch(DbException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } @@ -565,18 +567,18 @@ abstract class DuplexConnection implements DatabaseListener { } // This task runs on the writer thread - private class WriteTransportUpdate implements Runnable { + private class WriteTransportUpdates implements Runnable { - private final TransportUpdate update; + private final Collection<TransportUpdate> updates; - private WriteTransportUpdate(TransportUpdate update) { - this.update = update; + private WriteTransportUpdates(Collection<TransportUpdate> updates) { + this.updates = updates; } public void run() { assert writer != null; try { - writer.writeTransportUpdate(update); + for(TransportUpdate t : updates) writer.writeTransportUpdate(t); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); dispose(true, true); diff --git a/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java b/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java index ffc6b8479d9448638dcbd5f4f80d668e7383293a..e9abf0c16d718a03138053ca3fa9929c9cd909d4 100644 --- a/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/simplex/IncomingSimplexConnection.java @@ -30,6 +30,7 @@ import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionRegistry; import net.sf.briar.util.ByteUtils; +// FIXME: Read subscription and transport acks class IncomingSimplexConnection { private static final Logger LOG = diff --git a/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java b/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java index e53facc463fd6ecae7b6d2c054cfe33ace064d55..ef9c7c93246da943cd92301515dc08adfb94153e 100644 --- a/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java +++ b/briar-core/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnection.java @@ -25,6 +25,7 @@ import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.util.ByteUtils; +// FIXME: Write subscription and transport acks class OutgoingSimplexConnection { private static final Logger LOG = @@ -66,15 +67,15 @@ class OutgoingSimplexConnection { // There should be enough space for a packet long capacity = conn.getRemainingCapacity(); if(capacity < MAX_PACKET_LENGTH) throw new EOFException(); - // Write a transport update - TransportUpdate t = db.generateTransportUpdate(contactId); - if(t != null) writer.writeTransportUpdate(t); - // If there's space, write a subscription update - capacity = conn.getRemainingCapacity(); - if(capacity >= MAX_PACKET_LENGTH) { - SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); - if(s != null) writer.writeSubscriptionUpdate(s); + // Write transport updates. FIXME: Check for space + Collection<TransportUpdate> updates = + db.generateTransportUpdates(contactId); + if(updates != null) { + for(TransportUpdate t : updates) writer.writeTransportUpdate(t); } + // Write a subscription update. FIXME: Check for space + SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); + if(s != null) writer.writeSubscriptionUpdate(s); // Write acks until you can't write acks no more capacity = conn.getRemainingCapacity(); int maxMessages = writer.getMaxMessagesForAck(capacity); diff --git a/briar-core/src/net/sf/briar/serial/ReaderImpl.java b/briar-core/src/net/sf/briar/serial/ReaderImpl.java index 9fa9994e46bd251f933bf596b3edd25a3c5d38fc..56fab7d60b6beedca808bd286516f188d45d7d69 100644 --- a/briar-core/src/net/sf/briar/serial/ReaderImpl.java +++ b/briar-core/src/net/sf/briar/serial/ReaderImpl.java @@ -98,7 +98,7 @@ class ReaderImpl implements Reader { if(!consumers.remove(c)) throw new IllegalArgumentException(); } - public void addStructReader(int id, StructReader<?> o) { + public void addStructReader(int id, StructReader<?> r) { if(id < 0 || id > 255) throw new IllegalArgumentException(); if(structReaders.length < id + 1) { int len = Math.min(256, Math.max(id + 1, structReaders.length * 2)); @@ -107,7 +107,7 @@ class ReaderImpl implements Reader { structReaders.length); structReaders = newStructReaders; } - structReaders[id] = o; + structReaders[id] = r; } public void removeStructReader(int id) { diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java index c0be507489e29cc318d1c6df2dbbbaa9c9222a9f..18802f9be4416675c719a9fc45becc85d895a038 100644 --- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java @@ -12,7 +12,6 @@ import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Random; @@ -23,18 +22,18 @@ import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; -import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageVerifier; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriterFactory; import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.Subscription; +import net.sf.briar.api.protocol.SubscriptionHole; import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.TransportId; @@ -66,7 +65,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final ConnectionWriterFactory connectionWriterFactory; private final ProtocolReaderFactory protocolReaderFactory; private final ProtocolWriterFactory protocolWriterFactory; - private final PacketFactory packetFactory; private final MessageVerifier messageVerifier; private final ContactId contactId; @@ -80,7 +78,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { private final String messageBody = "Hello world"; private final Collection<MessageId> messageIds; private final Collection<Transport> transports; - private final long timestamp = System.currentTimeMillis(); public ProtocolIntegrationTest() throws Exception { super(); @@ -93,7 +90,6 @@ public class ProtocolIntegrationTest extends BriarTestCase { connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); - packetFactory = i.getInstance(PacketFactory.class); messageVerifier = i.getInstance(MessageVerifier.class); contactId = new ContactId(234); transportId = new TransportId(TestUtils.getRandomId()); @@ -149,33 +145,29 @@ public class ProtocolIntegrationTest extends BriarTestCase { ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1, false); - Ack a = packetFactory.createAck(messageIds); - writer.writeAck(a); + writer.writeAck(new Ack(messageIds)); writer.writeMessage(message.getSerialised()); writer.writeMessage(message1.getSerialised()); writer.writeMessage(message2.getSerialised()); writer.writeMessage(message3.getSerialised()); - Offer o = packetFactory.createOffer(messageIds); - writer.writeOffer(o); + writer.writeOffer(new Offer(messageIds)); BitSet requested = new BitSet(4); requested.set(1); requested.set(3); - Request r = packetFactory.createRequest(requested, 4); - writer.writeRequest(r); - - // Use a LinkedHashMap for predictable iteration order - Map<Group, Long> subs = new LinkedHashMap<Group, Long>(); - subs.put(group, 0L); - subs.put(group1, 0L); - SubscriptionUpdate s = packetFactory.createSubscriptionUpdate( - Collections.<GroupId, GroupId>emptyMap(), subs, 0L, timestamp); + writer.writeRequest(new Request(requested, 4)); + + Collection<SubscriptionHole> holes = Arrays.asList( + new SubscriptionHole(group.getId(), group1.getId())); + Collection<Subscription> subs = Arrays.asList( + new Subscription(group, 0L), new Subscription(group1, 0L)); + SubscriptionUpdate s = new SubscriptionUpdate(holes, subs, 0L, + subscriptionVersion); writer.writeSubscriptionUpdate(s); - TransportUpdate t = packetFactory.createTransportUpdate(transports, - timestamp); + TransportUpdate t = new TransportUpdate(transports, transportVersion); writer.writeTransportUpdate(t); writer.flush(); @@ -232,17 +224,12 @@ public class ProtocolIntegrationTest extends BriarTestCase { // Read the subscription update assertTrue(reader.hasSubscriptionUpdate()); SubscriptionUpdate s = reader.readSubscriptionUpdate(); - Map<Group, Long> subs = s.getSubscriptions(); - assertEquals(2, subs.size()); - assertEquals(Long.valueOf(0L), subs.get(group)); - assertEquals(Long.valueOf(0L), subs.get(group1)); - assertTrue(s.getTimestamp() == timestamp); + // FIXME: Test for equality // Read the transport update assertTrue(reader.hasTransportUpdate()); TransportUpdate t = reader.readTransportUpdate(); - assertEquals(transports, t.getTransports()); - assertTrue(t.getTimestamp() == timestamp); + // FIXME: Test for equality in.close(); } diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java index 389bb46a6314464aae821f5d7a8a32bad54c6fd0..3d4d367acdefde156dfdd942ea323ae342598a17 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java @@ -9,7 +9,6 @@ import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.lifecycle.ShutdownManager; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.db.DatabaseCleaner.Callback; import org.jmock.Expectations; @@ -29,13 +28,11 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -49,7 +46,6 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -62,8 +58,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -72,13 +67,12 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Test public void testExpiringUnsendableMessageDoesNotTriggerBackwardInclusion() - throws DbException { + throws DbException { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -93,8 +87,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -103,13 +96,12 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Test public void testExpiringSendableMessageTriggersBackwardInclusion() - throws DbException { + throws DbException { Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE - 1)); @@ -126,8 +118,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).getFreeSpace(); will(returnValue(MIN_FREE_SPACE)); }}); - Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown); db.checkFreeSpaceAndClean(); @@ -137,15 +128,14 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { @Override protected <T> DatabaseComponent createDatabaseComponent( Database<T> database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory) { - return createDatabaseComponentImpl(database, cleaner, shutdown, - packetFactory); + ShutdownManager shutdown) { + return createDatabaseComponentImpl(database, cleaner, shutdown); } private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( Database<T> database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory) { + ShutdownManager shutdown) { return new DatabaseComponentImpl<T>(database, cleaner, shutdown, - packetFactory, new SystemClock()); + new SystemClock()); } } diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java index 4e03a034116232fe268608712e3e423cb2a7011b..298503a0c7d2af3659874806ec0ea7dea2152cbc 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java @@ -1,6 +1,5 @@ package net.sf.briar.db; -import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; @@ -29,7 +28,6 @@ import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.Transport; @@ -89,7 +87,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { protected abstract <T> DatabaseComponent createDatabaseComponent( Database<T> database, DatabaseCleaner cleaner, - ShutdownManager shutdown, PacketFactory packetFactory); + ShutdownManager shutdown); @Test @SuppressWarnings("unchecked") @@ -99,7 +97,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -167,7 +164,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).close(); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.open(false); db.addListener(listener); @@ -198,7 +195,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) allowing(database).startTransaction(); @@ -217,7 +213,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -231,7 +227,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) oneOf(database).startTransaction(); @@ -254,7 +249,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -269,7 +264,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // setRating(authorId, Rating.GOOD) oneOf(database).startTransaction(); @@ -295,7 +289,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setRating(authorId, Rating.GOOD); @@ -310,7 +304,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -320,7 +313,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -334,7 +327,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -346,7 +338,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -360,7 +352,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -381,7 +372,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -396,7 +387,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) oneOf(database).startTransaction(); @@ -420,7 +410,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalGroupMessage(message); @@ -434,7 +424,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -446,7 +435,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(false)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalPrivateMessage(privateMessage, contactId); @@ -460,7 +449,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -473,7 +461,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addLocalPrivateMessage(privateMessage, contactId); @@ -488,7 +476,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final Ack ack = context.mock(Ack.class); final Offer offer = context.mock(Offer.class); final SubscriptionUpdate subscriptionUpdate = @@ -504,7 +491,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { exactly(16).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); try { db.addContactTransport(contactTransport); @@ -598,7 +585,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // Check whether the contact transport is in the DB (which it's not) exactly(2).of(database).startTransaction(); @@ -609,7 +595,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { exactly(2).of(database).abortTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); try { db.incrementConnectionCounter(contactId, transportId, 0L); @@ -633,8 +619,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Ack ack = context.mock(Ack.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -644,16 +628,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // Get the messages to ack oneOf(database).getMessagesToAck(txn, contactId, 123); will(returnValue(messagesToAck)); - // Create the ack packet - oneOf(packetFactory).createAck(messagesToAck); - will(returnValue(ack)); // Record the messages that were acked oneOf(database).removeMessagesToAck(txn, contactId, messagesToAck); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(ack, db.generateAck(contactId, 123)); + Ack a = db.generateAck(contactId, 123); + assertEquals(messagesToAck, a.getMessageIds()); context.assertIsSatisfied(); } @@ -669,7 +651,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -687,7 +668,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addOutstandingMessages(txn, contactId, sendable); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertEquals(messages, db.generateBatch(contactId, size * 2)); @@ -698,17 +679,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testGenerateBatchFromRequest() throws Exception { final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); final byte[] raw1 = new byte[size]; - final Collection<MessageId> requested = new ArrayList<MessageId>(); - requested.add(messageId); - requested.add(messageId1); - requested.add(messageId2); + final Collection<MessageId> requested = Arrays.asList(messageId, + messageId1, messageId2); final Collection<byte[]> messages = Arrays.asList(raw1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -727,7 +705,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { Collections.singletonList(messageId1)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertEquals(messages, db.generateBatch(contactId, size * 3, requested)); @@ -738,16 +716,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testGenerateOffer() throws Exception { final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); - final Collection<MessageId> offerable = new ArrayList<MessageId>(); - offerable.add(messageId); - offerable.add(messageId1); + final Collection<MessageId> messagesToOffer = Arrays.asList(messageId, + messageId1); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Offer offer = context.mock(Offer.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -756,15 +731,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Get the sendable message IDs oneOf(database).getMessagesToOffer(txn, contactId, 123); - will(returnValue(offerable)); - // Create the packet - oneOf(packetFactory).createOffer(offerable); - will(returnValue(offer)); + will(returnValue(messagesToOffer)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(offer, db.generateOffer(contactId, 123)); + Offer o = db.generateOffer(contactId, 123); + assertEquals(messagesToOffer, o.getMessageIds()); context.assertIsSatisfied(); } @@ -776,9 +749,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -786,55 +756,39 @@ public abstract class DatabaseComponentTest extends BriarTestCase { allowing(database).containsContact(txn, contactId); will(returnValue(true)); // Get the visible holes and subscriptions - oneOf(database).getVisibleHoles(with(txn), with(contactId), - with(any(long.class))); - will(returnValue(Collections.emptyMap())); - oneOf(database).getVisibleSubscriptions(with(txn), with(contactId), - with(any(long.class))); - will(returnValue(Collections.singletonMap(group, 0L))); + oneOf(database).getVisibleHoles(txn, contactId); + will(returnValue(Collections.emptyMap())); // FIXME + oneOf(database).getVisibleSubscriptions(txn, contactId); + will(returnValue(Collections.singletonMap(group, 0L))); // FIXME // Get the expiry time oneOf(database).getExpiryTime(txn); will(returnValue(0L)); - // Create the packet - oneOf(packetFactory).createSubscriptionUpdate( - with(Collections.<GroupId, GroupId>emptyMap()), - with(Collections.singletonMap(group, 0L)), - with(any(long.class)), - with(any(long.class))); - will(returnValue(subscriptionUpdate)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(subscriptionUpdate, - db.generateSubscriptionUpdate(contactId)); + SubscriptionUpdate s = db.generateSubscriptionUpdate(contactId); + // FIXME: Check that the update contains the expected data context.assertIsSatisfied(); } @Test public void testTransportUpdateNotSentUnlessDue() throws Exception { - final long now = System.currentTimeMillis(); Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); allowing(database).commitTransaction(txn); allowing(database).containsContact(txn, contactId); will(returnValue(true)); - // Check whether an update is due - oneOf(database).getTransportsModified(txn); - will(returnValue(now - 1L)); - oneOf(database).getTransportsSent(txn, contactId); - will(returnValue(now)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); assertNull(db.generateTransportUpdate(contactId)); @@ -848,34 +802,21 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); allowing(database).commitTransaction(txn); allowing(database).containsContact(txn, contactId); will(returnValue(true)); - // Check whether an update is due - oneOf(database).getTransportsModified(txn); - will(returnValue(0L)); - oneOf(database).getTransportsSent(txn, contactId); - will(returnValue(0L)); // Get the local transport properties - oneOf(database).getLocalTransports(txn); - will(returnValue(transports)); - oneOf(database).setTransportsSent(with(txn), with(contactId), - with(any(long.class))); - // Create the packet - oneOf(packetFactory).createTransportUpdate(with(transports), - with(any(long.class))); - will(returnValue(transportUpdate)); + oneOf(database).getTransports(txn); + will(returnValue(transports)); // FIXME }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(transportUpdate, db.generateTransportUpdate(contactId)); + TransportUpdate t = db.generateTransportUpdate(contactId); + // FIXME: Check that the update contains the expected data context.assertIsSatisfied(); } @@ -887,8 +828,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); - final Ack ack = context.mock(Ack.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -896,15 +835,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { allowing(database).containsContact(txn, contactId); will(returnValue(true)); // Get the acked messages - oneOf(ack).getMessageIds(); - will(returnValue(Collections.singletonList(messageId))); oneOf(database).removeOutstandingMessages(txn, contactId, Collections.singletonList(messageId)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - db.receiveAck(contactId, ack); + db.receiveAck(contactId, new Ack(Collections.singletonList(messageId))); context.assertIsSatisfied(); } @@ -916,7 +853,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -931,7 +867,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, privateMessage); @@ -945,7 +881,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -959,7 +894,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, privateMessage); @@ -974,7 +909,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -989,7 +923,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1004,7 +938,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1023,7 +956,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1037,7 +970,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1065,7 +997,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1080,7 +1012,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1110,7 +1041,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).addMessageToAck(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveMessage(contactId, message); @@ -1121,10 +1052,8 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testReceiveOffer() throws Exception { final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); - final Collection<MessageId> offered = new ArrayList<MessageId>(); - offered.add(messageId); - offered.add(messageId1); - offered.add(messageId2); + final Collection<MessageId> offered = Arrays.asList(messageId, + messageId1, messageId2); final BitSet expectedRequest = new BitSet(3); expectedRequest.set(0); expectedRequest.set(2); @@ -1133,9 +1062,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final Offer offer = context.mock(Offer.class); - final Request request = context.mock(Request.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1151,14 +1078,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Visible - do not request message # 1 oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId2); will(returnValue(false)); // Not visible - request message # 2 - // Create the packet - oneOf(packetFactory).createRequest(expectedRequest, 3); - will(returnValue(request)); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); - assertEquals(request, db.receiveOffer(contactId, offer)); + Request r = db.receiveOffer(contactId, offer); + assertEquals(expectedRequest, r.getBitmap()); + assertEquals(3, r.getLength()); context.assertIsSatisfied(); } @@ -1167,15 +1093,14 @@ public abstract class DatabaseComponentTest extends BriarTestCase { public void testReceiveSubscriptionUpdate() throws Exception { final GroupId start = new GroupId(TestUtils.getRandomId()); final GroupId end = new GroupId(TestUtils.getRandomId()); - final long expiry = 1234L, timestamp = 5678L; + final long expiry = 1234L; Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final SubscriptionUpdate subscriptionUpdate = - context.mock(SubscriptionUpdate.class); + context.mock(SubscriptionUpdate.class); // FIXME: Don't mock context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1184,21 +1109,18 @@ public abstract class DatabaseComponentTest extends BriarTestCase { will(returnValue(true)); // Get the contents of the update oneOf(subscriptionUpdate).getHoles(); - will(returnValue(Collections.singletonMap(start, end))); - oneOf(subscriptionUpdate).getSubscriptions(); - will(returnValue(Collections.singletonMap(group, 0L))); + will(returnValue(Collections.singletonMap(start, end))); // FIXME + oneOf(subscriptionUpdate).getGroupIds(); + will(returnValue(Collections.singletonMap(group, 0L))); // FIXME oneOf(subscriptionUpdate).getExpiryTime(); will(returnValue(expiry)); - oneOf(subscriptionUpdate).getTimestamp(); - will(returnValue(timestamp)); // Store the contents of the update oneOf(database).removeSubscriptions(txn, contactId, start, end); oneOf(database).addSubscription(txn, contactId, group, 0L); oneOf(database).setExpiryTime(txn, contactId, expiry); - oneOf(database).setSubscriptionsReceived(txn, contactId, timestamp); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveSubscriptionUpdate(contactId, subscriptionUpdate); @@ -1207,15 +1129,13 @@ public abstract class DatabaseComponentTest extends BriarTestCase { @Test public void testReceiveTransportUpdate() throws Exception { - final long timestamp = 1234L; Mockery context = new Mockery(); @SuppressWarnings("unchecked") final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final TransportUpdate transportUpdate = - context.mock(TransportUpdate.class); + context.mock(TransportUpdate.class); // FIXME: Don't mock context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1225,13 +1145,10 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // Get the contents of the update oneOf(transportUpdate).getTransports(); will(returnValue(transports)); - oneOf(transportUpdate).getTimestamp(); - will(returnValue(timestamp)); - oneOf(database).setTransports(txn, contactId, transports, - timestamp); + oneOf(database).setTransports(txn, contactId, transports); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.receiveTransportUpdate(contactId, transportUpdate); @@ -1245,7 +1162,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) @@ -1268,7 +1184,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalGroupMessage(message); @@ -1283,7 +1199,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -1299,7 +1214,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(listener).eventOccurred(with(any(MessageAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalPrivateMessage(privateMessage, contactId); @@ -1315,7 +1230,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ // addLocalGroupMessage(message) @@ -1329,7 +1243,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message was not added, so the listener should not be called }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalGroupMessage(message); @@ -1345,7 +1259,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); @@ -1359,7 +1272,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { // The message was not added, so the listener should not be called }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.addLocalPrivateMessage(privateMessage, contactId); @@ -1377,19 +1290,16 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); will(returnValue(txn)); oneOf(database).getLocalProperties(txn, transportId); will(returnValue(new TransportProperties())); oneOf(database).mergeLocalProperties(txn, transportId, properties); - oneOf(database).setTransportsModified(with(txn), - with(any(long.class))); oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.mergeLocalProperties(transportId, properties); @@ -1406,7 +1316,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1416,7 +1325,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.mergeLocalProperties(transportId, properties); @@ -1431,7 +1340,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ allowing(database).startTransaction(); will(returnValue(txn)); @@ -1442,7 +1350,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.setSeen(contactId, Collections.singletonList(messageId)); @@ -1458,7 +1366,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1473,7 +1380,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { SubscriptionsUpdatedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.setVisibility(groupId, Collections.singletonList(contactId)); @@ -1490,7 +1397,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); final DatabaseListener listener = context.mock(DatabaseListener.class); context.checking(new Expectations() {{ oneOf(database).startTransaction(); @@ -1502,7 +1408,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addListener(listener); db.setVisibility(groupId, both); @@ -1517,7 +1423,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase { final Database<Object> database = context.mock(Database.class); final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class); - final PacketFactory packetFactory = context.mock(PacketFactory.class); context.checking(new Expectations() {{ // addSecrets() oneOf(database).startTransaction(); @@ -1536,7 +1441,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase { oneOf(database).commitTransaction(txn); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner, - shutdown, packetFactory); + shutdown); db.addSecrets(Collections.singletonList(temporarySecret)); assertEquals(Collections.singletonList(temporarySecret), diff --git a/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java index d99bc010fc3d1cf9edce3349d2b8acb2939f7bb8..82f792ab2ecc81d135538a60b2c48a800240afa0 100644 --- a/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java @@ -5,13 +5,11 @@ import static net.sf.briar.api.protocol.Types.ACK; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Collection; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Ack; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.SerialComponent; @@ -19,8 +17,6 @@ import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -33,7 +29,6 @@ public class AckReaderTest extends BriarTestCase { private final SerialComponent serial; private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final Mockery context; public AckReaderTest() throws Exception { super(); @@ -41,61 +36,39 @@ public class AckReaderTest extends BriarTestCase { serial = i.getInstance(SerialComponent.class); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfAckIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - byte[] b = createAck(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - + reader.addStructReader(ACK, new AckReader()); try { reader.readStruct(ACK, Ack.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test - @SuppressWarnings("unchecked") public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - final Ack ack = context.mock(Ack.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createAck(with(any(Collection.class))); - will(returnValue(ack)); - }}); - byte[] b = createAck(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - - assertEquals(ack, reader.readStruct(ACK, Ack.class)); - context.assertIsSatisfied(); + reader.addStructReader(ACK, new AckReader()); + reader.readStruct(ACK, Ack.class); } @Test public void testEmptyAck() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - AckReader ackReader = new AckReader(packetFactory); - byte[] b = createEmptyAck(); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(ACK, ackReader); - + reader.addStructReader(ACK, new AckReader()); try { reader.readStruct(ACK, Ack.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } private byte[] createAck(boolean tooBig) throws Exception { diff --git a/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java index 94543fb5f79e59f442ab51a59b8af6665bb241ed..3b06c4b49ad864ec34abbb3fa372aec62be062bd 100644 --- a/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java +++ b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java @@ -4,17 +4,13 @@ import static net.sf.briar.api.protocol.ProtocolConstants.MAX_AUTHOR_NAME_LENGTH import static net.sf.briar.api.protocol.ProtocolConstants.MAX_BODY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_GROUP_NAME_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PUBLIC_KEY_LENGTH; import static net.sf.briar.api.protocol.ProtocolConstants.MAX_SUBJECT_LENGTH; -import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS; import java.io.ByteArrayOutputStream; import java.security.PrivateKey; import java.util.ArrayList; import java.util.Collection; -import java.util.Map; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; @@ -28,12 +24,8 @@ import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.MessageFactory; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.ProtocolWriterFactory; -import net.sf.briar.api.protocol.Transport; -import net.sf.briar.api.protocol.TransportId; -import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.clock.ClockModule; import net.sf.briar.crypto.CryptoModule; @@ -50,7 +42,6 @@ public class ConstantsTest extends BriarTestCase { private final GroupFactory groupFactory; private final AuthorFactory authorFactory; private final MessageFactory messageFactory; - private final PacketFactory packetFactory; private final ProtocolWriterFactory protocolWriterFactory; public ConstantsTest() throws Exception { @@ -61,7 +52,6 @@ public class ConstantsTest extends BriarTestCase { groupFactory = i.getInstance(GroupFactory.class); authorFactory = i.getInstance(AuthorFactory.class); messageFactory = i.getInstance(MessageFactory.class); - packetFactory = i.getInstance(PacketFactory.class); protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); } @@ -85,8 +75,7 @@ public class ConstantsTest extends BriarTestCase { for(int i = 0; i < maxMessages; i++) { acked.add(new MessageId(TestUtils.getRandomId())); } - Ack a = packetFactory.createAck(acked); - writer.writeAck(a); + writer.writeAck(new Ack(acked)); // Check the size of the serialised ack assertTrue(out.size() <= length); } @@ -136,43 +125,11 @@ public class ConstantsTest extends BriarTestCase { for(int i = 0; i < maxMessages; i++) { offered.add(new MessageId(TestUtils.getRandomId())); } - Offer o = packetFactory.createOffer(offered); - writer.writeOffer(o); + writer.writeOffer(new Offer(offered)); // Check the size of the serialised offer assertTrue(out.size() <= length); } - @Test - public void testTransportsFitIntoUpdate() throws Exception { - // Create the maximum number of plugins, each with the maximum number - // of maximum-length properties - Collection<Transport> transports = new ArrayList<Transport>(); - for(int i = 0; i < MAX_TRANSPORTS; i++) { - TransportId id = new TransportId(TestUtils.getRandomId()); - Transport t = new Transport(id); - Map<String, String> m = t.getProperties(); - for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) { - String key = createRandomString(MAX_PROPERTY_LENGTH); - String value = createRandomString(MAX_PROPERTY_LENGTH); - m.put(key, value); - } - transports.add(t); - } - // Add the transports to an update - ByteArrayOutputStream out = - new ByteArrayOutputStream(MAX_PACKET_LENGTH); - ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, - true); - TransportUpdate t = packetFactory.createTransportUpdate(transports, - Long.MAX_VALUE); - writer.writeTransportUpdate(t); - // Check the size of the serialised update - assertTrue(out.size() > MAX_TRANSPORTS * (UniqueId.LENGTH + 4 - + (MAX_PROPERTIES_PER_TRANSPORT * MAX_PROPERTY_LENGTH * 2)) - + 8); - assertTrue(out.size() <= MAX_PACKET_LENGTH); - } - private static String createRandomString(int length) throws Exception { StringBuilder s = new StringBuilder(length); for(int i = 0; i < length; i++) { diff --git a/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java index 7b89cc8d06599c71fef1658ec2ee98b6b071e809..e10da58b0d12dd682c0b104015eedd946e825f9f 100644 --- a/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java @@ -5,13 +5,11 @@ import static net.sf.briar.api.protocol.Types.OFFER; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; -import java.util.Collection; import net.sf.briar.BriarTestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.FormatException; import net.sf.briar.api.protocol.Offer; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; import net.sf.briar.api.serial.SerialComponent; @@ -19,8 +17,6 @@ import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -33,7 +29,6 @@ public class OfferReaderTest extends BriarTestCase { private final SerialComponent serial; private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final Mockery context; public OfferReaderTest() throws Exception { super(); @@ -41,61 +36,39 @@ public class OfferReaderTest extends BriarTestCase { serial = i.getInstance(SerialComponent.class); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfOfferIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - byte[] b = createOffer(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - + reader.addStructReader(OFFER, new OfferReader()); try { reader.readStruct(OFFER, Offer.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test - @SuppressWarnings("unchecked") public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - final Offer offer = context.mock(Offer.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createOffer(with(any(Collection.class))); - will(returnValue(offer)); - }}); - byte[] b = createOffer(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - - assertEquals(offer, reader.readStruct(OFFER, Offer.class)); - context.assertIsSatisfied(); + reader.addStructReader(OFFER, new OfferReader()); + reader.readStruct(OFFER, Offer.class); } @Test public void testEmptyOffer() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - OfferReader offerReader = new OfferReader(packetFactory); - byte[] b = createEmptyOffer(); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(OFFER, offerReader); - + reader.addStructReader(OFFER, new OfferReader()); try { reader.readStruct(OFFER, Offer.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } private byte[] createOffer(boolean tooBig) throws Exception { diff --git a/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java index 6c916c3e56828ef502f53cddf247ad19f59b80de..b89b9209d14ff5603a453603baa4b904af5be379 100644 --- a/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java +++ b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.util.BitSet; import net.sf.briar.BriarTestCase; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.ProtocolWriter; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.SerialComponent; @@ -24,7 +23,6 @@ public class ProtocolWriterImplTest extends BriarTestCase { // FIXME: This is an integration test, not a unit test - private final PacketFactory packetFactory; private final SerialComponent serial; private final WriterFactory writerFactory; @@ -32,7 +30,6 @@ public class ProtocolWriterImplTest extends BriarTestCase { super(); Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(), new ProtocolModule(), new SerialModule()); - packetFactory = i.getInstance(PacketFactory.class); serial = i.getInstance(SerialComponent.class); writerFactory = i.getInstance(WriterFactory.class); } @@ -54,8 +51,7 @@ public class ProtocolWriterImplTest extends BriarTestCase { b.set(11); b.set(12); b.set(15); - Request r = packetFactory.createRequest(b, 16); - w.writeRequest(r); + w.writeRequest(new Request(b, 16)); // Short user tag 6, 0 as uint7, short bytes with length 2, 0xD959 byte[] output = out.toByteArray(); assertEquals("C6" + "00" + "92" + "D959", @@ -78,8 +74,7 @@ public class ProtocolWriterImplTest extends BriarTestCase { b.set(9); b.set(11); b.set(12); - Request r = packetFactory.createRequest(b, 13); - w.writeRequest(r); + w.writeRequest(new Request(b, 13)); // Short user tag 6, 3 as uint7, short bytes with length 2, 0x59D8 byte[] output = out.toByteArray(); assertEquals("C6" + "03" + "92" + "59D8", diff --git a/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java index d92b0dc420c936facac91620d1995085a1e0996f..ec9d2746d449907dd4c33d3565c2951cf72f9bfe 100644 --- a/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java +++ b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java @@ -9,7 +9,6 @@ import java.util.BitSet; import net.sf.briar.BriarTestCase; import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.PacketFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.ReaderFactory; @@ -19,8 +18,6 @@ import net.sf.briar.clock.ClockModule; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.serial.SerialModule; -import org.jmock.Expectations; -import org.jmock.Mockery; import org.junit.Test; import com.google.inject.Guice; @@ -32,8 +29,6 @@ public class RequestReaderTest extends BriarTestCase { private final ReaderFactory readerFactory; private final WriterFactory writerFactory; - private final PacketFactory packetFactory; - private final Mockery context; public RequestReaderTest() throws Exception { super(); @@ -41,45 +36,27 @@ public class RequestReaderTest extends BriarTestCase { new ProtocolModule(), new SerialModule()); readerFactory = i.getInstance(ReaderFactory.class); writerFactory = i.getInstance(WriterFactory.class); - packetFactory = i.getInstance(PacketFactory.class); - context = new Mockery(); } @Test public void testFormatExceptionIfRequestIsTooLarge() throws Exception { - PacketFactory packetFactory = context.mock(PacketFactory.class); - RequestReader requestReader = new RequestReader(packetFactory); - byte[] b = createRequest(true); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(REQUEST, requestReader); - + reader.addStructReader(REQUEST, new RequestReader()); try { reader.readStruct(REQUEST, Request.class); fail(); } catch(FormatException expected) {} - context.assertIsSatisfied(); } @Test public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception { - final PacketFactory packetFactory = context.mock(PacketFactory.class); - RequestReader requestReader = new RequestReader(packetFactory); - final Request request = context.mock(Request.class); - context.checking(new Expectations() {{ - oneOf(packetFactory).createRequest(with(any(BitSet.class)), - with(any(int.class))); - will(returnValue(request)); - }}); - byte[] b = createRequest(false); ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - reader.addStructReader(REQUEST, requestReader); - - assertEquals(request, reader.readStruct(REQUEST, Request.class)); - context.assertIsSatisfied(); + reader.addStructReader(REQUEST, new RequestReader()); + reader.readStruct(REQUEST, Request.class); } @Test @@ -104,7 +81,7 @@ public class RequestReaderTest extends BriarTestCase { // Deserialise the request ByteArrayInputStream in = new ByteArrayInputStream(b); Reader reader = readerFactory.createReader(in); - RequestReader requestReader = new RequestReader(packetFactory); + RequestReader requestReader = new RequestReader(); reader.addStructReader(REQUEST, requestReader); Request r = reader.readStruct(REQUEST, Request.class); BitSet decoded = r.getBitmap(); diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java index cffd54b1e97944a360daa01e960c107f3d7bcd01..eea453a76800c6028fd7770a080fd1f63656bb5b 100644 --- a/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java +++ b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java @@ -5,8 +5,6 @@ import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; -import java.util.Collection; -import java.util.Collections; import java.util.Random; import net.sf.briar.BriarTestCase; @@ -161,17 +159,9 @@ public class SimplexProtocolIntegrationTest extends BriarTestCase { MessageListener listener = new MessageListener(); db.addListener(listener); // Fake a transport update from Alice - TransportUpdate transportUpdate = new TransportUpdate() { - - public Collection<Transport> getTransports() { - Transport t = new Transport(transportId); - return Collections.singletonList(t); - } - - public long getTimestamp() { - return System.currentTimeMillis(); - } - }; + Transport t = new Transport(transportId); + TransportUpdate transportUpdate = new TransportUpdate(t, + transportVersion); db.receiveTransportUpdate(contactId, transportUpdate); // Create a connection recogniser and recognise the connection ByteArrayInputStream in = new ByteArrayInputStream(b);