diff --git a/api/net/sf/briar/api/ContactId.java b/api/net/sf/briar/api/ContactId.java index 0dbdc140fbd7b8d98a0c056704b5853db7898660..d8a9f7a388c4ab7f1df97bec91c0c61766670e8b 100644 --- a/api/net/sf/briar/api/ContactId.java +++ b/api/net/sf/briar/api/ContactId.java @@ -1,6 +1,9 @@ package net.sf.briar.api; -/** Type-safe wrapper for an integer that uniquely identifies a contact. */ +/** + * Type-safe wrapper for an integer that uniquely identifies a contact within + * the scope of a single node. + */ public class ContactId { private final int id; @@ -23,9 +26,4 @@ public class ContactId { public int hashCode() { return id; } - - @Override - public String toString() { - return String.valueOf(id); - } } diff --git a/api/net/sf/briar/api/TransportId.java b/api/net/sf/briar/api/TransportId.java deleted file mode 100644 index 591ba0769cdc84ee26f8d4bd8347fc1949a6576a..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/TransportId.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.sf.briar.api; - -import java.io.IOException; - -import net.sf.briar.api.serial.Writable; -import net.sf.briar.api.serial.Writer; - -/** - * Type-safe wrapper for an integer that uniquely identifies a transport plugin. - */ -public class TransportId implements Writable { - - public static final int MIN_ID = 0; - public static final int MAX_ID = 65535; - - private final int id; - - public TransportId(int id) { - if(id < MIN_ID || id > MAX_ID) throw new IllegalArgumentException(); - this.id = id; - } - - public int getInt() { - return id; - } - - public void writeTo(Writer w) throws IOException { - w.writeInt32(id); - } - - @Override - public boolean equals(Object o) { - if(o instanceof TransportId) return id == ((TransportId) o).id; - return false; - } - - @Override - public int hashCode() { - return id; - } -} diff --git a/api/net/sf/briar/api/db/DatabaseComponent.java b/api/net/sf/briar/api/db/DatabaseComponent.java index 4d50c4cc5c656ff93c1b684e53306575e1e4c969..1e1402b1f1afd7c479ef30cd27f4b1222235e218 100644 --- a/api/net/sf/briar/api/db/DatabaseComponent.java +++ b/api/net/sf/briar/api/db/DatabaseComponent.java @@ -7,7 +7,6 @@ import java.util.Map; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.protocol.Ack; @@ -19,6 +18,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.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; @@ -51,11 +53,10 @@ public interface DatabaseComponent { void removeListener(DatabaseListener d); /** - * Adds a new contact to the database with the given transport properties - * and shared secret, returns an ID for the contact. + * Adds a new contact to the database with the given secret and returns an + * ID for the contact. */ - ContactId addContact(Map<TransportId, TransportProperties> transports, - byte[] secret) throws DbException; + ContactId addContact(byte[] secret) throws DbException; /** Adds a locally generated group message to the database. */ void addLocalGroupMessage(Message m) throws DbException; @@ -63,6 +64,12 @@ public interface DatabaseComponent { /** Adds a locally generated private message to the database. */ void addLocalPrivateMessage(Message m, ContactId c) throws DbException; + /** + * Allocates and returns a local index for the given transport. Returns + * null if all indices have been allocated. + */ + TransportIndex addTransport(TransportId t) throws DbException; + /** * Generates an acknowledgement for the given contact. * @return True if any batch IDs were added to the acknowledgement. @@ -109,24 +116,29 @@ public interface DatabaseComponent { * Returns an outgoing connection number for the given contact and * transport. */ - long getConnectionNumber(ContactId c, TransportId t) throws DbException; + long getConnectionNumber(ContactId c, TransportIndex i) throws DbException; /** * Returns the connection reordering window for the given contact and * transport. */ - ConnectionWindow getConnectionWindow(ContactId c, TransportId t) + ConnectionWindow getConnectionWindow(ContactId c, TransportIndex i) throws DbException; /** Returns the IDs of all contacts. */ Collection<ContactId> getContacts() throws DbException; + /** + * Returns the local index for the given transport, or null if no index + * has been allocated. + */ + TransportIndex getLocalIndex(TransportId t) throws DbException; + /** Returns the local transport properties for the given transport. */ TransportProperties getLocalProperties(TransportId t) throws DbException; - /** Returns all local transport properties. */ - Map<TransportId, TransportProperties> getLocalTransports() - 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; @@ -134,6 +146,13 @@ public interface DatabaseComponent { /** Returns the user's rating for the given author. */ Rating getRating(AuthorId a) throws DbException; + /** + * Returns the given contact's index for the given transport, or null if + * the contact does not support the transport. + */ + TransportIndex getRemoteIndex(ContactId c, TransportId t) + throws DbException; + /** Returns all remote transport properties for the given transport. */ Map<ContactId, TransportProperties> getRemoteProperties(TransportId t) throws DbException; @@ -191,8 +210,8 @@ public interface DatabaseComponent { * Sets the connection reordering window for the given contact and * transport. */ - void setConnectionWindow(ContactId c, TransportId t, ConnectionWindow w) - throws DbException; + void setConnectionWindow(ContactId c, TransportIndex i, + ConnectionWindow w) throws DbException; /** * Sets the local transport properties for the given transport, replacing diff --git a/api/net/sf/briar/api/db/event/ContactRemovedEvent.java b/api/net/sf/briar/api/db/event/ContactRemovedEvent.java index face9f4e6d1eae72d19280ef3e9f867e5d26506a..74eed602994e691912f2f385d6d8a666b65c97bb 100644 --- a/api/net/sf/briar/api/db/event/ContactRemovedEvent.java +++ b/api/net/sf/briar/api/db/event/ContactRemovedEvent.java @@ -3,9 +3,15 @@ package net.sf.briar.api.db.event; import net.sf.briar.api.ContactId; /** An event that is broadcast when a contact is removed. */ -public class ContactRemovedEvent extends ContactAddedEvent { +public class ContactRemovedEvent extends DatabaseEvent { + + private final ContactId contactId; public ContactRemovedEvent(ContactId contactId) { - super(contactId); + this.contactId = contactId; + } + + public ContactId getContactId() { + return contactId; } } diff --git a/api/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java b/api/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..c60c23b83b1000df6d6b7d933bbb8f2822abfb2e --- /dev/null +++ b/api/net/sf/briar/api/db/event/LocalTransportsUpdatedEvent.java @@ -0,0 +1,9 @@ +package net.sf.briar.api.db.event; + +/** + * An event that is broadcast when the local transport properties are + * updated. + */ +public class LocalTransportsUpdatedEvent extends DatabaseEvent { + +} diff --git a/api/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java b/api/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..8418d235951e7abdb99b28ebd1d2297fa0a1d263 --- /dev/null +++ b/api/net/sf/briar/api/db/event/RemoteTransportsUpdatedEvent.java @@ -0,0 +1,17 @@ +package net.sf.briar.api.db.event; + +import net.sf.briar.api.ContactId; + +/** An event that is broadcast when a contact's transports are updated. */ +public class RemoteTransportsUpdatedEvent extends DatabaseEvent { + + private final ContactId contactId; + + public RemoteTransportsUpdatedEvent(ContactId contactId) { + this.contactId = contactId; + } + + public ContactId getContactId() { + return contactId; + } +} diff --git a/api/net/sf/briar/api/db/event/TransportAddedEvent.java b/api/net/sf/briar/api/db/event/TransportAddedEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..75156882292478155b0ef2b3c29e30511c5e6f8b --- /dev/null +++ b/api/net/sf/briar/api/db/event/TransportAddedEvent.java @@ -0,0 +1,17 @@ +package net.sf.briar.api.db.event; + +import net.sf.briar.api.protocol.TransportId; + +/** An event that is broadcast when a transport is added. */ +public class TransportAddedEvent extends DatabaseEvent { + + private final TransportId transportId; + + public TransportAddedEvent(TransportId transportId) { + this.transportId = transportId; + } + + public TransportId getTransportId() { + return transportId; + } +} diff --git a/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java b/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java deleted file mode 100644 index 1360fe9ace25847ed97332eac6f22a68ee5df431..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/db/event/TransportsUpdatedEvent.java +++ /dev/null @@ -1,6 +0,0 @@ -package net.sf.briar.api.db.event; - -/** An event that is broadcast when the local transports are updated. */ -public class TransportsUpdatedEvent extends DatabaseEvent { - -} diff --git a/api/net/sf/briar/api/plugins/BatchPluginFactory.java b/api/net/sf/briar/api/plugins/BatchPluginFactory.java index e8c665b216b6d248a5f233f5eee96e36bda48f62..1a6d6a0799058a4a24481dc8d886b2f3bef5141c 100644 --- a/api/net/sf/briar/api/plugins/BatchPluginFactory.java +++ b/api/net/sf/briar/api/plugins/BatchPluginFactory.java @@ -4,6 +4,5 @@ import java.util.concurrent.Executor; public interface BatchPluginFactory { - BatchPlugin createPlugin(Executor executor, - BatchPluginCallback callback); + BatchPlugin createPlugin(Executor executor, BatchPluginCallback callback); } diff --git a/api/net/sf/briar/api/plugins/Plugin.java b/api/net/sf/briar/api/plugins/Plugin.java index 3358c1ff6a31ee1c7e37310c3df57cb73f90c3bf..6d476087ca1cc63877cd86f8c057b551e3624e53 100644 --- a/api/net/sf/briar/api/plugins/Plugin.java +++ b/api/net/sf/briar/api/plugins/Plugin.java @@ -2,7 +2,7 @@ package net.sf.briar.api.plugins; import java.io.IOException; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportId; public interface Plugin { diff --git a/api/net/sf/briar/api/plugins/PluginManager.java b/api/net/sf/briar/api/plugins/PluginManager.java index 379c07bb977315c5060029491c4eb453df12feeb..2fae4b17c8789b8d315d2d67fbfc8f495ae4608a 100644 --- a/api/net/sf/briar/api/plugins/PluginManager.java +++ b/api/net/sf/briar/api/plugins/PluginManager.java @@ -3,14 +3,13 @@ package net.sf.briar.api.plugins; public interface PluginManager { /** - * Starts all the plugins the manager knows about and returns the number of - * plugins successfully started. + * Starts the plugins and returns the number of plugins successfully + * started. */ int startPlugins(); /** - * Stops all the plugins started by startPlugins() and returns the number - * of plugins successfully stopped. + * Stops the plugins and returns the number of plugins successfully stopped. */ int stopPlugins(); } diff --git a/api/net/sf/briar/api/plugins/StreamPluginFactory.java b/api/net/sf/briar/api/plugins/StreamPluginFactory.java index 97246ffc5ebc0f2ac99bfe041053d22b9b65da8b..f8a0388b70ad44184b37990eb1a38f3ac641ec2e 100644 --- a/api/net/sf/briar/api/plugins/StreamPluginFactory.java +++ b/api/net/sf/briar/api/plugins/StreamPluginFactory.java @@ -4,6 +4,5 @@ import java.util.concurrent.Executor; public interface StreamPluginFactory { - StreamPlugin createPlugin(Executor executor, - StreamPluginCallback callback); + StreamPlugin createPlugin(Executor executor, StreamPluginCallback callback); } diff --git a/api/net/sf/briar/api/protocol/Author.java b/api/net/sf/briar/api/protocol/Author.java index 2c72bc4986ebf1912277194f6a419e44d01db95a..52266b9621ca4de098d7bb10f50ab48412b399f1 100644 --- a/api/net/sf/briar/api/protocol/Author.java +++ b/api/net/sf/briar/api/protocol/Author.java @@ -1,15 +1,7 @@ package net.sf.briar.api.protocol; -import net.sf.briar.api.serial.Writable; - /** A pseudonymous author of messages. */ -public interface Author extends Writable { - - /** The maximum length of an author's name in UTF-8 bytes. */ - static final int MAX_NAME_LENGTH = 50; - - /** The maximum length of an author's public key in bytes. */ - static final int MAX_PUBLIC_KEY_LENGTH = 100; +public interface Author { /** Returns the author's unique identifier. */ AuthorId getId(); diff --git a/api/net/sf/briar/api/protocol/AuthorId.java b/api/net/sf/briar/api/protocol/AuthorId.java index 9cefbfc5f5a2e4f0e539c14f4c0a9053353c1d18..578c89c2accf4374de24d59bfcd7541808d79f24 100644 --- a/api/net/sf/briar/api/protocol/AuthorId.java +++ b/api/net/sf/briar/api/protocol/AuthorId.java @@ -1,10 +1,7 @@ package net.sf.briar.api.protocol; -import java.io.IOException; import java.util.Arrays; -import net.sf.briar.api.serial.Writer; - /** Type-safe wrapper for a byte array that uniquely identifies an author. */ public class AuthorId extends UniqueId { @@ -12,11 +9,6 @@ public class AuthorId extends UniqueId { super(id); } - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.AUTHOR_ID); - w.writeBytes(id); - } - @Override public boolean equals(Object o) { if(o instanceof AuthorId) diff --git a/api/net/sf/briar/api/protocol/BatchId.java b/api/net/sf/briar/api/protocol/BatchId.java index a3d573bc9b2f559dce8e2c007fee9646618ae3ac..ab33800761d9558416896dde3862f3c69d83793e 100644 --- a/api/net/sf/briar/api/protocol/BatchId.java +++ b/api/net/sf/briar/api/protocol/BatchId.java @@ -1,10 +1,7 @@ package net.sf.briar.api.protocol; -import java.io.IOException; import java.util.Arrays; -import net.sf.briar.api.serial.Writer; - /** * Type-safe wrapper for a byte array that uniquely identifies a batch of * messages. @@ -15,11 +12,6 @@ public class BatchId extends UniqueId { super(id); } - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.BATCH_ID); - w.writeBytes(id); - } - @Override public boolean equals(Object o) { if(o instanceof BatchId) diff --git a/api/net/sf/briar/api/protocol/Group.java b/api/net/sf/briar/api/protocol/Group.java index 587bc72869b90091054e3633e83a068626ffd4a0..cc1f6ccf07a313240adc2c5ad4ffd43573d2f7df 100644 --- a/api/net/sf/briar/api/protocol/Group.java +++ b/api/net/sf/briar/api/protocol/Group.java @@ -1,15 +1,7 @@ package net.sf.briar.api.protocol; -import net.sf.briar.api.serial.Writable; - /** A group to which users may subscribe. */ -public interface Group extends Writable { - - /** The maximum length of a group's name in UTF-8 bytes. */ - static final int MAX_NAME_LENGTH = 50; - - /** The maximum length of a group's public key in bytes. */ - static final int MAX_PUBLIC_KEY_LENGTH = 100; +public interface Group { /** Returns the group's unique identifier. */ GroupId getId(); diff --git a/api/net/sf/briar/api/protocol/GroupId.java b/api/net/sf/briar/api/protocol/GroupId.java index 0ea39e3b0fc6e58f2cdc1b6d94528d766d971701..532941867942b4bb02f4e8a6ed828820c7cbb20f 100644 --- a/api/net/sf/briar/api/protocol/GroupId.java +++ b/api/net/sf/briar/api/protocol/GroupId.java @@ -1,10 +1,7 @@ package net.sf.briar.api.protocol; -import java.io.IOException; import java.util.Arrays; -import net.sf.briar.api.serial.Writer; - /** * Type-safe wrapper for a byte array that uniquely identifies a group to which * users may subscribe. @@ -15,11 +12,6 @@ public class GroupId extends UniqueId { super(id); } - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.GROUP_ID); - w.writeBytes(id); - } - @Override public boolean equals(Object o) { if(o instanceof GroupId) diff --git a/api/net/sf/briar/api/protocol/Message.java b/api/net/sf/briar/api/protocol/Message.java index d204fd667f1d9673e4e394e5f688061f274cf837..e4b702fc7385d84c40fd2702f2aa60bbaf274c9e 100644 --- a/api/net/sf/briar/api/protocol/Message.java +++ b/api/net/sf/briar/api/protocol/Message.java @@ -2,23 +2,6 @@ package net.sf.briar.api.protocol; public interface Message { - /** - * The maximum length of a message body in bytes. To allow for future - * changes in the protocol, this is smaller than the maximum packet length - * even when all the message's other fields have their maximum lengths. - */ - static final int MAX_BODY_LENGTH = - ProtocolConstants.MAX_PACKET_LENGTH - 1024; - - /** The maximum length of a subject line in UTF-8 bytes. */ - static final int MAX_SUBJECT_LENGTH = 100; - - /** The maximum length of a signature in bytes. */ - static final int MAX_SIGNATURE_LENGTH = 100; - - /** The length of the random salt in bytes. */ - static final int SALT_LENGTH = 8; - /** Returns the message's unique identifier. */ MessageId getId(); diff --git a/api/net/sf/briar/api/protocol/MessageId.java b/api/net/sf/briar/api/protocol/MessageId.java index 5f1bd67c0477982e968468d4c5ba03585d0c55c4..e540f769ea820de7abd40dbb776075af264be3dd 100644 --- a/api/net/sf/briar/api/protocol/MessageId.java +++ b/api/net/sf/briar/api/protocol/MessageId.java @@ -1,10 +1,7 @@ package net.sf.briar.api.protocol; -import java.io.IOException; import java.util.Arrays; -import net.sf.briar.api.serial.Writer; - /** Type-safe wrapper for a byte array that uniquely identifies a message. */ public class MessageId extends UniqueId { @@ -12,11 +9,6 @@ public class MessageId extends UniqueId { super(id); } - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.MESSAGE_ID); - w.writeBytes(id); - } - @Override public boolean equals(Object o) { if(o instanceof MessageId) diff --git a/api/net/sf/briar/api/protocol/ProtocolConstants.java b/api/net/sf/briar/api/protocol/ProtocolConstants.java index a84f20e572fcdbb9de8de0ca118f16bb84ed899e..29f31df89975b9698beedc2a1c06dd514578854c 100644 --- a/api/net/sf/briar/api/protocol/ProtocolConstants.java +++ b/api/net/sf/briar/api/protocol/ProtocolConstants.java @@ -11,4 +11,41 @@ public interface ProtocolConstants { */ static final int MAX_PACKET_LENGTH = TransportConstants.MIN_CONNECTION_LENGTH - 1024; + + /** The maximum number of transport plugins a node may support. */ + static final int MAX_TRANSPORTS = 50; + + /** The maximum number of properties per transport plugin. */ + static final int MAX_PROPERTIES_PER_TRANSPORT = 100; + + /** The maximum length of a property's key or value in UTF-8 bytes. */ + static final int MAX_PROPERTY_LENGTH = 100; + + /** The maximum number of groups a node may subscribe to. */ + static final int MAX_GROUPS = 6000; + + /** The maximum length of a group's name in UTF-8 bytes. */ + static final int MAX_GROUP_NAME_LENGTH = 50; + + /** The maximum length of a serialised public key in bytes. */ + static final int MAX_PUBLIC_KEY_LENGTH = 100; + + /** The maximum length of an author's name in UTF-8 bytes. */ + static final int MAX_AUTHOR_NAME_LENGTH = 50; + + /** + * The maximum length of a message body in bytes. To allow for future + * changes in the protocol, this is smaller than the maximum packet length + * even when all the message's other fields have their maximum lengths. + */ + static final int MAX_BODY_LENGTH = MAX_PACKET_LENGTH - 1024; + + /** The maximum length of a message's subject line in UTF-8 bytes. */ + static final int MAX_SUBJECT_LENGTH = 100; + + /** The maximum length of a signature in bytes. */ + static final int MAX_SIGNATURE_LENGTH = 100; + + /** The length of a message's random salt in bytes. */ + static final int SALT_LENGTH = 8; } diff --git a/api/net/sf/briar/api/protocol/SubscriptionUpdate.java b/api/net/sf/briar/api/protocol/SubscriptionUpdate.java index 31e2f4672f4b31b303700da7d479d8c0fd150d1d..05a17ed54ad1385071da215ed3fdd30c2e99ba3c 100644 --- a/api/net/sf/briar/api/protocol/SubscriptionUpdate.java +++ b/api/net/sf/briar/api/protocol/SubscriptionUpdate.java @@ -5,9 +5,6 @@ import java.util.Map; /** A packet updating the sender's subscriptions. */ public interface SubscriptionUpdate { - /** The maximum number of subscriptions per update. */ - static final int MAX_SUBS_PER_UPDATE = 6000; - /** Returns the subscriptions contained in the update. */ Map<Group, Long> getSubscriptions(); diff --git a/api/net/sf/briar/api/protocol/Transport.java b/api/net/sf/briar/api/protocol/Transport.java new file mode 100644 index 0000000000000000000000000000000000000000..37c9ab021c65e35050ece282d044820e4bd4bbfb --- /dev/null +++ b/api/net/sf/briar/api/protocol/Transport.java @@ -0,0 +1,47 @@ +package net.sf.briar.api.protocol; + +import java.util.Map; +import java.util.TreeMap; + +public class Transport extends TreeMap<String, String> { + + private static final long serialVersionUID = 4900420175715429560L; + + private final TransportId id; + private final TransportIndex index; + + public Transport(TransportId id, TransportIndex index, + Map<String, String> p) { + super(p); + this.id = id; + this.index = index; + } + + public Transport(TransportId id, TransportIndex index) { + super(); + this.id = id; + this.index = index; + } + + public TransportId getId() { + return id; + } + + public TransportIndex getIndex() { + return index; + } + + @Override + public int hashCode() { + return id.hashCode(); + } + + @Override + public boolean equals(Object o) { + if(o instanceof Transport) { + Transport t = (Transport) o; + return id.equals(t.id) && index.equals(t.index) && super.equals(o); + } + return false; + } +} diff --git a/api/net/sf/briar/api/protocol/TransportId.java b/api/net/sf/briar/api/protocol/TransportId.java new file mode 100644 index 0000000000000000000000000000000000000000..8f295683dd9b11ee685149a3a56296800d5a08a7 --- /dev/null +++ b/api/net/sf/briar/api/protocol/TransportId.java @@ -0,0 +1,21 @@ +package net.sf.briar.api.protocol; + +import java.util.Arrays; + +/** + * Type-safe wrapper for a byte array that uniquely identifies a transport + * plugin. + */ +public class TransportId extends UniqueId { + + public TransportId(byte[] id) { + super(id); + } + + @Override + public boolean equals(Object o) { + if(o instanceof TransportId) + return Arrays.equals(id, ((TransportId) o).id); + return false; + } +} diff --git a/api/net/sf/briar/api/protocol/TransportIndex.java b/api/net/sf/briar/api/protocol/TransportIndex.java new file mode 100644 index 0000000000000000000000000000000000000000..c167b49c69ac23498bea7870aae293d216a2674d --- /dev/null +++ b/api/net/sf/briar/api/protocol/TransportIndex.java @@ -0,0 +1,33 @@ +package net.sf.briar.api.protocol; + + +/** + * Type-safe wrapper for an integer that uniquely identifies a transport plugin + * within the scope of a single node. + */ +public class TransportIndex { + + private final int index; + + public TransportIndex(int index) { + if(index < 0 || index >= ProtocolConstants.MAX_TRANSPORTS) + throw new IllegalArgumentException(); + this.index = index; + } + + public int getInt() { + return index; + } + + @Override + public boolean equals(Object o) { + if(o instanceof TransportIndex) + return index == ((TransportIndex) o).index; + return false; + } + + @Override + public int hashCode() { + return index; + } +} diff --git a/api/net/sf/briar/api/protocol/TransportUpdate.java b/api/net/sf/briar/api/protocol/TransportUpdate.java index c9ec131f8d9f381213d4d21a3a6b5e9875529558..48615ccd7e72531f730fad5fce3492333d68f569 100644 --- a/api/net/sf/briar/api/protocol/TransportUpdate.java +++ b/api/net/sf/briar/api/protocol/TransportUpdate.java @@ -1,24 +1,12 @@ package net.sf.briar.api.protocol; -import java.util.Map; - -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; +import java.util.Collection; /** A packet updating the sender's transport properties. */ public interface TransportUpdate { - /** The maximum length of a property's key or value in UTF-8 bytes. */ - static final int MAX_KEY_OR_VALUE_LENGTH = 100; - - /** The maximum number of properties per plugin. */ - static final int MAX_PROPERTIES_PER_PLUGIN = 100; - - /** The maximum number of plugins per update. */ - static final int MAX_PLUGINS_PER_UPDATE = 50; - - /** Returns the transport properties contained in the update. */ - Map<TransportId, TransportProperties> getTransports(); + /** Returns the transports contained in the update. */ + Collection<Transport> getTransports(); /** * Returns the update's timestamp. Updates that are older than the newest diff --git a/api/net/sf/briar/api/protocol/Types.java b/api/net/sf/briar/api/protocol/Types.java index 54dc03d9689b7ea70bb221f66c544b3e9c719b05..8ace5c0d9eb8a5a844d9a7b9a81f92e7676e012a 100644 --- a/api/net/sf/briar/api/protocol/Types.java +++ b/api/net/sf/briar/api/protocol/Types.java @@ -5,16 +5,14 @@ public interface Types { static final int ACK = 0; static final int AUTHOR = 1; - static final int AUTHOR_ID = 2; - static final int BATCH = 3; - static final int BATCH_ID = 4; - static final int GROUP = 5; - static final int GROUP_ID = 6; - static final int MESSAGE = 7; - static final int MESSAGE_ID = 8; - static final int OFFER = 9; - static final int REQUEST = 10; - static final int SUBSCRIPTION_UPDATE = 11; - static final int TRANSPORT_PROPERTIES = 12; - static final int TRANSPORT_UPDATE = 13; + static final int BATCH = 2; + static final int BATCH_ID = 3; + static final int GROUP = 4; + static final int MESSAGE = 5; + static final int MESSAGE_ID = 6; + static final int OFFER = 7; + static final int REQUEST = 8; + static final int SUBSCRIPTION_UPDATE = 9; + static final int TRANSPORT = 10; + static final int TRANSPORT_UPDATE = 11; } diff --git a/api/net/sf/briar/api/protocol/UniqueId.java b/api/net/sf/briar/api/protocol/UniqueId.java index cb379b89a5a0065a84c06231cfafd4667b3c60b1..8124ed03f0e930c70d4955c2ac23475a0bf7b5a4 100644 --- a/api/net/sf/briar/api/protocol/UniqueId.java +++ b/api/net/sf/briar/api/protocol/UniqueId.java @@ -2,9 +2,7 @@ package net.sf.briar.api.protocol; import java.util.Arrays; -import net.sf.briar.api.serial.Writable; - -public abstract class UniqueId implements Writable { +public abstract class UniqueId { /** The length of a unique identifier in bytes. */ public static final int LENGTH = 32; diff --git a/api/net/sf/briar/api/protocol/writers/AuthorWriter.java b/api/net/sf/briar/api/protocol/writers/AuthorWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..702b0963ced259889e1dd1f5757bb3232b4e35d4 --- /dev/null +++ b/api/net/sf/briar/api/protocol/writers/AuthorWriter.java @@ -0,0 +1,11 @@ +package net.sf.briar.api.protocol.writers; + +import java.io.IOException; + +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.serial.Writer; + +public interface AuthorWriter { + + void writeAuthor(Writer w, Author a) throws IOException; +} diff --git a/api/net/sf/briar/api/protocol/writers/GroupWriter.java b/api/net/sf/briar/api/protocol/writers/GroupWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..40d595b1a7aa77a9530757f73b704016d8ed0b03 --- /dev/null +++ b/api/net/sf/briar/api/protocol/writers/GroupWriter.java @@ -0,0 +1,11 @@ +package net.sf.briar.api.protocol.writers; + +import java.io.IOException; + +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.serial.Writer; + +public interface GroupWriter { + + void writeGroup(Writer w, Group g) throws IOException; +} diff --git a/api/net/sf/briar/api/protocol/MessageEncoder.java b/api/net/sf/briar/api/protocol/writers/MessageEncoder.java similarity index 85% rename from api/net/sf/briar/api/protocol/MessageEncoder.java rename to api/net/sf/briar/api/protocol/writers/MessageEncoder.java index d01f5f0a9dc57739a91c2ca7883693a870070160..bb144720efc2e189d805b1354e54eb73f97dc8b9 100644 --- a/api/net/sf/briar/api/protocol/MessageEncoder.java +++ b/api/net/sf/briar/api/protocol/writers/MessageEncoder.java @@ -1,9 +1,14 @@ -package net.sf.briar.api.protocol; +package net.sf.briar.api.protocol.writers; import java.io.IOException; import java.security.GeneralSecurityException; import java.security.PrivateKey; +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageId; + public interface MessageEncoder { /** Encodes a private message. */ diff --git a/api/net/sf/briar/api/protocol/writers/TransportWriter.java b/api/net/sf/briar/api/protocol/writers/TransportWriter.java index 2f19ad31d9b5b35411202ed4e441fad0fd065f64..fb5a88bffe6f74fd77a3ba19908995eac1e11e7d 100644 --- a/api/net/sf/briar/api/protocol/writers/TransportWriter.java +++ b/api/net/sf/briar/api/protocol/writers/TransportWriter.java @@ -1,15 +1,14 @@ package net.sf.briar.api.protocol.writers; import java.io.IOException; -import java.util.Map; +import java.util.Collection; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.protocol.Transport; /** An interface for creating a transport update. */ public interface TransportWriter { /** Writes the contents of the update. */ - void writeTransports(Map<TransportId, TransportProperties> transports, - long timestamp) throws IOException; + void writeTransports(Collection<Transport> transports, long timestamp) + throws IOException; } diff --git a/api/net/sf/briar/api/serial/Writable.java b/api/net/sf/briar/api/serial/Writable.java deleted file mode 100644 index 7e75aa54351d4dfbed19c8813c8ac417b83784eb..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/serial/Writable.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.sf.briar.api.serial; - -import java.io.IOException; - -public interface Writable { - - void writeTo(Writer w) throws IOException; -} diff --git a/api/net/sf/briar/api/transport/BatchConnectionFactory.java b/api/net/sf/briar/api/transport/BatchConnectionFactory.java index 622a02d7722814b9a4d22f30988b6a99f8a2994c..20fb14e60bac0e0b2778eecad776a1c28bf1fb2d 100644 --- a/api/net/sf/briar/api/transport/BatchConnectionFactory.java +++ b/api/net/sf/briar/api/transport/BatchConnectionFactory.java @@ -1,13 +1,13 @@ package net.sf.briar.api.transport; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; public interface BatchConnectionFactory { - void createIncomingConnection(TransportId t, ContactId c, + void createIncomingConnection(TransportIndex i, ContactId c, BatchTransportReader r, byte[] encryptedIv); - void createOutgoingConnection(TransportId t, ContactId c, + void createOutgoingConnection(TransportIndex i, ContactId c, BatchTransportWriter w); } diff --git a/api/net/sf/briar/api/transport/ConnectionContext.java b/api/net/sf/briar/api/transport/ConnectionContext.java new file mode 100644 index 0000000000000000000000000000000000000000..692d69b65c16fb362641b33bc4a5d228406ebe01 --- /dev/null +++ b/api/net/sf/briar/api/transport/ConnectionContext.java @@ -0,0 +1,16 @@ +package net.sf.briar.api.transport; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; + +public interface ConnectionContext { + + ContactId getContactId(); + + TransportId getTransportId(); + + TransportIndex getTransportIndex(); + + long getConnectionNumber(); +} diff --git a/api/net/sf/briar/api/transport/ConnectionDispatcher.java b/api/net/sf/briar/api/transport/ConnectionDispatcher.java index 9d21d65bf951a4b8fa1095f769c20e4c09fa6368..6207063d5a18849eabdad4367231edb5a920e411 100644 --- a/api/net/sf/briar/api/transport/ConnectionDispatcher.java +++ b/api/net/sf/briar/api/transport/ConnectionDispatcher.java @@ -1,17 +1,17 @@ package net.sf.briar.api.transport; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; public interface ConnectionDispatcher { void dispatchReader(TransportId t, BatchTransportReader r); - void dispatchWriter(TransportId t, ContactId c, - BatchTransportWriter w); + void dispatchWriter(TransportIndex i, ContactId c, BatchTransportWriter w); void dispatchIncomingConnection(TransportId t, StreamTransportConnection s); - void dispatchOutgoingConnection(TransportId t, ContactId c, + void dispatchOutgoingConnection(TransportIndex i, ContactId c, StreamTransportConnection s); } diff --git a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java index fdb7ef91d88bdc1b724e4a1565c27957ff88cf9e..5d7c9142c76a2727a146783a03f127c38c228866 100644 --- a/api/net/sf/briar/api/transport/ConnectionReaderFactory.java +++ b/api/net/sf/briar/api/transport/ConnectionReaderFactory.java @@ -2,7 +2,7 @@ package net.sf.briar.api.transport; import java.io.InputStream; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; public interface ConnectionReaderFactory { @@ -10,13 +10,13 @@ public interface ConnectionReaderFactory { * Creates a connection reader for a batch-mode connection or the * initiator's side of a stream-mode connection. */ - ConnectionReader createConnectionReader(InputStream in, TransportId t, + ConnectionReader createConnectionReader(InputStream in, TransportIndex i, byte[] encryptedIv, byte[] secret); /** * Creates a connection reader for the responder's side of a stream-mode * connection. */ - ConnectionReader createConnectionReader(InputStream in, TransportId t, + ConnectionReader createConnectionReader(InputStream in, TransportIndex i, long connection, byte[] secret); } diff --git a/api/net/sf/briar/api/transport/ConnectionRecogniser.java b/api/net/sf/briar/api/transport/ConnectionRecogniser.java index b57af58d4beda85ddfb54fc41058e5a32f4484e5..7336f002dfc9ec8696c71bdc2f66cc4e03cb3694 100644 --- a/api/net/sf/briar/api/transport/ConnectionRecogniser.java +++ b/api/net/sf/briar/api/transport/ConnectionRecogniser.java @@ -1,18 +1,16 @@ package net.sf.briar.api.transport; -import net.sf.briar.api.ContactId; import net.sf.briar.api.db.DbException; /** - * Maintains a transport plugin's connection reordering window and decides - * whether incoming connections should be accepted or rejected. + * Maintains the connection reordering windows and decides whether incoming + * connections should be accepted or rejected. */ public interface ConnectionRecogniser { /** - * Returns the ID of the contact who created the encrypted IV if the - * connection should be accepted, or null if the connection should be - * rejected. + * Returns the connection's context if the connection should be accepted, + * or null if the connection should be rejected. */ - ContactId acceptConnection(byte[] encryptedIv) throws DbException; + ConnectionContext acceptConnection(byte[] encryptedIv) throws DbException; } diff --git a/api/net/sf/briar/api/transport/ConnectionRecogniserFactory.java b/api/net/sf/briar/api/transport/ConnectionRecogniserFactory.java deleted file mode 100644 index 688b453dbf9462de8e1edb70ebe7288ed623e03a..0000000000000000000000000000000000000000 --- a/api/net/sf/briar/api/transport/ConnectionRecogniserFactory.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.sf.briar.api.transport; - -import net.sf.briar.api.TransportId; - -public interface ConnectionRecogniserFactory { - - ConnectionRecogniser createConnectionRecogniser(TransportId t); -} diff --git a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java index 07064512222631314835c51d4e4e96c301ebb64b..63a13a3619f372e3a00a4a19278eb64931beea5a 100644 --- a/api/net/sf/briar/api/transport/ConnectionWriterFactory.java +++ b/api/net/sf/briar/api/transport/ConnectionWriterFactory.java @@ -2,7 +2,7 @@ package net.sf.briar.api.transport; import java.io.OutputStream; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; public interface ConnectionWriterFactory { @@ -11,12 +11,12 @@ public interface ConnectionWriterFactory { * initiator's side of a stream-mode connection. */ ConnectionWriter createConnectionWriter(OutputStream out, long capacity, - TransportId t, long connection, byte[] secret); + TransportIndex i, long connection, byte[] secret); /** * Creates a connection writer for the responder's side of a stream-mode * connection. */ ConnectionWriter createConnectionWriter(OutputStream out, long capacity, - TransportId t, byte[] encryptedIv, byte[] secret); + TransportIndex i, byte[] encryptedIv, byte[] secret); } diff --git a/api/net/sf/briar/api/transport/StreamConnectionFactory.java b/api/net/sf/briar/api/transport/StreamConnectionFactory.java index ee29406c5906e4e5dd012ebbe1f7ca1a10ac4bfa..621a072518b594e40a79e0653e84c4efcded2aef 100644 --- a/api/net/sf/briar/api/transport/StreamConnectionFactory.java +++ b/api/net/sf/briar/api/transport/StreamConnectionFactory.java @@ -1,13 +1,13 @@ package net.sf.briar.api.transport; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; public interface StreamConnectionFactory { - void createIncomingConnection(TransportId t, ContactId c, + void createIncomingConnection(TransportIndex i, ContactId c, StreamTransportConnection s, byte[] encryptedIv); - void createOutgoingConnection(TransportId t, ContactId c, + void createOutgoingConnection(TransportIndex i, ContactId c, StreamTransportConnection s); } diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java index e8c9d05bd8e5874e260a6c40721dc4c5c5f8ddf3..39eb9889af59403a7dee2bec971e57215acfa546 100644 --- a/components/net/sf/briar/db/Database.java +++ b/components/net/sf/briar/db/Database.java @@ -7,7 +7,6 @@ import java.util.Map; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; @@ -18,6 +17,9 @@ 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.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionWindow; /** @@ -78,14 +80,12 @@ interface Database<T> { void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException; /** - * Adds a new contact to the database with the given transport properties - * and secret, and returns an ID for the contact. + * Adds a new contact to the database with the given secret and returns an + * ID for the contact. * <p> - * Locking: contact write, transport write. + * Locking: contact write. */ - ContactId addContact(T txn, - Map<TransportId, TransportProperties> transports, byte[] secret) - throws DbException; + ContactId addContact(T txn, byte[] secret) throws DbException; /** * Returns false if the given message is already in the database. Otherwise @@ -118,6 +118,14 @@ interface Database<T> { */ void addSubscription(T txn, Group g) throws DbException; + /** + * Allocates and returns a local index for the given transport. Returns + * null if all indices have been allocated. + * <p> + * Locking: transport write. + */ + TransportIndex addTransport(T txn, TransportId t) throws DbException; + /** * Returns true if the database contains the given contact. * <p> @@ -179,7 +187,7 @@ interface Database<T> { * <p> * Locking: contact read, window write. */ - long getConnectionNumber(T txn, ContactId c, TransportId t) + long getConnectionNumber(T txn, ContactId c, TransportIndex i) throws DbException; /** @@ -188,7 +196,7 @@ interface Database<T> { * <p> * Locking: contact read, window read. */ - ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportId t) + ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportIndex i) throws DbException; /** @@ -216,6 +224,14 @@ interface Database<T> { */ MessageId getGroupMessageParent(T txn, MessageId m) throws DbException; + /** + * Returns the local index for the given transport, or null if no index + * has been allocated. + * <p> + * Locking: transport read. + */ + TransportIndex getLocalIndex(T txn, TransportId t) throws DbException; + /** * Returns the local transport properties for the given transport. * <p> @@ -225,12 +241,11 @@ interface Database<T> { throws DbException; /** - * Returns all local transport properties. + * Returns all local transports. * <p> * Locking: transport read. */ - Map<TransportId, TransportProperties> getLocalTransports(T txn) - throws DbException; + Collection<Transport> getLocalTransports(T txn) throws DbException; /** * Returns the IDs of any batches sent to the given contact that should now @@ -312,6 +327,15 @@ interface Database<T> { */ boolean getRead(T txn, MessageId m) throws DbException; + /** + * Returns the given contact's index for the given transport, or null if + * the contact does not support the transport. + * <p> + * Locking: contact read, window read. + */ + TransportIndex getRemoteIndex(T txn, ContactId c, TransportId t) + throws DbException; + /** * Returns all remote properties for the given transport. * <p> @@ -456,7 +480,7 @@ interface Database<T> { * Removes a contact (and all associated state) from the database. * <p> * Locking: contact write, message write, messageFlag write, - * messageStatus write, subscription write, transport write. + * messageStatus write, subscription write, transport write, window write. */ void removeContact(T txn, ContactId c) throws DbException; @@ -501,7 +525,7 @@ interface Database<T> { * <p> * Locking: contact read, window write. */ - void setConnectionWindow(T txn, ContactId c, TransportId t, + void setConnectionWindow(T txn, ContactId c, TransportIndex i, ConnectionWindow w) throws DbException; /** @@ -591,15 +615,13 @@ interface Database<T> { throws DbException; /** - * Sets the transport properties for the given contact, replacing any - * existing properties unless the existing properties have a newer - * timestamp. + * 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, - Map<TransportId, TransportProperties> transports, long timestamp) - throws DbException; + void setTransports(T txn, ContactId c, Collection<Transport> transports, + long timestamp) throws DbException; /** * Records the time at which the local transports were last modified. diff --git a/components/net/sf/briar/db/DatabaseComponentImpl.java b/components/net/sf/briar/db/DatabaseComponentImpl.java index 2457bd98bbd6051b72ca447b3cf46d51a0f8c256..23b3ae4e14a89bbfdbca4f26307fe2b460339219 100644 --- a/components/net/sf/briar/db/DatabaseComponentImpl.java +++ b/components/net/sf/briar/db/DatabaseComponentImpl.java @@ -23,7 +23,6 @@ import net.sf.briar.api.Bytes; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; @@ -35,10 +34,12 @@ 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.MessagesAddedEvent; 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.db.event.TransportAddedEvent; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.Batch; @@ -49,6 +50,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.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; @@ -131,27 +135,19 @@ DatabaseCleaner.Callback { } } - public ContactId addContact( - Map<TransportId, TransportProperties> transports, byte[] secret) - throws DbException { + public ContactId addContact(byte[] secret) throws DbException { if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact"); ContactId c; contactLock.writeLock().lock(); try { - transportLock.writeLock().lock(); + T txn = db.startTransaction(); try { - T txn = db.startTransaction(); - try { - c = db.addContact(txn, transports, secret); - db.commitTransaction(txn); - if(LOG.isLoggable(Level.FINE)) - LOG.fine("Added contact " + c); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; - } - } finally { - transportLock.writeLock().unlock(); + c = db.addContact(txn, secret); + db.commitTransaction(txn); + if(LOG.isLoggable(Level.FINE)) LOG.fine("Added contact " + c); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; } } finally { contactLock.writeLock().unlock(); @@ -370,6 +366,26 @@ DatabaseCleaner.Callback { } } + public TransportIndex addTransport(TransportId t) throws DbException { + TransportIndex i; + transportLock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + i = db.addTransport(txn, t); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.writeLock().unlock(); + } + // Call the listeners outside the lock + if(i != null) callListeners(new TransportAddedEvent(t)); + return i; + } + public boolean generateAck(ContactId c, AckWriter a) throws DbException, IOException { contactLock.readLock().lock(); @@ -630,7 +646,7 @@ DatabaseCleaner.Callback { public void generateTransportUpdate(ContactId c, TransportWriter t) throws DbException, IOException { - Map<TransportId, TransportProperties> transports = null; + Collection<Transport> transports = null; long timestamp = 0L; contactLock.readLock().lock(); try { @@ -683,7 +699,7 @@ DatabaseCleaner.Callback { } } - public long getConnectionNumber(ContactId c, TransportId t) + public long getConnectionNumber(ContactId c, TransportIndex i) throws DbException { contactLock.readLock().lock(); try { @@ -692,7 +708,7 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - long outgoing = db.getConnectionNumber(txn, c, t); + long outgoing = db.getConnectionNumber(txn, c, i); db.commitTransaction(txn); return outgoing; } catch(DbException e) { @@ -707,7 +723,7 @@ DatabaseCleaner.Callback { } } - public ConnectionWindow getConnectionWindow(ContactId c, TransportId t) + public ConnectionWindow getConnectionWindow(ContactId c, TransportIndex i) throws DbException { contactLock.readLock().lock(); try { @@ -716,7 +732,7 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - ConnectionWindow w = db.getConnectionWindow(txn, c, t); + ConnectionWindow w = db.getConnectionWindow(txn, c, i); db.commitTransaction(txn); return w; } catch(DbException e) { @@ -748,6 +764,23 @@ DatabaseCleaner.Callback { } } + public TransportIndex getLocalIndex(TransportId t) throws DbException { + transportLock.readLock().lock(); + try { + T txn = db.startTransaction(); + try { + TransportIndex i = db.getLocalIndex(txn, t); + db.commitTransaction(txn); + return i; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.readLock().unlock(); + } + } + public TransportProperties getLocalProperties(TransportId t) throws DbException { transportLock.readLock().lock(); @@ -766,14 +799,12 @@ DatabaseCleaner.Callback { } } - public Map<TransportId, TransportProperties> getLocalTransports() - throws DbException { + public Collection<Transport> getLocalTransports() throws DbException { transportLock.readLock().lock(); try { T txn = db.startTransaction(); try { - Map<TransportId, TransportProperties> transports = - db.getLocalTransports(txn); + Collection<Transport> transports = db.getLocalTransports(txn); db.commitTransaction(txn); return transports; } catch(DbException e) { @@ -826,6 +857,30 @@ DatabaseCleaner.Callback { } } + public TransportIndex getRemoteIndex(ContactId c, TransportId t) + throws DbException { + contactLock.readLock().lock(); + try { + if(!containsContact(c)) throw new NoSuchContactException(); + transportLock.readLock().lock(); + try { + T txn = db.startTransaction(); + try { + TransportIndex i = db.getRemoteIndex(txn, c, t); + db.commitTransaction(txn); + return i; + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + transportLock.readLock().unlock(); + } + } finally { + contactLock.readLock().unlock(); + } + } + public Map<ContactId, TransportProperties> getRemoteProperties( TransportId t) throws DbException { contactLock.readLock().lock(); @@ -1135,6 +1190,8 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } + // Call the listeners outside the lock + callListeners(new SubscriptionsUpdatedEvent(Collections.singleton(c))); } public void receiveTransportUpdate(ContactId c, TransportUpdate t) @@ -1147,8 +1204,7 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - Map<TransportId, TransportProperties> transports = - t.getTransports(); + Collection<Transport> transports = t.getTransports(); db.setTransports(txn, c, transports, t.getTimestamp()); if(LOG.isLoggable(Level.FINE)) LOG.fine("Received " + transports.size() @@ -1164,6 +1220,8 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } + // Call the listeners outside the lock + callListeners(new RemoteTransportsUpdatedEvent(c)); } public void removeContact(ContactId c) throws DbException { @@ -1180,13 +1238,18 @@ DatabaseCleaner.Callback { try { transportLock.writeLock().lock(); try { - T txn = db.startTransaction(); + windowLock.writeLock().lock(); try { - db.removeContact(txn, c); - db.commitTransaction(txn); - } catch(DbException e) { - db.abortTransaction(txn); - throw e; + T txn = db.startTransaction(); + try { + db.removeContact(txn, c); + db.commitTransaction(txn); + } catch(DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + windowLock.writeLock().unlock(); } } finally { transportLock.writeLock().unlock(); @@ -1227,7 +1290,7 @@ DatabaseCleaner.Callback { } } - public void setConnectionWindow(ContactId c, TransportId t, + public void setConnectionWindow(ContactId c, TransportIndex i, ConnectionWindow w) throws DbException { contactLock.readLock().lock(); try { @@ -1236,7 +1299,7 @@ DatabaseCleaner.Callback { try { T txn = db.startTransaction(); try { - db.setConnectionWindow(txn, c, t, w); + db.setConnectionWindow(txn, c, i, w); db.commitTransaction(txn); } catch(DbException e) { db.abortTransaction(txn); @@ -1270,7 +1333,7 @@ DatabaseCleaner.Callback { transportLock.writeLock().unlock(); } // Call the listeners outside the lock - if(changed) callListeners(new TransportsUpdatedEvent()); + if(changed) callListeners(new LocalTransportsUpdatedEvent()); } public void setRating(AuthorId a, Rating r) throws DbException { @@ -1430,6 +1493,7 @@ DatabaseCleaner.Callback { } finally { subscriptionLock.writeLock().unlock(); } + // Listeners will be notified when the group's visibility is set } public void unsubscribe(GroupId g) throws DbException { diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java index 16e691135118dc18378a28f2096064d51cfd60c3..40d519045ff42e6b59e49c2cf53eefe52ade52cc 100644 --- a/components/net/sf/briar/db/JdbcDatabase.java +++ b/components/net/sf/briar/db/JdbcDatabase.java @@ -20,7 +20,6 @@ import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.MessageHeader; @@ -32,6 +31,10 @@ 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.MessageId; +import net.sf.briar.api.protocol.ProtocolConstants; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.util.FileUtils; @@ -169,38 +172,56 @@ abstract class JdbcDatabase implements Database<Connection> { private static final String INDEX_STATUSES_BY_CONTACT = "CREATE INDEX statusesByContact ON statuses (contactId)"; - private static final String CREATE_CONTACT_TRANSPORTS = - "CREATE TABLE contactTransports" - + " (contactId INT NOT NULL," - + " transportId INT NOT NULL," - + " key VARCHAR NOT NULL," - + " value VARCHAR NOT NULL," - + " PRIMARY KEY (contactId, transportId, key)," - + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" - + " ON DELETE CASCADE)"; - private static final String CREATE_TRANSPORTS = "CREATE TABLE transports" - + " (transportId INT NOT NULL," + + " (transportId HASH NOT NULL," + + " index COUNTER," + + " UNIQUE(transportId)," + + " PRIMARY KEY (transportId, index))"; + + 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))"; - private static final String CREATE_TRANSPORT_CONFIG = - "CREATE TABLE transportConfig" - + " (transportId INT NOT NULL," + 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))"; + private static final String CREATE_CONTACT_TRANSPORTS = + "CREATE TABLE contactTransports" + + " (contactId INT NOT NULL," + + " transportId HASH NOT NULL," + + " index INT NOT NULL," + + " UNIQUE (contactId, transportId)," + + " UNIQUE (contactId, index)," + + " PRIMARY KEY (contactId, transportId, index)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + + private static final String CREATE_CONTACT_TRANSPORT_PROPS = + "CREATE TABLE contactTransportProperties" + + " (contactId INT NOT NULL," + + " transportId HASH NOT NULL," + + " key VARCHAR NOT NULL," + + " value VARCHAR NOT NULL," + + " PRIMARY KEY (contactId, transportId, key)," + + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + private static final String CREATE_CONNECTION_WINDOWS = "CREATE TABLE connectionWindows" + " (contactId INT NOT NULL," - + " transportId INT NOT NULL," + + " index INT NOT NULL," + " centre BIGINT NOT NULL," + " bitmap INT NOT NULL," + " outgoing BIGINT NOT NULL," - + " PRIMARY KEY (contactId, transportId)," + + " PRIMARY KEY (contactId, index)," + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; @@ -316,9 +337,11 @@ abstract class JdbcDatabase implements Database<Connection> { s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_CONTACT); - s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); - s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIG)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS)); + s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_PROPS)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS)); + s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORT_PROPS)); s.executeUpdate(insertTypeNames(CREATE_CONNECTION_WINDOWS)); s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS)); @@ -478,8 +501,7 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public ContactId addContact(Connection txn, - Map<TransportId, TransportProperties> transports, byte[] secret) + public ContactId addContact(Connection txn, byte[] secret) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -502,29 +524,6 @@ abstract class JdbcDatabase implements Database<Connection> { if(rs.next()) throw new DbStateException(); rs.close(); ps.close(); - // Store the contact's transport properties - sql = "INSERT INTO contactTransports" - + " (contactId, transportId, key, value)" - + " VALUES (?, ?, ?, ?)"; - ps = txn.prepareStatement(sql); - ps.setInt(1, c.getInt()); - int batchSize = 0; - for(Entry<TransportId, TransportProperties> e - : transports.entrySet()) { - ps.setInt(2, e.getKey().getInt()); - for(Entry<String, String> e1 : e.getValue().entrySet()) { - ps.setString(3, e1.getKey()); - ps.setString(4, e1.getValue()); - ps.addBatch(); - batchSize++; - } - } - 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(); - } - ps.close(); // Initialise the subscription timestamps sql = "INSERT INTO subscriptionTimestamps" + " (contactId, sent, received, modified)" @@ -693,6 +692,44 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public TransportIndex addTransport(Connection txn, TransportId t) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + // Allocate a new index + 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(); + // If the new index is in range, return it + sql = "SELECT index FROM transports WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + rs = ps.executeQuery(); + if(!rs.next()) throw new DbStateException(); + int i = rs.getInt(1); + if(rs.next()) throw new DbStateException(); + rs.close(); + ps.close(); + if(i < ProtocolConstants.MAX_TRANSPORTS) + return new TransportIndex(i); + // Too many transports - delete the new index and return null + sql = "DELETE FROM transports WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + affected = ps.executeUpdate(); + if(affected != 1) throw new DbStateException(); + return null; + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public boolean containsContact(Connection txn, ContactId c) throws DbException { PreparedStatement ps = null; @@ -836,10 +873,10 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT key, value FROM transportConfig" + String sql = "SELECT key, value FROM transportConfigs" + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); TransportConfig c = new TransportConfig(); while(rs.next()) c.put(rs.getString(1), rs.getString(2)); @@ -854,15 +891,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public long getConnectionNumber(Connection txn, ContactId c, - TransportId t) throws DbException { + TransportIndex i) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT outgoing FROM connectionWindows" - + " WHERE contactId = ? AND transportId = ?"; + + " WHERE contactId = ? AND index = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, t.getInt()); + ps.setInt(2, i.getInt()); rs = ps.executeQuery(); if(rs.next()) { // A connection window row exists - update it @@ -871,11 +908,11 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "UPDATE connectionWindows SET outgoing = ?" - + " WHERE contactId = ? AND transportId = ?"; + + " WHERE contactId = ? AND index = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, outgoing + 1); ps.setInt(2, c.getInt()); - ps.setInt(3, t.getInt()); + ps.setInt(3, i.getInt()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -885,11 +922,11 @@ abstract class JdbcDatabase implements Database<Connection> { rs.close(); ps.close(); sql = "INSERT INTO connectionWindows" - + " (contactId, transportId, centre, bitmap, outgoing)" + + " (contactId, index, centre, bitmap, outgoing)" + " VALUES(?, ?, ZERO(), ZERO(), ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, t.getInt()); + ps.setInt(2, i.getInt()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -903,15 +940,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public ConnectionWindow getConnectionWindow(Connection txn, ContactId c, - TransportId t) throws DbException { + TransportIndex i) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT centre, bitmap FROM connectionWindows" - + " WHERE contactId = ? AND transportId = ?"; + + " WHERE contactId = ? AND index = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, t.getInt()); + ps.setInt(2, i.getInt()); rs = ps.executeQuery(); long centre = 0L; int bitmap = 0; @@ -987,15 +1024,39 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public TransportIndex getLocalIndex(Connection txn, TransportId t) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT index FROM transports WHERE transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, t.getBytes()); + rs = ps.executeQuery(); + TransportIndex index = null; + if(rs.next()) { + index = new TransportIndex(rs.getInt(1)); + if(rs.next()) throw new DbStateException(); + } + rs.close(); + ps.close(); + return index; + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public TransportProperties getLocalProperties(Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT key, value FROM transports" + String sql = "SELECT key, value FROM transportProperties" + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); TransportProperties p = new TransportProperties(); while(rs.next()) p.put(rs.getString(1), rs.getString(2)); @@ -1009,26 +1070,31 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Map<TransportId, TransportProperties> getLocalTransports( - Connection txn) throws DbException { + public Collection<Transport> getLocalTransports(Connection txn) + throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT transportId, key, value FROM transports" - + " ORDER BY transportId"; + String sql = "SELECT transports.transportId, index, key, value" + + " FROM transports LEFT OUTER JOIN transportProperties" + + " ON transports.transportId" + + " = transportProperties.transportId" + + " ORDER BY transports.transportId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); - Map<TransportId, TransportProperties> transports = - new HashMap<TransportId, TransportProperties>(); - TransportProperties p = null; + Collection<Transport> transports = new ArrayList<Transport>(); TransportId lastId = null; + Transport t = null; while(rs.next()) { - TransportId id = new TransportId(rs.getInt(1)); + TransportId id = new TransportId(rs.getBytes(1)); if(!id.equals(lastId)) { - p = new TransportProperties(); - transports.put(id, p); + t = new Transport(id, new TransportIndex(rs.getInt(2))); + transports.add(t); } - p.put(rs.getString(2), rs.getString(3)); + // Key and value may be null due to the left outer join + String key = rs.getString(3); + String value = rs.getString(4); + if(key != null && value != null) t.put(key, value); } rs.close(); ps.close(); @@ -1357,21 +1423,48 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public TransportIndex getRemoteIndex(Connection txn, ContactId c, + TransportId t) throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT index FROM contactTransports" + + " WHERE contactId = ? AND transportId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.setBytes(2, t.getBytes()); + rs = ps.executeQuery(); + TransportIndex index = null; + if(rs.next()) { + index = new TransportIndex(rs.getInt(1)); + if(rs.next()) throw new DbStateException(); + } + rs.close(); + ps.close(); + return index; + } catch(SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + public Map<ContactId, TransportProperties> getRemoteProperties( Connection txn, TransportId t) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT contactId, key, value FROM contactTransports" + String sql = "SELECT contactId, key, value" + + " FROM contactTransportProperties" + " WHERE transportId = ?" + " ORDER BY contactId"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); rs = ps.executeQuery(); Map<ContactId, TransportProperties> properties = new HashMap<ContactId, TransportProperties>(); - TransportProperties p = null; ContactId lastId = null; + TransportProperties p = null; while(rs.next()) { ContactId id = new ContactId(rs.getInt(1)); if(!id.equals(lastId)) { @@ -2034,16 +2127,16 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { // Delete any existing config for the given transport - String sql = "DELETE FROM transportConfig WHERE transportId = ?"; + String sql = "DELETE FROM transportConfigs WHERE transportId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); ps.executeUpdate(); ps.close(); // Store the new config - sql = "INSERT INTO transportConfig (transportId, key, value)" + sql = "INSERT INTO transportConfigs (transportId, key, value)" + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); for(Entry<String, String> e : c.entrySet()) { ps.setString(2, e.getKey()); ps.setString(3, e.getValue()); @@ -2063,15 +2156,15 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setConnectionWindow(Connection txn, ContactId c, - TransportId t, ConnectionWindow w) throws DbException { + TransportIndex i, ConnectionWindow w) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { String sql = "SELECT NULL FROM connectionWindows" - + " WHERE contactId = ? AND transportId = ?"; + + " WHERE contactId = ? AND index = ?"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, t.getInt()); + ps.setInt(2, i.getInt()); rs = ps.executeQuery(); boolean found = rs.next(); if(rs.next()) throw new DbStateException(); @@ -2080,23 +2173,23 @@ abstract class JdbcDatabase implements Database<Connection> { if(found) { // A connection window row exists - update it sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?" - + " WHERE contactId = ? AND transportId = ?"; + + " WHERE contactId = ? AND index = ?"; ps = txn.prepareStatement(sql); ps.setLong(1, w.getCentre()); ps.setInt(2, w.getBitmap()); ps.setInt(3, c.getInt()); - ps.setInt(4, t.getInt()); + ps.setInt(4, i.getInt()); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); } else { // No connection window row exists - create one sql = "INSERT INTO connectionWindows" - + " (contactId, transportId, centre, bitmap, outgoing)" + + " (contactId, index, centre, bitmap, outgoing)" + " VALUES(?, ?, ?, ?, ZERO())"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); - ps.setInt(2, t.getInt()); + ps.setInt(2, i.getInt()); ps.setLong(3, w.getCentre()); ps.setInt(4, w.getBitmap()); int affected = ps.executeUpdate(); @@ -2115,16 +2208,17 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; try { // Delete any existing properties for the given transport - String sql = "DELETE FROM transports WHERE transportId = ?"; + String sql = "DELETE FROM transportProperties" + + " WHERE transportId = ?"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); ps.executeUpdate(); ps.close(); // Store the new properties - sql = "INSERT INTO transports (transportId, key, value)" + sql = "INSERT INTO transportProperties (transportId, key, value)" + " VALUES (?, ?, ?)"; ps = txn.prepareStatement(sql); - ps.setInt(1, t.getInt()); + ps.setBytes(1, t.getBytes()); for(Entry<String, String> e : p.entrySet()) { ps.setString(2, e.getKey()); ps.setString(3, e.getValue()); @@ -2504,7 +2598,7 @@ abstract class JdbcDatabase implements Database<Connection> { } public void setTransports(Connection txn, ContactId c, - Map<TransportId, TransportProperties> transports, long timestamp) + Collection<Transport> transports, long timestamp) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -2527,24 +2621,45 @@ abstract class JdbcDatabase implements Database<Connection> { ps.setInt(1, c.getInt()); ps.executeUpdate(); ps.close(); + // Delete any existing transport properties + sql = "DELETE FROM contactTransportProperties WHERE contactId = ?"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + ps.executeUpdate(); + ps.close(); // Store the new transports sql = "INSERT INTO contactTransports" - + " (contactId, transportId, key, value)" - + " VALUES (?, ?, ?, ?)"; + + " (contactId, transportId, index) VALUES (?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, c.getInt()); + for(Transport t : transports) { + ps.setBytes(2, t.getId().getBytes()); + ps.setInt(3, t.getIndex().getInt()); + ps.addBatch(); + } + int[] batchAffected = ps.executeBatch(); + if(batchAffected.length != transports.size()) + throw new DbStateException(); + for(int i = 0; i < batchAffected.length; i++) { + if(batchAffected[i] != 1) throw new DbStateException(); + } + ps.close(); + // Store the new transport properties + sql = "INSERT INTO contactTransportProperties" + + " (contactId, transportId, key, value) VALUES (?, ?, ?, ?)"; ps = txn.prepareStatement(sql); ps.setInt(1, c.getInt()); int batchSize = 0; - for(Entry<TransportId, TransportProperties> e - : transports.entrySet()) { - ps.setInt(2, e.getKey().getInt()); - for(Entry<String, String> e1 : e.getValue().entrySet()) { + for(Transport t : transports) { + ps.setBytes(2, t.getId().getBytes()); + for(Entry<String, String> e1 : t.entrySet()) { ps.setString(3, e1.getKey()); ps.setString(4, e1.getValue()); ps.addBatch(); batchSize++; } } - int[] batchAffected = ps.executeBatch(); + 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(); diff --git a/components/net/sf/briar/invitation/InvitationWorker.java b/components/net/sf/briar/invitation/InvitationWorker.java index ddebf3574913cdb75bb7b03252ca2d34e8c0c533..8f2cd77f2befecf092a70e0319946f84dee613b3 100644 --- a/components/net/sf/briar/invitation/InvitationWorker.java +++ b/components/net/sf/briar/invitation/InvitationWorker.java @@ -5,15 +5,14 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; -import java.util.Map; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.invitation.InvitationCallback; import net.sf.briar.api.invitation.InvitationParameters; +import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.util.FileUtils; @@ -72,7 +71,7 @@ class InvitationWorker implements Runnable { File invitationDat = new File(dir, "invitation.dat"); callback.encryptingFile(invitationDat); // FIXME: Create a real invitation - Map<TransportId, TransportProperties> transports; + Collection<Transport> transports; try { transports = db.getLocalTransports(); } catch(DbException e) { @@ -80,7 +79,7 @@ class InvitationWorker implements Runnable { } FileOutputStream out = new FileOutputStream(invitationDat); Writer w = writerFactory.createWriter(out); - w.writeMap(transports); + w.writeList(transports); out.flush(); out.close(); return invitationDat; diff --git a/components/net/sf/briar/plugins/PluginManagerImpl.java b/components/net/sf/briar/plugins/PluginManagerImpl.java index beb11d06e6ad177b7e3c7f8159ce399739dfd591..0d65447654c940a9f3329e9519b1d6eab3c6b63c 100644 --- a/components/net/sf/briar/plugins/PluginManagerImpl.java +++ b/components/net/sf/briar/plugins/PluginManagerImpl.java @@ -8,12 +8,12 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; @@ -26,7 +26,9 @@ import net.sf.briar.api.plugins.PluginManager; import net.sf.briar.api.plugins.StreamPlugin; import net.sf.briar.api.plugins.StreamPluginCallback; import net.sf.briar.api.plugins.StreamPluginFactory; -import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.ProtocolConstants; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportWriter; import net.sf.briar.api.transport.ConnectionDispatcher; @@ -49,26 +51,32 @@ class PluginManagerImpl implements PluginManager { "net.sf.briar.plugins.socket.SimpleSocketPluginFactory" }; - private final Executor executor; + private static final int THREAD_POOL_SIZE = 5; + private final DatabaseComponent db; private final Poller poller; private final ConnectionDispatcher dispatcher; private final UiCallback uiCallback; + private final Executor executor; private final List<BatchPlugin> batchPlugins; private final List<StreamPlugin> streamPlugins; @Inject - PluginManagerImpl(Executor executor, DatabaseComponent db, Poller poller, + PluginManagerImpl(DatabaseComponent db, Poller poller, ConnectionDispatcher dispatcher, UiCallback uiCallback) { - this.executor = executor; this.db = db; this.poller = poller; this.dispatcher = dispatcher; this.uiCallback = uiCallback; + executor = new ScheduledThreadPoolExecutor(THREAD_POOL_SIZE); batchPlugins = new ArrayList<BatchPlugin>(); streamPlugins = new ArrayList<StreamPlugin>(); } + public synchronized int getPluginCount() { + return batchPlugins.size() + streamPlugins.size(); + } + public synchronized int startPlugins() { Set<TransportId> ids = new HashSet<TransportId>(); // Instantiate and start the batch plugins @@ -81,8 +89,8 @@ class PluginManagerImpl implements PluginManager { BatchPlugin plugin = factory.createPlugin(executor, callback); if(plugin == null) { if(LOG.isLoggable(Level.INFO)) - LOG.info(factory.getClass().getSimpleName() + - " did not create a plugin"); + LOG.info(factory.getClass().getSimpleName() + + " did not create a plugin"); continue; } TransportId id = plugin.getId(); @@ -91,7 +99,14 @@ class PluginManagerImpl implements PluginManager { LOG.warning("Duplicate transport ID: " + id); continue; } - callback.setId(id); + TransportIndex index = db.getLocalIndex(id); + if(index == null) index = db.addTransport(id); + if(index == null) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Could not allocate index for ID: " + id); + continue; + } + callback.init(id, index); plugin.start(); batchPlugins.add(plugin); } catch(ClassCastException e) { @@ -122,7 +137,14 @@ class PluginManagerImpl implements PluginManager { LOG.warning("Duplicate transport ID: " + id); continue; } - callback.setId(id); + TransportIndex index = db.getLocalIndex(id); + if(index == null) index = db.addTransport(id); + if(index == null) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Could not allocate index for ID: " + id); + continue; + } + callback.init(id, index); plugin.start(); streamPlugins.add(plugin); } catch(ClassCastException e) { @@ -138,7 +160,7 @@ class PluginManagerImpl implements PluginManager { plugins.addAll(batchPlugins); plugins.addAll(streamPlugins); poller.startPolling(plugins); - // Return the number of plugins started + // Return the number of plugins successfully started return batchPlugins.size() + streamPlugins.size(); } @@ -164,17 +186,19 @@ class PluginManagerImpl implements PluginManager { } } streamPlugins.clear(); - // Return the number of plugins stopped + // Return the number of plugins successfully stopped return stopped; } private abstract class PluginCallbackImpl implements PluginCallback { protected volatile TransportId id = null; + protected volatile TransportIndex index = null; - protected void setId(TransportId id) { - assert this.id == null; + protected void init(TransportId id, TransportIndex index) { + assert this.id == null && this.index == null; this.id = id; + this.index = index; } public TransportConfig getConfig() { @@ -219,20 +243,20 @@ class PluginManagerImpl implements PluginManager { public void setLocalProperties(TransportProperties p) { assert id != null; - if(p.size() > TransportUpdate.MAX_PROPERTIES_PER_PLUGIN) { + if(p.size() > ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT) { if(LOG.isLoggable(Level.WARNING)) LOG.warning("Plugin " + id + " set too many properties"); return; } for(String s : p.keySet()) { - if(s.length() > TransportUpdate.MAX_KEY_OR_VALUE_LENGTH) { + if(s.length() > ProtocolConstants.MAX_PROPERTY_LENGTH) { if(LOG.isLoggable(Level.WARNING)) LOG.warning("Plugin " + id + " set long key: " + s); return; } } for(String s : p.values()) { - if(s.length() > TransportUpdate.MAX_KEY_OR_VALUE_LENGTH) { + if(s.length() > ProtocolConstants.MAX_PROPERTY_LENGTH) { if(LOG.isLoggable(Level.WARNING)) LOG.warning("Plugin " + id + " set long value: " + s); return; @@ -267,8 +291,8 @@ class PluginManagerImpl implements PluginManager { } public void writerCreated(ContactId c, BatchTransportWriter w) { - assert id != null; - dispatcher.dispatchWriter(id, c, w); + assert index != null; + dispatcher.dispatchWriter(index, c, w); } } @@ -282,8 +306,8 @@ class PluginManagerImpl implements PluginManager { public void outgoingConnectionCreated(ContactId c, StreamTransportConnection s) { - assert id != null; - dispatcher.dispatchOutgoingConnection(id, c, s); + assert index != null; + dispatcher.dispatchOutgoingConnection(index, c, s); } } } \ No newline at end of file diff --git a/components/net/sf/briar/plugins/PollerImpl.java b/components/net/sf/briar/plugins/PollerImpl.java index 87ee77c0d4fd85937e6c325621aade127c70f94b..708a56baee845e10654f4044cbd700f7573a1acc 100644 --- a/components/net/sf/briar/plugins/PollerImpl.java +++ b/components/net/sf/briar/plugins/PollerImpl.java @@ -73,8 +73,6 @@ class PollerImpl implements Poller, Runnable { public int compareTo(PollTime p) { if(time < p.time) return -1; if(time > p.time) return 1; - if(plugin.getId().getInt() < p.plugin.getId().getInt()) return -1; - if(plugin.getId().getInt() > p.plugin.getId().getInt()) return 1; return 0; } } diff --git a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java index 75d42231d14a157cc4299d5e688161ea05e3c579..ec0f7a585d5775886d5f36f8a730ce81a514dfb2 100644 --- a/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java +++ b/components/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java @@ -18,10 +18,10 @@ import javax.microedition.io.StreamConnection; import javax.microedition.io.StreamConnectionNotifier; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.plugins.StreamPlugin; import net.sf.briar.api.plugins.StreamPluginCallback; +import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.transport.StreamTransportConnection; import net.sf.briar.plugins.AbstractPlugin; import net.sf.briar.util.OsUtils; @@ -29,7 +29,9 @@ import net.sf.briar.util.StringUtils; class BluetoothPlugin extends AbstractPlugin implements StreamPlugin { - public static final int TRANSPORT_ID = 2; + public static final byte[] TRANSPORT_ID = + StringUtils.fromHexString("d99c9313c04417dcf22fc60d12a187ea" + + "00a539fd260f08a13a0d8a900cde5e49"); private static final TransportId id = new TransportId(TRANSPORT_ID); private static final Logger LOG = diff --git a/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java index 64a6763d43ff632e666c09bb5ee321f649bd72fa..af60b0ec87fe3a4ff0f3e98274dbfeb6a3a1c59a 100644 --- a/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java +++ b/components/net/sf/briar/plugins/file/RemovableDrivePlugin.java @@ -9,13 +9,16 @@ import java.util.concurrent.Executor; import java.util.logging.Level; import java.util.logging.Logger; -import net.sf.briar.api.TransportId; import net.sf.briar.api.plugins.BatchPluginCallback; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.util.StringUtils; class RemovableDrivePlugin extends FilePlugin implements RemovableDriveMonitor.Callback { - public static final int TRANSPORT_ID = 0; + public static final byte[] TRANSPORT_ID = + StringUtils.fromHexString("7c81bf5c9b1cd557685548c85f976bbd" + + "e633d2418ea2e230e5710fb43c6f8cc0"); private static final TransportId id = new TransportId(TRANSPORT_ID); private static final Logger LOG = diff --git a/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java b/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java index 84a273c56202f1377692e00de96a4819e57abf4b..cfe698b96e8905c7a2c096f0d0aa93c4d54d860b 100644 --- a/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java +++ b/components/net/sf/briar/plugins/socket/SimpleSocketPlugin.java @@ -14,14 +14,17 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.plugins.StreamPluginCallback; +import net.sf.briar.api.protocol.TransportId; import net.sf.briar.api.transport.StreamTransportConnection; +import net.sf.briar.util.StringUtils; class SimpleSocketPlugin extends SocketPlugin { - public static final int TRANSPORT_ID = 1; + public static final byte[] TRANSPORT_ID = + StringUtils.fromHexString("58c66d999e492b85065924acfd739d80" + + "c65a62f87e5a4fc6c284f95908b9007d"); private static final TransportId id = new TransportId(TRANSPORT_ID); private static final Logger LOG = diff --git a/components/net/sf/briar/plugins/socket/SocketPlugin.java b/components/net/sf/briar/plugins/socket/SocketPlugin.java index ae20ba4cca4aa93ae652b4dc26eaa8d089a0c6d9..279a48e06ff5aedcaab6a3c82027ae0dbe894729 100644 --- a/components/net/sf/briar/plugins/socket/SocketPlugin.java +++ b/components/net/sf/briar/plugins/socket/SocketPlugin.java @@ -32,8 +32,7 @@ abstract class SocketPlugin extends AbstractPlugin implements StreamPlugin { protected abstract SocketAddress getLocalSocketAddress(); protected abstract SocketAddress getRemoteSocketAddress(ContactId c); - protected SocketPlugin(Executor executor, - StreamPluginCallback callback) { + protected SocketPlugin(Executor executor, StreamPluginCallback callback) { super(executor); this.callback = callback; } diff --git a/components/net/sf/briar/protocol/AuthorFactoryImpl.java b/components/net/sf/briar/protocol/AuthorFactoryImpl.java index f617e5fc89e8a8246be134df0517c3e19ec76203..7356a0414bb239785259e951a2f0ea5c14b532c5 100644 --- a/components/net/sf/briar/protocol/AuthorFactoryImpl.java +++ b/components/net/sf/briar/protocol/AuthorFactoryImpl.java @@ -8,6 +8,7 @@ import net.sf.briar.api.crypto.CryptoComponent; 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.protocol.Types; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -28,7 +29,9 @@ class AuthorFactoryImpl implements AuthorFactory { throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Writer w = writerFactory.createWriter(out); - new AuthorImpl(null, name, publicKey).writeTo(w); + w.writeUserDefinedId(Types.AUTHOR); + w.writeString(name); + w.writeBytes(publicKey); MessageDigest messageDigest = crypto.getMessageDigest(); messageDigest.reset(); messageDigest.update(out.toByteArray()); diff --git a/components/net/sf/briar/protocol/AuthorImpl.java b/components/net/sf/briar/protocol/AuthorImpl.java index f232f932f5dba876c2162e55e096ba56316c50e7..dfaf022c84678e796aa924b60b363ccfe86aca69 100644 --- a/components/net/sf/briar/protocol/AuthorImpl.java +++ b/components/net/sf/briar/protocol/AuthorImpl.java @@ -1,11 +1,7 @@ package net.sf.briar.protocol; -import java.io.IOException; - import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.AuthorId; -import net.sf.briar.api.protocol.Types; -import net.sf.briar.api.serial.Writer; class AuthorImpl implements Author { @@ -30,10 +26,4 @@ class AuthorImpl implements Author { public byte[] getPublicKey() { return publicKey; } - - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.AUTHOR); - w.writeString(name); - w.writeBytes(publicKey); - } } diff --git a/components/net/sf/briar/protocol/AuthorReader.java b/components/net/sf/briar/protocol/AuthorReader.java index 06d509cafaed22598b0d58326eadece18ce20422..4451197bbe0cdc0c43e3757ab0c53bd57f2af6a1 100644 --- a/components/net/sf/briar/protocol/AuthorReader.java +++ b/components/net/sf/briar/protocol/AuthorReader.java @@ -7,6 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent; 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.protocol.ProtocolConstants; import net.sf.briar.api.protocol.Types; import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.Reader; @@ -28,8 +29,8 @@ class AuthorReader implements ObjectReader<Author> { // Read and digest the data r.addConsumer(digesting); r.readUserDefinedId(Types.AUTHOR); - String name = r.readString(Author.MAX_NAME_LENGTH); - byte[] publicKey = r.readBytes(Author.MAX_PUBLIC_KEY_LENGTH); + String name = r.readString(ProtocolConstants.MAX_AUTHOR_NAME_LENGTH); + byte[] publicKey = r.readBytes(ProtocolConstants.MAX_PUBLIC_KEY_LENGTH); r.removeConsumer(digesting); // Build and return the author AuthorId id = new AuthorId(messageDigest.digest()); diff --git a/components/net/sf/briar/protocol/GroupFactoryImpl.java b/components/net/sf/briar/protocol/GroupFactoryImpl.java index a8411c091d26a3fe9c2d6ad8026cf862aeb6dc7a..6df7b18553fef451c776006d8d8d9ebaf20d6926 100644 --- a/components/net/sf/briar/protocol/GroupFactoryImpl.java +++ b/components/net/sf/briar/protocol/GroupFactoryImpl.java @@ -8,6 +8,7 @@ import net.sf.briar.api.crypto.CryptoComponent; 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.Types; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -27,7 +28,10 @@ class GroupFactoryImpl implements GroupFactory { public Group createGroup(String name, byte[] publicKey) throws IOException { ByteArrayOutputStream out = new ByteArrayOutputStream(); Writer w = writerFactory.createWriter(out); - new GroupImpl(null, name, publicKey).writeTo(w); + w.writeUserDefinedId(Types.GROUP); + w.writeString(name); + if(publicKey == null) w.writeNull(); + else w.writeBytes(publicKey); MessageDigest messageDigest = crypto.getMessageDigest(); messageDigest.reset(); messageDigest.update(out.toByteArray()); diff --git a/components/net/sf/briar/protocol/GroupIdReader.java b/components/net/sf/briar/protocol/GroupIdReader.java deleted file mode 100644 index b121fd8cbc950d0e1c69eefa33768de09eac4d2f..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/GroupIdReader.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.sf.briar.protocol; - -import java.io.IOException; - -import net.sf.briar.api.FormatException; -import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.Types; -import net.sf.briar.api.protocol.UniqueId; -import net.sf.briar.api.serial.ObjectReader; -import net.sf.briar.api.serial.Reader; - -class GroupIdReader implements ObjectReader<GroupId> { - - public GroupId readObject(Reader r) throws IOException { - r.readUserDefinedId(Types.GROUP_ID); - byte[] b = r.readBytes(UniqueId.LENGTH); - if(b.length != UniqueId.LENGTH) throw new FormatException(); - return new GroupId(b); - } -} diff --git a/components/net/sf/briar/protocol/GroupImpl.java b/components/net/sf/briar/protocol/GroupImpl.java index 4f6f613e904d83c1ed46f823a09600bf980ca28d..88f63622d82873303953c8305202a117842a4ad9 100644 --- a/components/net/sf/briar/protocol/GroupImpl.java +++ b/components/net/sf/briar/protocol/GroupImpl.java @@ -1,11 +1,7 @@ package net.sf.briar.protocol; -import java.io.IOException; - import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.protocol.Types; -import net.sf.briar.api.serial.Writer; class GroupImpl implements Group { @@ -31,13 +27,6 @@ class GroupImpl implements Group { return publicKey; } - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(Types.GROUP); - w.writeString(name); - if(publicKey == null) w.writeNull(); - else w.writeBytes(publicKey); - } - @Override public boolean equals(Object o) { return o instanceof Group && id.equals(((Group) o).getId()); diff --git a/components/net/sf/briar/protocol/GroupReader.java b/components/net/sf/briar/protocol/GroupReader.java index 9b371c19da12da6c009d9abd4db4051c5ea1bb3e..9e5dc2d14b2e62e2e6d186c0a25dc25a4dfb63b0 100644 --- a/components/net/sf/briar/protocol/GroupReader.java +++ b/components/net/sf/briar/protocol/GroupReader.java @@ -7,6 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent; 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.ProtocolConstants; import net.sf.briar.api.protocol.Types; import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.Reader; @@ -28,10 +29,10 @@ class GroupReader implements ObjectReader<Group> { // Read and digest the data r.addConsumer(digesting); r.readUserDefinedId(Types.GROUP); - String name = r.readString(Group.MAX_NAME_LENGTH); + String name = r.readString(ProtocolConstants.MAX_GROUP_NAME_LENGTH); byte[] publicKey = null; if(r.hasNull()) r.readNull(); - else publicKey = r.readBytes(Group.MAX_PUBLIC_KEY_LENGTH); + else publicKey = r.readBytes(ProtocolConstants.MAX_PUBLIC_KEY_LENGTH); r.removeConsumer(digesting); // Build and return the group GroupId id = new GroupId(messageDigest.digest()); diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java index eeef6c410541a860e426df36a107536215b38b7c..821e53016611497d6749a74ab12e4424bcd6e637 100644 --- a/components/net/sf/briar/protocol/MessageEncoderImpl.java +++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java @@ -14,10 +14,12 @@ import net.sf.briar.api.protocol.AuthorId; 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.MessageEncoder; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.ProtocolConstants; import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.writers.AuthorWriter; +import net.sf.briar.api.protocol.writers.GroupWriter; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -30,14 +32,19 @@ class MessageEncoderImpl implements MessageEncoder { private final SecureRandom random; private final MessageDigest messageDigest; private final WriterFactory writerFactory; + private final AuthorWriter authorWriter; + private final GroupWriter groupWriter; @Inject - MessageEncoderImpl(CryptoComponent crypto, WriterFactory writerFactory) { + MessageEncoderImpl(CryptoComponent crypto, WriterFactory writerFactory, + AuthorWriter authorWriter, GroupWriter groupWriter) { authorSignature = crypto.getSignature(); groupSignature = crypto.getSignature(); random = crypto.getSecureRandom(); messageDigest = crypto.getMessageDigest(); this.writerFactory = writerFactory; + this.authorWriter = authorWriter; + this.groupWriter = groupWriter; } public Message encodeMessage(MessageId parent, String subject, byte[] body) @@ -74,9 +81,9 @@ class MessageEncoderImpl implements MessageEncoder { if((group == null || group.getPublicKey() == null) != (groupKey == null)) throw new IllegalArgumentException(); - if(subject.getBytes("UTF-8").length > Message.MAX_SUBJECT_LENGTH) + if(subject.getBytes("UTF-8").length > ProtocolConstants.MAX_SUBJECT_LENGTH) throw new IllegalArgumentException(); - if(body.length > Message.MAX_BODY_LENGTH) + if(body.length > ProtocolConstants.MAX_BODY_LENGTH) throw new IllegalArgumentException(); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -102,15 +109,15 @@ class MessageEncoderImpl implements MessageEncoder { // Write the message w.writeUserDefinedId(Types.MESSAGE); if(parent == null) w.writeNull(); - else parent.writeTo(w); + else w.writeBytes(parent.getBytes()); if(group == null) w.writeNull(); - else group.writeTo(w); + else groupWriter.writeGroup(w, group); if(author == null) w.writeNull(); - else author.writeTo(w); + else authorWriter.writeAuthor(w, author); w.writeString(subject); long timestamp = System.currentTimeMillis(); w.writeInt64(timestamp); - byte[] salt = new byte[Message.SALT_LENGTH]; + byte[] salt = new byte[ProtocolConstants.SALT_LENGTH]; random.nextBytes(salt); w.writeBytes(salt); w.writeBytes(body); @@ -121,7 +128,7 @@ class MessageEncoderImpl implements MessageEncoder { } else { w.removeConsumer(authorConsumer); byte[] sig = authorSignature.sign(); - if(sig.length > Message.MAX_SIGNATURE_LENGTH) + if(sig.length > ProtocolConstants.MAX_SIGNATURE_LENGTH) throw new IllegalArgumentException(); w.writeBytes(sig); } @@ -131,7 +138,7 @@ class MessageEncoderImpl implements MessageEncoder { } else { w.removeConsumer(groupConsumer); byte[] sig = groupSignature.sign(); - if(sig.length > Message.MAX_SIGNATURE_LENGTH) + if(sig.length > ProtocolConstants.MAX_SIGNATURE_LENGTH) throw new IllegalArgumentException(); w.writeBytes(sig); } diff --git a/components/net/sf/briar/protocol/MessageImpl.java b/components/net/sf/briar/protocol/MessageImpl.java index 6e35f76102d6434698402b776d1fc5ce1697f044..eece7bde294b471354b607222732eef661173e95 100644 --- a/components/net/sf/briar/protocol/MessageImpl.java +++ b/components/net/sf/briar/protocol/MessageImpl.java @@ -4,6 +4,7 @@ import net.sf.briar.api.protocol.AuthorId; 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.ProtocolConstants; /** A simple in-memory implementation of a message. */ class MessageImpl implements Message { @@ -21,7 +22,7 @@ class MessageImpl implements Message { int bodyStart, int bodyLength) { if(bodyStart + bodyLength > raw.length) throw new IllegalArgumentException(); - if(bodyLength > Message.MAX_BODY_LENGTH) + if(bodyLength > ProtocolConstants.MAX_BODY_LENGTH) throw new IllegalArgumentException(); this.id = id; this.parent = parent; diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java index 1039a42d7b9c581f04880903be59435ee93e1526..54502aff02970f7bf4c0d80c2f8acaa15d41ac95 100644 --- a/components/net/sf/briar/protocol/MessageReader.java +++ b/components/net/sf/briar/protocol/MessageReader.java @@ -77,15 +77,15 @@ class MessageReader implements ObjectReader<Message> { r.removeObjectReader(Types.AUTHOR); } // Read the subject - String subject = r.readString(Message.MAX_SUBJECT_LENGTH); + String subject = r.readString(ProtocolConstants.MAX_SUBJECT_LENGTH); // Read the timestamp long timestamp = r.readInt64(); if(timestamp < 0L) throw new FormatException(); // Read the salt - byte[] salt = r.readBytes(Message.SALT_LENGTH); - if(salt.length != Message.SALT_LENGTH) throw new FormatException(); + byte[] salt = r.readBytes(ProtocolConstants.SALT_LENGTH); + if(salt.length != ProtocolConstants.SALT_LENGTH) throw new FormatException(); // Read the message body - byte[] body = r.readBytes(Message.MAX_BODY_LENGTH); + byte[] body = r.readBytes(ProtocolConstants.MAX_BODY_LENGTH); // Record the offset of the body within the message int bodyStart = (int) counting.getCount() - body.length; // Record the length of the data covered by the author's signature @@ -93,13 +93,13 @@ class MessageReader implements ObjectReader<Message> { // Read the author's signature, if there is one byte[] authorSig = null; if(author == null) r.readNull(); - else authorSig = r.readBytes(Message.MAX_SIGNATURE_LENGTH); + else authorSig = r.readBytes(ProtocolConstants.MAX_SIGNATURE_LENGTH); // Record the length of the data covered by the group's signature int signedByGroup = (int) counting.getCount(); // Read the group's signature, if there is one byte[] groupSig = null; if(group == null || group.getPublicKey() == null) r.readNull(); - else groupSig = r.readBytes(Message.MAX_SIGNATURE_LENGTH); + else groupSig = r.readBytes(ProtocolConstants.MAX_SIGNATURE_LENGTH); // That's all, folks r.removeConsumer(counting); r.removeConsumer(copying); diff --git a/components/net/sf/briar/protocol/ProtocolModule.java b/components/net/sf/briar/protocol/ProtocolModule.java index 6d36d8efb38cc18522aa7a1f9d5a7f83dcb95c53..aa9c033f2554729565ed5b87971ab4ca843728aa 100644 --- a/components/net/sf/briar/protocol/ProtocolModule.java +++ b/components/net/sf/briar/protocol/ProtocolModule.java @@ -9,13 +9,13 @@ import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.serial.ObjectReader; import com.google.inject.AbstractModule; @@ -33,8 +33,8 @@ public class ProtocolModule extends AbstractModule { bind(OfferFactory.class).to(OfferFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(RequestFactory.class).to(RequestFactoryImpl.class); - bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class); - bind(TransportFactory.class).to(TransportFactoryImpl.class); + bind(SubscriptionUpdateFactory.class).to(SubscriptionUpdateFactoryImpl.class); + bind(TransportUpdateFactory.class).to(TransportUpdateFactoryImpl.class); } @Provides @@ -94,13 +94,13 @@ public class ProtocolModule extends AbstractModule { @Provides ObjectReader<SubscriptionUpdate> getSubscriptionReader( ObjectReader<Group> groupReader, - SubscriptionFactory subscriptionFactory) { - return new SubscriptionReader(groupReader, subscriptionFactory); + SubscriptionUpdateFactory subscriptionFactory) { + return new SubscriptionUpdateReader(groupReader, subscriptionFactory); } @Provides ObjectReader<TransportUpdate> getTransportReader( - TransportFactory transportFactory) { - return new TransportReader(transportFactory); + TransportUpdateFactory transportFactory) { + return new TransportUpdateReader(transportFactory); } } diff --git a/components/net/sf/briar/protocol/SubscriptionFactory.java b/components/net/sf/briar/protocol/SubscriptionUpdateFactory.java similarity index 86% rename from components/net/sf/briar/protocol/SubscriptionFactory.java rename to components/net/sf/briar/protocol/SubscriptionUpdateFactory.java index ee598296810252a0a036679c891b55c8a997e562..5d04150ececd0f35a58bc21a2c71c781ff2af32c 100644 --- a/components/net/sf/briar/protocol/SubscriptionFactory.java +++ b/components/net/sf/briar/protocol/SubscriptionUpdateFactory.java @@ -5,7 +5,7 @@ import java.util.Map; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.SubscriptionUpdate; -interface SubscriptionFactory { +interface SubscriptionUpdateFactory { SubscriptionUpdate createSubscriptions(Map<Group, Long> subs, long timestamp); diff --git a/components/net/sf/briar/protocol/SubscriptionFactoryImpl.java b/components/net/sf/briar/protocol/SubscriptionUpdateFactoryImpl.java similarity index 80% rename from components/net/sf/briar/protocol/SubscriptionFactoryImpl.java rename to components/net/sf/briar/protocol/SubscriptionUpdateFactoryImpl.java index 07dfdec95266a292e2eed14082de57ab520c7b93..aeb93b3a768f010ac0682bb4b7b86272df88c7e8 100644 --- a/components/net/sf/briar/protocol/SubscriptionFactoryImpl.java +++ b/components/net/sf/briar/protocol/SubscriptionUpdateFactoryImpl.java @@ -5,7 +5,7 @@ import java.util.Map; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.SubscriptionUpdate; -class SubscriptionFactoryImpl implements SubscriptionFactory { +class SubscriptionUpdateFactoryImpl implements SubscriptionUpdateFactory { public SubscriptionUpdate createSubscriptions(Map<Group, Long> subs, long timestamp) { diff --git a/components/net/sf/briar/protocol/SubscriptionReader.java b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java similarity index 83% rename from components/net/sf/briar/protocol/SubscriptionReader.java rename to components/net/sf/briar/protocol/SubscriptionUpdateReader.java index d8bb47a03d6fe8e44438abdb0e3c5be3ad1b6987..b5f37f01f8b53eeda0879da869bce6a82ddcc8a1 100644 --- a/components/net/sf/briar/protocol/SubscriptionReader.java +++ b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java @@ -12,13 +12,13 @@ import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.Reader; -class SubscriptionReader implements ObjectReader<SubscriptionUpdate> { +class SubscriptionUpdateReader implements ObjectReader<SubscriptionUpdate> { private final ObjectReader<Group> groupReader; - private final SubscriptionFactory subscriptionFactory; + private final SubscriptionUpdateFactory subscriptionFactory; - SubscriptionReader(ObjectReader<Group> groupReader, - SubscriptionFactory subscriptionFactory) { + SubscriptionUpdateReader(ObjectReader<Group> groupReader, + SubscriptionUpdateFactory subscriptionFactory) { this.groupReader = groupReader; this.subscriptionFactory = subscriptionFactory; } diff --git a/components/net/sf/briar/protocol/TransportFactory.java b/components/net/sf/briar/protocol/TransportFactory.java deleted file mode 100644 index 7cec4d4e1df3f1016d3489af8be1721e22a370a6..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/TransportFactory.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Map; - -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; -import net.sf.briar.api.protocol.TransportUpdate; - -interface TransportFactory { - - TransportUpdate createTransportUpdate( - Map<TransportId, TransportProperties> transports, long timestamp); -} diff --git a/components/net/sf/briar/protocol/TransportFactoryImpl.java b/components/net/sf/briar/protocol/TransportFactoryImpl.java deleted file mode 100644 index 25c88070c2467c1b03be1fed42e40e151f1bf063..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/TransportFactoryImpl.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.sf.briar.protocol; - -import java.util.Map; - -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; -import net.sf.briar.api.protocol.TransportUpdate; - -class TransportFactoryImpl implements TransportFactory { - - public TransportUpdate createTransportUpdate( - Map<TransportId, TransportProperties> transports, long timestamp) { - return new TransportUpdateImpl(transports, timestamp); - } -} diff --git a/components/net/sf/briar/protocol/TransportReader.java b/components/net/sf/briar/protocol/TransportReader.java deleted file mode 100644 index 0010d4262fc67a548ed45fc8386aa33d3c70b082..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/protocol/TransportReader.java +++ /dev/null @@ -1,81 +0,0 @@ -package net.sf.briar.protocol; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import net.sf.briar.api.FormatException; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; -import net.sf.briar.api.protocol.ProtocolConstants; -import net.sf.briar.api.protocol.TransportUpdate; -import net.sf.briar.api.protocol.Types; -import net.sf.briar.api.serial.Consumer; -import net.sf.briar.api.serial.ObjectReader; -import net.sf.briar.api.serial.Reader; - -class TransportReader implements ObjectReader<TransportUpdate> { - - private final TransportFactory transportFactory; - private final ObjectReader<Transport> propertiesReader; - - TransportReader(TransportFactory transportFactory) { - this.transportFactory = transportFactory; - propertiesReader = new PropertiesReader(); - } - - public TransportUpdate readObject(Reader r) throws IOException { - // Initialise the consumer - Consumer counting = - new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH); - // Read the data - r.addConsumer(counting); - r.readUserDefinedId(Types.TRANSPORT_UPDATE); - r.addObjectReader(Types.TRANSPORT_PROPERTIES, propertiesReader); - r.setMaxStringLength(ProtocolConstants.MAX_PACKET_LENGTH); - List<Transport> l = r.readList(Transport.class); - r.resetMaxStringLength(); - r.removeObjectReader(Types.TRANSPORT_PROPERTIES); - if(l.size() > TransportUpdate.MAX_PLUGINS_PER_UPDATE) - throw new FormatException(); - Map<TransportId, TransportProperties> transports = - new HashMap<TransportId, TransportProperties>(); - for(Transport t : l) { - if(transports.put(t.id, t.properties) != null) - throw new FormatException(); // Duplicate transport ID - } - long timestamp = r.readInt64(); - r.removeConsumer(counting); - // Build and return the transport update - return transportFactory.createTransportUpdate(transports, timestamp); - } - - private static class Transport { - - private final TransportId id; - private final TransportProperties properties; - - Transport(TransportId id, TransportProperties properties) { - this.id = id; - this.properties = properties; - } - } - - private static class PropertiesReader implements ObjectReader<Transport> { - - public Transport readObject(Reader r) throws IOException { - r.readUserDefinedId(Types.TRANSPORT_PROPERTIES); - int i = r.readInt32(); - if(i < TransportId.MIN_ID || i > TransportId.MAX_ID) - throw new FormatException(); - TransportId id = new TransportId(i); - r.setMaxStringLength(TransportUpdate.MAX_KEY_OR_VALUE_LENGTH); - Map<String, String> m = r.readMap(String.class, String.class); - r.resetMaxStringLength(); - if(m.size() > TransportUpdate.MAX_PROPERTIES_PER_PLUGIN) - throw new FormatException(); - return new Transport(id, new TransportProperties(m)); - } - } -} diff --git a/components/net/sf/briar/protocol/TransportUpdateFactory.java b/components/net/sf/briar/protocol/TransportUpdateFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..0be7251d21d8b88a697677c5447e562da6102be9 --- /dev/null +++ b/components/net/sf/briar/protocol/TransportUpdateFactory.java @@ -0,0 +1,12 @@ +package net.sf.briar.protocol; + +import java.util.Collection; + +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportUpdate; + +interface TransportUpdateFactory { + + TransportUpdate createTransportUpdate(Collection<Transport> transports, + long timestamp); +} diff --git a/components/net/sf/briar/protocol/TransportUpdateFactoryImpl.java b/components/net/sf/briar/protocol/TransportUpdateFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..bf098bcd0bc5a6fb310d7d51f069d385360ccf9c --- /dev/null +++ b/components/net/sf/briar/protocol/TransportUpdateFactoryImpl.java @@ -0,0 +1,14 @@ +package net.sf.briar.protocol; + +import java.util.Collection; + +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportUpdate; + +class TransportUpdateFactoryImpl implements TransportUpdateFactory { + + public TransportUpdate createTransportUpdate( + Collection<Transport> transports, long timestamp) { + return new TransportUpdateImpl(transports, timestamp); + } +} diff --git a/components/net/sf/briar/protocol/TransportUpdateImpl.java b/components/net/sf/briar/protocol/TransportUpdateImpl.java index 330275ca0c5c5d3a44ea82920ec4ec6d3649f11c..b69a2bce777c3288e46bbaf67588d2fa48abd842 100644 --- a/components/net/sf/briar/protocol/TransportUpdateImpl.java +++ b/components/net/sf/briar/protocol/TransportUpdateImpl.java @@ -1,23 +1,22 @@ package net.sf.briar.protocol; -import java.util.Map; +import java.util.Collection; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.TransportUpdate; class TransportUpdateImpl implements TransportUpdate { - private final Map<TransportId, TransportProperties> transports; + private final Collection<Transport> transports; private final long timestamp; - TransportUpdateImpl(Map<TransportId, TransportProperties> transports, + TransportUpdateImpl(Collection<Transport> transports, long timestamp) { this.transports = transports; this.timestamp = timestamp; } - public Map<TransportId, TransportProperties> getTransports() { + public Collection<Transport> getTransports() { return transports; } diff --git a/components/net/sf/briar/protocol/TransportUpdateReader.java b/components/net/sf/briar/protocol/TransportUpdateReader.java new file mode 100644 index 0000000000000000000000000000000000000000..5eae4d6138f10b8691fa7f0bfe0adb26e4f1b217 --- /dev/null +++ b/components/net/sf/briar/protocol/TransportUpdateReader.java @@ -0,0 +1,79 @@ +package net.sf.briar.protocol; + +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.ProtocolConstants; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.api.serial.Consumer; +import net.sf.briar.api.serial.ObjectReader; +import net.sf.briar.api.serial.Reader; + +class TransportUpdateReader implements ObjectReader<TransportUpdate> { + + private final TransportUpdateFactory transportUpdateFactory; + private final ObjectReader<Transport> transportReader; + + TransportUpdateReader(TransportUpdateFactory transportFactory) { + this.transportUpdateFactory = transportFactory; + transportReader = new TransportReader(); + } + + public TransportUpdate readObject(Reader r) throws IOException { + // Initialise the consumer + Consumer counting = + new CountingConsumer(ProtocolConstants.MAX_PACKET_LENGTH); + // Read the data + r.addConsumer(counting); + r.readUserDefinedId(Types.TRANSPORT_UPDATE); + r.addObjectReader(Types.TRANSPORT, transportReader); + Collection<Transport> transports = r.readList(Transport.class); + r.removeObjectReader(Types.TRANSPORT); + if(transports.size() > ProtocolConstants.MAX_TRANSPORTS) + throw new FormatException(); + long timestamp = r.readInt64(); + r.removeConsumer(counting); + // Check for duplicate IDs or indices + Set<TransportId> ids = new HashSet<TransportId>(); + Set<TransportIndex> indices = new HashSet<TransportIndex>(); + for(Transport t : transports) { + if(!ids.add(t.getId())) throw new FormatException(); + if(!indices.add(t.getIndex())) throw new FormatException(); + } + // Build and return the transport update + return transportUpdateFactory.createTransportUpdate(transports, + timestamp); + } + + private class TransportReader implements ObjectReader<Transport> { + + public Transport readObject(Reader r) throws IOException { + r.readUserDefinedId(Types.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 index + int i = r.readInt32(); + if(i < 0 || i >= ProtocolConstants.MAX_TRANSPORTS) + throw new FormatException(); + TransportIndex index = new TransportIndex(i); + // Read the properties + r.setMaxStringLength(ProtocolConstants.MAX_PROPERTY_LENGTH); + Map<String, String> m = r.readMap(String.class, String.class); + r.resetMaxStringLength(); + if(m.size() > ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT) + throw new FormatException(); + return new Transport(id, index, m); + } + } +} diff --git a/components/net/sf/briar/protocol/writers/AckWriterImpl.java b/components/net/sf/briar/protocol/writers/AckWriterImpl.java index ade9ef6bf02d1463e75bfe1af04642d587011dcf..f943b77271c14a00ed51565c1d48d22d9e0af14d 100644 --- a/components/net/sf/briar/protocol/writers/AckWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/AckWriterImpl.java @@ -41,7 +41,8 @@ class AckWriterImpl implements AckWriter { int overhead = started ? footerLength : headerLength + footerLength; if(capacity < idLength + overhead) return false; if(!started) start(); - b.writeTo(w); + w.writeUserDefinedId(Types.BATCH_ID); + w.writeBytes(b.getBytes()); capacity -= idLength; return true; } diff --git a/components/net/sf/briar/protocol/writers/AuthorWriterImpl.java b/components/net/sf/briar/protocol/writers/AuthorWriterImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..eb1d16e90aee71a46f53db552cdcc3b4cc866410 --- /dev/null +++ b/components/net/sf/briar/protocol/writers/AuthorWriterImpl.java @@ -0,0 +1,17 @@ +package net.sf.briar.protocol.writers; + +import java.io.IOException; + +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.writers.AuthorWriter; +import net.sf.briar.api.serial.Writer; + +class AuthorWriterImpl implements AuthorWriter { + + public void writeAuthor(Writer w, Author a) throws IOException { + w.writeUserDefinedId(Types.AUTHOR); + w.writeString(a.getName()); + w.writeBytes(a.getPublicKey()); + } +} diff --git a/components/net/sf/briar/protocol/writers/GroupWriterImpl.java b/components/net/sf/briar/protocol/writers/GroupWriterImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..44974a1f6044f121047582aefe23e55f94d5e854 --- /dev/null +++ b/components/net/sf/briar/protocol/writers/GroupWriterImpl.java @@ -0,0 +1,19 @@ +package net.sf.briar.protocol.writers; + +import java.io.IOException; + +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.writers.GroupWriter; +import net.sf.briar.api.serial.Writer; + +class GroupWriterImpl implements GroupWriter { + + public void writeGroup(Writer w, Group g) throws IOException { + w.writeUserDefinedId(Types.GROUP); + w.writeString(g.getName()); + byte[] publicKey = g.getPublicKey(); + if(publicKey == null) w.writeNull(); + else w.writeBytes(publicKey); + } +} diff --git a/components/net/sf/briar/protocol/writers/OfferWriterImpl.java b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java index 0aa54a6d0f8a9b0b6c25dd6c7f8ab5fba3a46b9b..523a534af92e983a239e3f303b25706e929ff45c 100644 --- a/components/net/sf/briar/protocol/writers/OfferWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/OfferWriterImpl.java @@ -41,7 +41,8 @@ class OfferWriterImpl implements OfferWriter { int overhead = started ? footerLength : headerLength + footerLength; if(capacity < idLength + overhead) return false; if(!started) start(); - m.writeTo(w); + w.writeUserDefinedId(Types.MESSAGE_ID); + w.writeBytes(m.getBytes()); capacity -= idLength; return true; } diff --git a/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java b/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java index 7e83b36b0511020ffe133de15e4f77347271c20a..1afe708d9b259b0949706dfa78cc861480be314c 100644 --- a/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java +++ b/components/net/sf/briar/protocol/writers/ProtocolWritersModule.java @@ -1,5 +1,7 @@ package net.sf.briar.protocol.writers; +import net.sf.briar.api.protocol.writers.AuthorWriter; +import net.sf.briar.api.protocol.writers.GroupWriter; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import com.google.inject.AbstractModule; @@ -8,6 +10,8 @@ public class ProtocolWritersModule extends AbstractModule { @Override protected void configure() { + bind(AuthorWriter.class).to(AuthorWriterImpl.class); + bind(GroupWriter.class).to(GroupWriterImpl.class); bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class); } } diff --git a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java index 82440ba4c312fb1ee54fe3a78981b4b2bed6b354..cf1d8ff60c50b109d4c9d2c217c9229529fef83d 100644 --- a/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/SubscriptionWriterImpl.java @@ -3,9 +3,11 @@ package net.sf.briar.protocol.writers; import java.io.IOException; import java.io.OutputStream; import java.util.Map; +import java.util.Map.Entry; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.writers.GroupWriter; import net.sf.briar.api.protocol.writers.SubscriptionWriter; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -14,16 +16,23 @@ class SubscriptionWriterImpl implements SubscriptionWriter { private final OutputStream out; private final Writer w; + private final GroupWriter groupWriter; SubscriptionWriterImpl(OutputStream out, WriterFactory writerFactory) { this.out = out; w = writerFactory.createWriter(out); + groupWriter = new GroupWriterImpl(); } public void writeSubscriptions(Map<Group, Long> subs, long timestamp) throws IOException { w.writeUserDefinedId(Types.SUBSCRIPTION_UPDATE); - w.writeMap(subs); + w.writeMapStart(); + for(Entry<Group, Long> e : subs.entrySet()) { + groupWriter.writeGroup(w, e.getKey()); + w.writeInt64(e.getValue()); + } + w.writeMapEnd(); w.writeInt64(timestamp); out.flush(); } diff --git a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java index 6e3ca629b735d23d98ec9d3ba3be7f93c181d566..aa58385e49bc285a7acfcc7f7beb34d118b8f34d 100644 --- a/components/net/sf/briar/protocol/writers/TransportWriterImpl.java +++ b/components/net/sf/briar/protocol/writers/TransportWriterImpl.java @@ -2,11 +2,9 @@ package net.sf.briar.protocol.writers; import java.io.IOException; import java.io.OutputStream; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Collection; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.protocol.Transport; import net.sf.briar.api.protocol.Types; import net.sf.briar.api.protocol.writers.TransportWriter; import net.sf.briar.api.serial.Writer; @@ -22,15 +20,15 @@ class TransportWriterImpl implements TransportWriter { w = writerFactory.createWriter(out); } - public void writeTransports( - Map<TransportId, TransportProperties> transports, long timestamp) - throws IOException { + public void writeTransports(Collection<Transport> transports, + long timestamp) throws IOException { w.writeUserDefinedId(Types.TRANSPORT_UPDATE); w.writeListStart(); - for(Entry<TransportId, TransportProperties> e : transports.entrySet()) { - w.writeUserDefinedId(Types.TRANSPORT_PROPERTIES); - w.writeInt32(e.getKey().getInt()); - w.writeMap(e.getValue()); + for(Transport p : transports) { + w.writeUserDefinedId(Types.TRANSPORT); + w.writeBytes(p.getId().getBytes()); + w.writeInt32(p.getIndex().getInt()); + w.writeMap(p); } w.writeListEnd(); w.writeInt64(timestamp); diff --git a/components/net/sf/briar/serial/WriterImpl.java b/components/net/sf/briar/serial/WriterImpl.java index c84d989ceabf7cb4a2155f455396a3227cacd4e1..a212a13075fe3c2f8c80874ee39fd0dd10d10035 100644 --- a/components/net/sf/briar/serial/WriterImpl.java +++ b/components/net/sf/briar/serial/WriterImpl.java @@ -10,7 +10,6 @@ import java.util.Map.Entry; import net.sf.briar.api.Bytes; import net.sf.briar.api.serial.Consumer; -import net.sf.briar.api.serial.Writable; import net.sf.briar.api.serial.Writer; class WriterImpl implements Writer { @@ -139,8 +138,7 @@ class WriterImpl implements Writer { } private void writeObject(Object o) throws IOException { - if(o instanceof Writable) ((Writable) o).writeTo(this); - else if(o instanceof Boolean) writeBoolean((Boolean) o); + if(o instanceof Boolean) writeBoolean((Boolean) o); else if(o instanceof Byte) writeIntAny((Byte) o); else if(o instanceof Short) writeIntAny((Short) o); else if(o instanceof Integer) writeIntAny((Integer) o); diff --git a/components/net/sf/briar/transport/ConnectionContextImpl.java b/components/net/sf/briar/transport/ConnectionContextImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..eedbf73fe6063dff6790fe228d30209458878e22 --- /dev/null +++ b/components/net/sf/briar/transport/ConnectionContextImpl.java @@ -0,0 +1,38 @@ +package net.sf.briar.transport; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.transport.ConnectionContext; + +class ConnectionContextImpl implements ConnectionContext { + + private final ContactId contactId; + private final TransportId transportId; + private final TransportIndex transportIndex; + private final long connectionNumber; + + ConnectionContextImpl(ContactId contactId, TransportId transportId, + TransportIndex transportIndex, long connectionNumber) { + this.contactId = contactId; + this.transportId = transportId; + this.transportIndex = transportIndex; + this.connectionNumber = connectionNumber; + } + + public ContactId getContactId() { + return contactId; + } + + public TransportId getTransportId() { + return transportId; + } + + public TransportIndex getTransportIndex() { + return transportIndex; + } + + public long getConnectionNumber() { + return connectionNumber; + } +} diff --git a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java index 0cc06f95a73f749424139a475639f2fb1270c98a..ca25c792a6145470ce541d7c3a76c7e8ba160473 100644 --- a/components/net/sf/briar/transport/ConnectionDispatcherImpl.java +++ b/components/net/sf/briar/transport/ConnectionDispatcherImpl.java @@ -2,41 +2,41 @@ package net.sf.briar.transport; import java.io.IOException; import java.io.InputStream; -import java.util.HashMap; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.BatchConnectionFactory; import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportWriter; +import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionDispatcher; import net.sf.briar.api.transport.ConnectionRecogniser; -import net.sf.briar.api.transport.ConnectionRecogniserFactory; import net.sf.briar.api.transport.StreamConnectionFactory; import net.sf.briar.api.transport.StreamTransportConnection; import net.sf.briar.api.transport.TransportConstants; +import com.google.inject.Inject; + public class ConnectionDispatcherImpl implements ConnectionDispatcher { private static final Logger LOG = Logger.getLogger(ConnectionDispatcherImpl.class.getName()); - private final ConnectionRecogniserFactory recFactory; + private final ConnectionRecogniser recogniser; private final BatchConnectionFactory batchConnFactory; private final StreamConnectionFactory streamConnFactory; - private final Map<TransportId, ConnectionRecogniser> recognisers; - ConnectionDispatcherImpl(ConnectionRecogniserFactory recFactory, + @Inject + ConnectionDispatcherImpl(ConnectionRecogniser recogniser, BatchConnectionFactory batchConnFactory, StreamConnectionFactory streamConnFactory) { - this.recFactory = recFactory; + this.recogniser = recogniser; this.batchConnFactory = batchConnFactory; this.streamConnFactory = streamConnFactory; - recognisers = new HashMap<TransportId, ConnectionRecogniser>(); } public void dispatchReader(TransportId t, BatchTransportReader r) { @@ -49,21 +49,27 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher { r.dispose(false); return; } - // Get the contact ID, or null if the IV wasn't expected - ContactId c; + // Get the connection context, or null if the IV wasn't expected + ConnectionContext ctx; try { - ConnectionRecogniser rec = getRecogniser(t); - c = rec.acceptConnection(encryptedIv); + ctx = recogniser.acceptConnection(encryptedIv); } catch(DbException e) { if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage()); r.dispose(false); return; } - if(c == null) { + if(ctx == null) { r.dispose(false); return; } - batchConnFactory.createIncomingConnection(t, c, r, encryptedIv); + if(!t.equals(ctx.getTransportId())) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Connection has unexpected transport ID"); + r.dispose(false); + return; + } + batchConnFactory.createIncomingConnection(ctx.getTransportIndex(), + ctx.getContactId(), r, encryptedIv); } private byte[] readIv(InputStream in) throws IOException { @@ -77,20 +83,9 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher { return b; } - private ConnectionRecogniser getRecogniser(TransportId t) { - synchronized(recognisers) { - ConnectionRecogniser rec = recognisers.get(t); - if(rec == null) { - rec = recFactory.createConnectionRecogniser(t); - recognisers.put(t, rec); - } - return rec; - } - } - - public void dispatchWriter(TransportId t, ContactId c, + public void dispatchWriter(TransportIndex i, ContactId c, BatchTransportWriter w) { - batchConnFactory.createOutgoingConnection(t, c, w); + batchConnFactory.createOutgoingConnection(i, c, w); } public void dispatchIncomingConnection(TransportId t, @@ -104,25 +99,31 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher { s.dispose(false); return; } - // Get the contact ID, or null if the IV wasn't expected - ContactId c; + // Get the connection context, or null if the IV wasn't expected + ConnectionContext ctx; try { - ConnectionRecogniser rec = getRecogniser(t); - c = rec.acceptConnection(encryptedIv); + ctx = recogniser.acceptConnection(encryptedIv); } catch(DbException e) { if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage()); s.dispose(false); return; } - if(c == null) { + if(ctx == null) { + s.dispose(false); + return; + } + if(!t.equals(ctx.getTransportId())) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning("Connection has unexpected transport ID"); s.dispose(false); return; } - streamConnFactory.createIncomingConnection(t, c, s, encryptedIv); + streamConnFactory.createIncomingConnection(ctx.getTransportIndex(), + ctx.getContactId(), s, encryptedIv); } - public void dispatchOutgoingConnection(TransportId t, ContactId c, + public void dispatchOutgoingConnection(TransportIndex i, ContactId c, StreamTransportConnection s) { - streamConnFactory.createOutgoingConnection(t, c, s); + streamConnFactory.createOutgoingConnection(i, c, s); } } diff --git a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java index 98d9104f86fc2fea2b4355eff79b6615c68ce706..588837758dfb3f87304aa75c11779b7c51a5e0fb 100644 --- a/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionReaderFactoryImpl.java @@ -9,8 +9,8 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.Mac; import javax.crypto.SecretKey; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReaderFactory; @@ -26,7 +26,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory { } public ConnectionReader createConnectionReader(InputStream in, - TransportId t, byte[] encryptedIv, byte[] secret) { + TransportIndex i, byte[] encryptedIv, byte[] secret) { // Decrypt the IV Cipher ivCipher = crypto.getIvCipher(); SecretKey ivKey = crypto.deriveIncomingIvKey(secret); @@ -42,21 +42,22 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory { throw new IllegalArgumentException(badKey); } // Validate the IV - if(!IvEncoder.validateIv(iv, true, t)) + if(!IvEncoder.validateIv(iv, true, i)) throw new IllegalArgumentException(); // Copy the connection number long connection = IvEncoder.getConnectionNumber(iv); - return createConnectionReader(in, true, t, connection, secret); + return createConnectionReader(in, true, i, connection, secret); } public ConnectionReader createConnectionReader(InputStream in, - TransportId t, long connection, byte[] secret) { - return createConnectionReader(in, false, t, connection, secret); + TransportIndex i, long connection, byte[] secret) { + return createConnectionReader(in, false, i, connection, secret); } private ConnectionReader createConnectionReader(InputStream in, - boolean initiator, TransportId t, long connection, byte[] secret) { - byte[] iv = IvEncoder.encodeIv(initiator, t, connection); + boolean initiator, TransportIndex i, long connection, + byte[] secret) { + byte[] iv = IvEncoder.encodeIv(initiator, i, connection); // Create the decrypter Cipher frameCipher = crypto.getFrameCipher(); SecretKey frameKey = crypto.deriveIncomingFrameKey(secret); diff --git a/components/net/sf/briar/transport/ConnectionRecogniserFactoryImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserFactoryImpl.java deleted file mode 100644 index ed86ff5f942f5fcf3eef866d46a4dd4dfc29f826..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/ConnectionRecogniserFactoryImpl.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.sf.briar.transport; - -import net.sf.briar.api.TransportId; -import net.sf.briar.api.crypto.CryptoComponent; -import net.sf.briar.api.db.DatabaseComponent; -import net.sf.briar.api.transport.ConnectionRecogniser; -import net.sf.briar.api.transport.ConnectionRecogniserFactory; - -import com.google.inject.Inject; - -class ConnectionRecogniserFactoryImpl implements ConnectionRecogniserFactory { - - private final CryptoComponent crypto; - private final DatabaseComponent db; - - @Inject - ConnectionRecogniserFactoryImpl(CryptoComponent crypto, - DatabaseComponent db) { - this.crypto = crypto; - this.db = db; - } - - public ConnectionRecogniser createConnectionRecogniser(TransportId t) { - return new ConnectionRecogniserImpl(t, crypto, db); - } -} diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java index e2787b0baf04f575646235ccfeb9484c3763ad65..56f728f619ac631f7e37ca188dbad29034431c0e 100644 --- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java +++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java @@ -3,8 +3,13 @@ package net.sf.briar.transport; import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import java.security.InvalidKeyException; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -13,128 +18,178 @@ import javax.crypto.SecretKey; import net.sf.briar.api.Bytes; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.NoSuchContactException; -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.TransportAddedEvent; +import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionRecogniser; import net.sf.briar.api.transport.ConnectionWindow; +import com.google.inject.Inject; + class ConnectionRecogniserImpl implements ConnectionRecogniser, DatabaseListener { - private final TransportId id; + private static final Logger LOG = + Logger.getLogger(ConnectionRecogniserImpl.class.getName()); + private final CryptoComponent crypto; private final DatabaseComponent db; - private final Map<Bytes, ContactId> ivToContact; - private final Map<Bytes, Long> ivToConnectionNumber; - private final Map<ContactId, Map<Long, Bytes>> contactToIvs; - private final Map<ContactId, Cipher> contactToCipher; - private final Map<ContactId, ConnectionWindow> contactToWindow; + private final Cipher ivCipher; + private final Map<Bytes, ConnectionContext> expected; + private final Collection<TransportId> localTransportIds; + private boolean initialised = false; - ConnectionRecogniserImpl(TransportId id, CryptoComponent crypto, - DatabaseComponent db) { - this.id = id; + @Inject + ConnectionRecogniserImpl(CryptoComponent crypto, DatabaseComponent db) { this.crypto = crypto; this.db = db; - // FIXME: There's probably a tidier way of maintaining all this state - ivToContact = new HashMap<Bytes, ContactId>(); - ivToConnectionNumber = new HashMap<Bytes, Long>(); - contactToIvs = new HashMap<ContactId, Map<Long, Bytes>>(); - contactToCipher = new HashMap<ContactId, Cipher>(); - contactToWindow = new HashMap<ContactId, ConnectionWindow>(); + ivCipher = crypto.getIvCipher(); + expected = new HashMap<Bytes, ConnectionContext>(); + localTransportIds = new ArrayList<TransportId>(); db.addListener(this); } private synchronized void initialise() throws DbException { + for(Transport t : db.getLocalTransports()) { + localTransportIds.add(t.getId()); + } for(ContactId c : db.getContacts()) { try { - // Initialise and store the contact's IV cipher - byte[] secret = db.getSharedSecret(c); - SecretKey ivKey = crypto.deriveIncomingIvKey(secret); - Cipher cipher = crypto.getIvCipher(); - try { - cipher.init(Cipher.ENCRYPT_MODE, ivKey); - } catch(InvalidKeyException badKey) { - throw new RuntimeException(badKey); - } - contactToCipher.put(c, cipher); - // Calculate the IVs for the contact's connection window - ConnectionWindow w = db.getConnectionWindow(c, id); - Map<Long, Bytes> ivs = new HashMap<Long, Bytes>(); - for(Long unseen : w.getUnseenConnectionNumbers()) { - Bytes expectedIv = new Bytes(encryptIv(c, unseen)); - ivToContact.put(expectedIv, c); - ivToConnectionNumber.put(expectedIv, unseen); - ivs.put(unseen, expectedIv); - } - contactToIvs.put(c, ivs); - contactToWindow.put(c, w); + calculateIvs(c); } catch(NoSuchContactException e) { - // The contact was removed after the call to getContacts() - continue; + // The contact was removed - clean up in eventOccurred() } } initialised = true; } - private synchronized byte[] encryptIv(ContactId c, long connection) { - byte[] iv = IvEncoder.encodeIv(true, id, connection); - Cipher cipher = contactToCipher.get(c); - assert cipher != null; + private synchronized void calculateIvs(ContactId c) throws DbException { + SecretKey ivKey = crypto.deriveIncomingIvKey(db.getSharedSecret(c)); + for(TransportId t : localTransportIds) { + TransportIndex i = db.getRemoteIndex(c, t); + if(i != null) { + ConnectionWindow w = db.getConnectionWindow(c, i); + calculateIvs(c, t, i, ivKey, w); + } + } + } + + private synchronized void calculateIvs(ContactId c, TransportId t, + TransportIndex i, SecretKey ivKey, ConnectionWindow w) + throws DbException { + for(Long unseen : w.getUnseenConnectionNumbers()) { + Bytes iv = new Bytes(encryptIv(i, unseen, ivKey)); + expected.put(iv, new ConnectionContextImpl(c, t, i, unseen)); + } + } + + private synchronized byte[] encryptIv(TransportIndex i, long connection, + SecretKey ivKey) { + byte[] iv = IvEncoder.encodeIv(true, i, connection); try { - return cipher.doFinal(iv); + ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); + return ivCipher.doFinal(iv); } catch(BadPaddingException badCipher) { throw new RuntimeException(badCipher); } catch(IllegalBlockSizeException badCipher) { throw new RuntimeException(badCipher); + } catch(InvalidKeyException badKey) { + throw new RuntimeException(badKey); } } - public synchronized ContactId acceptConnection(byte[] encryptedIv) + public synchronized ConnectionContext acceptConnection(byte[] encryptedIv) throws DbException { if(encryptedIv.length != IV_LENGTH) throw new IllegalArgumentException(); if(!initialised) initialise(); - Bytes b = new Bytes(encryptedIv); - ContactId contactId = ivToContact.remove(b); - Long connection = ivToConnectionNumber.remove(b); - assert (contactId == null) == (connection == null); - if(contactId == null) return null; - // The IV was expected - update and save the connection window - ConnectionWindow w = contactToWindow.get(contactId); - assert w != null; - w.setSeen(connection); - db.setConnectionWindow(contactId, id, w); - // Update the set of expected IVs - Map<Long, Bytes> oldIvs = contactToIvs.remove(contactId); - assert oldIvs != null; - assert oldIvs.containsKey(connection); - Map<Long, Bytes> newIvs = new HashMap<Long, Bytes>(); - for(Long unseen : w.getUnseenConnectionNumbers()) { - Bytes expectedIv = oldIvs.get(unseen); - if(expectedIv == null) { - expectedIv = new Bytes(encryptIv(contactId, unseen)); - ivToContact.put(expectedIv, contactId); - ivToConnectionNumber.put(expectedIv, connection); + ConnectionContext ctx = expected.remove(new Bytes(encryptedIv)); + if(ctx == null) return null; // The IV was not expected + try { + ContactId c = ctx.getContactId(); + TransportIndex i = ctx.getTransportIndex(); + // Update the connection window + ConnectionWindow w = db.getConnectionWindow(c, i); + w.setSeen(ctx.getConnectionNumber()); + db.setConnectionWindow(c, i, w); + // Update the set of expected IVs + Iterator<ConnectionContext> it = expected.values().iterator(); + while(it.hasNext()) { + ConnectionContext ctx1 = it.next(); + ContactId c1 = ctx1.getContactId(); + TransportIndex i1 = ctx1.getTransportIndex(); + if(c1.equals(c) && i1.equals(i)) it.remove(); } - newIvs.put(unseen, expectedIv); + SecretKey ivKey = crypto.deriveIncomingIvKey(db.getSharedSecret(c)); + calculateIvs(c, ctx.getTransportId(), i, ivKey, w); + } catch(NoSuchContactException e) { + // The contact was removed - clean up when we get the event } - contactToIvs.put(contactId, newIvs); - return contactId; + return ctx; } public void eventOccurred(DatabaseEvent e) { - // When the set of contacts changes we need to re-initialise everything - if(e instanceof ContactAddedEvent || e instanceof ContactRemovedEvent) { + if(e instanceof ContactRemovedEvent) { + // Remove the expected IVs for the ex-contact + removeIvs(((ContactRemovedEvent) e).getContactId()); + } else if(e instanceof TransportAddedEvent) { + // Calculate the expected IVs for the new transport + TransportId t = ((TransportAddedEvent) e).getTransportId(); + synchronized(this) { + if(!initialised) return; + try { + localTransportIds.add(t); + calculateIvs(t); + } catch(DbException e1) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning(e1.getMessage()); + } + } + } else if(e instanceof RemoteTransportsUpdatedEvent) { + // Remove and recalculate the expected IVs for the contact + ContactId c = ((RemoteTransportsUpdatedEvent) e).getContactId(); synchronized(this) { - initialised = false; + if(!initialised) return; + removeIvs(c); + try { + calculateIvs(c); + } catch(DbException e1) { + if(LOG.isLoggable(Level.WARNING)) + LOG.warning(e1.getMessage()); + } + } + } + } + + private synchronized void removeIvs(ContactId c) { + if(!initialised) return; + Iterator<ConnectionContext> it = expected.values().iterator(); + while(it.hasNext()) if(it.next().getContactId().equals(c)) it.remove(); + } + + private synchronized void calculateIvs(TransportId t) throws DbException { + for(ContactId c : db.getContacts()) { + try { + byte[] secret = db.getSharedSecret(c); + SecretKey ivKey = crypto.deriveIncomingIvKey(secret); + TransportIndex i = db.getRemoteIndex(c, t); + if(i != null) { + ConnectionWindow w = db.getConnectionWindow(c, i); + calculateIvs(c, t, i, ivKey, w); + } + } catch(NoSuchContactException e) { + // The contact was removed - clean up when we get the event } } } diff --git a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java index e899f7135da19fd2249d169b29b4db399f74f28e..cabf922762fd4d7c66e45aacd949f53465399d4e 100644 --- a/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java +++ b/components/net/sf/briar/transport/ConnectionWriterFactoryImpl.java @@ -9,8 +9,8 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.Mac; import javax.crypto.SecretKey; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; @@ -26,13 +26,14 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory { } public ConnectionWriter createConnectionWriter(OutputStream out, - long capacity, TransportId t, long connection, byte[] secret) { - return createConnectionWriter(out, capacity, true, t, connection, + long capacity, TransportIndex i, long connection, byte[] secret) { + return createConnectionWriter(out, capacity, true, i, connection, secret); } public ConnectionWriter createConnectionWriter(OutputStream out, - long capacity, TransportId t, byte[] encryptedIv, byte[] secret) { + long capacity, TransportIndex i, byte[] encryptedIv, + byte[] secret) { // Decrypt the IV Cipher ivCipher = crypto.getIvCipher(); SecretKey ivKey = crypto.deriveIncomingIvKey(secret); @@ -48,23 +49,23 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory { throw new RuntimeException(badKey); } // Validate the IV - if(!IvEncoder.validateIv(iv, true, t)) + if(!IvEncoder.validateIv(iv, true, i)) throw new IllegalArgumentException(); // Copy the connection number long connection = IvEncoder.getConnectionNumber(iv); - return createConnectionWriter(out, capacity, false, t, connection, + return createConnectionWriter(out, capacity, false, i, connection, secret); } private ConnectionWriter createConnectionWriter(OutputStream out, - long capacity, boolean initiator, TransportId t, long connection, + long capacity, boolean initiator, TransportIndex i, long connection, byte[] secret) { // Create the encrypter Cipher ivCipher = crypto.getIvCipher(); Cipher frameCipher = crypto.getFrameCipher(); SecretKey ivKey = crypto.deriveOutgoingIvKey(secret); SecretKey frameKey = crypto.deriveOutgoingFrameKey(secret); - byte[] iv = IvEncoder.encodeIv(initiator, t, connection); + byte[] iv = IvEncoder.encodeIv(initiator, i, connection); ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out, capacity, iv, ivCipher, frameCipher, ivKey, frameKey); // Create the writer diff --git a/components/net/sf/briar/transport/IvEncoder.java b/components/net/sf/briar/transport/IvEncoder.java index f0cae6fede17423e37c76b437b9aa5dc700990db..8bba94ba7c3043aacc2360cbcf215610705a65b3 100644 --- a/components/net/sf/briar/transport/IvEncoder.java +++ b/components/net/sf/briar/transport/IvEncoder.java @@ -1,18 +1,18 @@ package net.sf.briar.transport; import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.util.ByteUtils; class IvEncoder { - static byte[] encodeIv(boolean initiator, TransportId transport, + static byte[] encodeIv(boolean initiator, TransportIndex i, long connection) { byte[] iv = new byte[IV_LENGTH]; // Bit 31 is the initiator flag if(initiator) iv[3] = 1; // Encode the transport identifier as an unsigned 16-bit integer - ByteUtils.writeUint16(transport.getInt(), iv, 4); + ByteUtils.writeUint16(i.getInt(), iv, 4); // Encode the connection number as an unsigned 32-bit integer ByteUtils.writeUint32(connection, iv, 6); return iv; @@ -24,16 +24,16 @@ class IvEncoder { ByteUtils.writeUint32(frame, iv, 10); } - static boolean validateIv(byte[] iv, boolean initiator, TransportId t) { + static boolean validateIv(byte[] iv, boolean initiator, TransportIndex i) { if(iv.length != IV_LENGTH) return false; // Check that the reserved bits are all zero - for(int i = 0; i < 2; i++) if(iv[i] != 0) return false; + for(int j = 0; j < 2; j++) if(iv[j] != 0) return false; if(iv[3] != 0 && iv[3] != 1) return false; - for(int i = 10; i < iv.length; i++) if(iv[i] != 0) return false; + for(int j = 10; j < iv.length; j++) if(iv[j] != 0) return false; // Check that the initiator flag matches if(initiator != getInitiatorFlag(iv)) return false; // Check that the transport ID matches - if(t.getInt() != getTransportId(iv)) return false; + if(i.getInt() != getTransportId(iv)) return false; // The IV is valid return true; } diff --git a/components/net/sf/briar/transport/MacConsumer.java b/components/net/sf/briar/transport/MacConsumer.java deleted file mode 100644 index f494cf2a554c1fce2d42ba75241b5cc3d6d58829..0000000000000000000000000000000000000000 --- a/components/net/sf/briar/transport/MacConsumer.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.sf.briar.transport; - -import javax.crypto.Mac; - -import net.sf.briar.api.serial.Consumer; - -/** A consumer that passes its input through a MAC. */ -class MacConsumer implements Consumer { - - private final Mac mac; - - MacConsumer(Mac mac) { - this.mac = mac; - } - - public void write(byte b) { - mac.update(b); - } - - public void write(byte[] b, int off, int len) { - mac.update(b, off, len); - } -} diff --git a/components/net/sf/briar/transport/TransportModule.java b/components/net/sf/briar/transport/TransportModule.java index 5d7b4162cc64d57cf83b5d5b788e97db52fd1dee..867b4e39f1cb167a75a21f5cb04e846d799c704b 100644 --- a/components/net/sf/briar/transport/TransportModule.java +++ b/components/net/sf/briar/transport/TransportModule.java @@ -1,7 +1,8 @@ package net.sf.briar.transport; +import net.sf.briar.api.transport.ConnectionDispatcher; import net.sf.briar.api.transport.ConnectionReaderFactory; -import net.sf.briar.api.transport.ConnectionRecogniserFactory; +import net.sf.briar.api.transport.ConnectionRecogniser; import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.api.transport.ConnectionWriterFactory; @@ -11,10 +12,10 @@ public class TransportModule extends AbstractModule { @Override protected void configure() { + bind(ConnectionDispatcher.class).to(ConnectionDispatcherImpl.class); bind(ConnectionReaderFactory.class).to( ConnectionReaderFactoryImpl.class); - bind(ConnectionRecogniserFactory.class).to( - ConnectionRecogniserFactoryImpl.class); + bind(ConnectionRecogniser.class).to(ConnectionRecogniserImpl.class); bind(ConnectionWindowFactory.class).to( ConnectionWindowFactoryImpl.class); bind(ConnectionWriterFactory.class).to( diff --git a/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java b/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java index 79a53887553cc3a3bd95fbed02201ea8625f1814..c3329d8492a025e710a856a9f9882a6373a81d4d 100644 --- a/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java +++ b/components/net/sf/briar/transport/batch/BatchConnectionFactoryImpl.java @@ -1,9 +1,9 @@ package net.sf.briar.transport.batch; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.transport.BatchConnectionFactory; import net.sf.briar.api.transport.BatchTransportReader; @@ -33,10 +33,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory { this.protoWriterFactory = protoWriterFactory; } - public void createIncomingConnection(TransportId t, ContactId c, + public void createIncomingConnection(TransportIndex i, ContactId c, BatchTransportReader r, byte[] encryptedIv) { final IncomingBatchConnection conn = new IncomingBatchConnection( - connReaderFactory, db, protoReaderFactory, t, c, r, + connReaderFactory, db, protoReaderFactory, i, c, r, encryptedIv); Runnable read = new Runnable() { public void run() { @@ -46,10 +46,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory { new Thread(read).start(); } - public void createOutgoingConnection(TransportId t, ContactId c, + public void createOutgoingConnection(TransportIndex i, ContactId c, BatchTransportWriter w) { final OutgoingBatchConnection conn = new OutgoingBatchConnection( - connWriterFactory, db, protoWriterFactory, t, c, w); + connWriterFactory, db, protoWriterFactory, i, c, w); Runnable write = new Runnable() { public void run() { conn.write(); diff --git a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java index b48f62547b03a6561238570b66f608e724bc0e5e..b1bfd1966cf2232126b3ffcd8e3bd23bae39d240 100644 --- a/components/net/sf/briar/transport/batch/IncomingBatchConnection.java +++ b/components/net/sf/briar/transport/batch/IncomingBatchConnection.java @@ -6,7 +6,6 @@ import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.FormatException; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.protocol.Ack; @@ -14,6 +13,7 @@ import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.ConnectionReader; @@ -27,19 +27,19 @@ class IncomingBatchConnection { private final ConnectionReaderFactory connFactory; private final DatabaseComponent db; private final ProtocolReaderFactory protoFactory; - private final TransportId transportId; + private final TransportIndex transportIndex; private final ContactId contactId; private final BatchTransportReader reader; private final byte[] encryptedIv; IncomingBatchConnection(ConnectionReaderFactory connFactory, DatabaseComponent db, ProtocolReaderFactory protoFactory, - TransportId transportId, ContactId contactId, + TransportIndex transportIndex, ContactId contactId, BatchTransportReader reader, byte[] encryptedIv) { this.connFactory = connFactory; this.db = db; this.protoFactory = protoFactory; - this.transportId = transportId; + this.transportIndex = transportIndex; this.contactId = contactId; this.reader = reader; this.encryptedIv = encryptedIv; @@ -49,7 +49,8 @@ class IncomingBatchConnection { try { byte[] secret = db.getSharedSecret(contactId); ConnectionReader conn = connFactory.createConnectionReader( - reader.getInputStream(), transportId, encryptedIv, secret); + reader.getInputStream(), transportIndex, encryptedIv, + secret); ProtocolReader proto = protoFactory.createProtocolReader( conn.getInputStream()); // Read packets until EOF diff --git a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java index bc2e636ed513c37642982b398b7dfb14d8aeb1f5..59207076c3547c6d4b6acd6a4b9c5f4ffebdee0f 100644 --- a/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java +++ b/components/net/sf/briar/transport/batch/OutgoingBatchConnection.java @@ -8,9 +8,9 @@ import java.util.logging.Level; import java.util.logging.Logger; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; @@ -28,18 +28,18 @@ class OutgoingBatchConnection { private final ConnectionWriterFactory connFactory; private final DatabaseComponent db; private final ProtocolWriterFactory protoFactory; - private final TransportId transportId; + private final TransportIndex transportIndex; private final ContactId contactId; private final BatchTransportWriter writer; OutgoingBatchConnection(ConnectionWriterFactory connFactory, DatabaseComponent db, ProtocolWriterFactory protoFactory, - TransportId transportId, ContactId contactId, + TransportIndex transportIndex, ContactId contactId, BatchTransportWriter writer) { this.connFactory = connFactory; this.db = db; this.protoFactory = protoFactory; - this.transportId = transportId; + this.transportIndex = transportIndex; this.contactId = contactId; this.writer = writer; } @@ -47,10 +47,10 @@ class OutgoingBatchConnection { void write() { try { byte[] secret = db.getSharedSecret(contactId); - long connection = db.getConnectionNumber(contactId, transportId); + long connection = db.getConnectionNumber(contactId, transportIndex); ConnectionWriter conn = connFactory.createConnectionWriter( - writer.getOutputStream(), writer.getCapacity(), transportId, - connection, secret); + writer.getOutputStream(), writer.getCapacity(), + transportIndex, connection, secret); OutputStream out = conn.getOutputStream(); // There should be enough space for a packet long capacity = conn.getRemainingCapacity(); diff --git a/components/net/sf/briar/transport/stream/IncomingStreamConnection.java b/components/net/sf/briar/transport/stream/IncomingStreamConnection.java index 5b63487b423b3f2ba3927e9265c49b791ca6e238..3518b75c92b7775e32c8b915f7ff42ae0074409e 100644 --- a/components/net/sf/briar/transport/stream/IncomingStreamConnection.java +++ b/components/net/sf/briar/transport/stream/IncomingStreamConnection.java @@ -3,10 +3,10 @@ package net.sf.briar.transport.stream; import java.io.IOException; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReaderFactory; @@ -21,11 +21,12 @@ public class IncomingStreamConnection extends StreamConnection { IncomingStreamConnection(ConnectionReaderFactory connReaderFactory, ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ProtocolReaderFactory protoReaderFactory, - ProtocolWriterFactory protoWriterFactory, TransportId transportId, - ContactId contactId, StreamTransportConnection connection, + ProtocolWriterFactory protoWriterFactory, + TransportIndex transportIndex, ContactId contactId, + StreamTransportConnection connection, byte[] encryptedIv) { super(connReaderFactory, connWriterFactory, db, protoReaderFactory, - protoWriterFactory, transportId, contactId, connection); + protoWriterFactory, transportIndex, contactId, connection); this.encryptedIv = encryptedIv; } @@ -34,7 +35,8 @@ public class IncomingStreamConnection extends StreamConnection { IOException { byte[] secret = db.getSharedSecret(contactId); return connReaderFactory.createConnectionReader( - connection.getInputStream(), transportId, encryptedIv, secret); + connection.getInputStream(), transportIndex, encryptedIv, + secret); } @Override @@ -42,7 +44,7 @@ public class IncomingStreamConnection extends StreamConnection { IOException { byte[] secret = db.getSharedSecret(contactId); return connWriterFactory.createConnectionWriter( - connection.getOutputStream(), Long.MAX_VALUE, transportId, + connection.getOutputStream(), Long.MAX_VALUE, transportIndex, encryptedIv, secret); } } diff --git a/components/net/sf/briar/transport/stream/OutgoingStreamConnection.java b/components/net/sf/briar/transport/stream/OutgoingStreamConnection.java index 5b86b729b178c4fceb7c14a102a437a9b27223a6..923fcf354f88101ca01a6e5d5243d3c0683c4ba5 100644 --- a/components/net/sf/briar/transport/stream/OutgoingStreamConnection.java +++ b/components/net/sf/briar/transport/stream/OutgoingStreamConnection.java @@ -3,10 +3,10 @@ package net.sf.briar.transport.stream; import java.io.IOException; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReaderFactory; @@ -21,22 +21,25 @@ public class OutgoingStreamConnection extends StreamConnection { OutgoingStreamConnection(ConnectionReaderFactory connReaderFactory, ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ProtocolReaderFactory protoReaderFactory, - ProtocolWriterFactory protoWriterFactory, TransportId transportId, - ContactId contactId, StreamTransportConnection connection) { + ProtocolWriterFactory protoWriterFactory, + TransportIndex transportIndex, ContactId contactId, + StreamTransportConnection connection) { super(connReaderFactory, connWriterFactory, db, protoReaderFactory, - protoWriterFactory, transportId, contactId, connection); + protoWriterFactory, transportIndex, contactId, connection); } @Override protected ConnectionReader createConnectionReader() throws DbException, IOException { synchronized(this) { - if(connectionNum == -1L) - connectionNum = db.getConnectionNumber(contactId, transportId); + if(connectionNum == -1L) { + connectionNum = db.getConnectionNumber(contactId, + transportIndex); + } } byte[] secret = db.getSharedSecret(contactId); return connReaderFactory.createConnectionReader( - connection.getInputStream(), transportId, connectionNum, + connection.getInputStream(), transportIndex, connectionNum, secret); } @@ -44,12 +47,14 @@ public class OutgoingStreamConnection extends StreamConnection { protected ConnectionWriter createConnectionWriter() throws DbException, IOException { synchronized(this) { - if(connectionNum == -1L) - connectionNum = db.getConnectionNumber(contactId, transportId); + if(connectionNum == -1L) { + connectionNum = db.getConnectionNumber(contactId, + transportIndex); + } } byte[] secret = db.getSharedSecret(contactId); return connWriterFactory.createConnectionWriter( - connection.getOutputStream(), Long.MAX_VALUE, transportId, + connection.getOutputStream(), Long.MAX_VALUE, transportIndex, connectionNum, secret); } } diff --git a/components/net/sf/briar/transport/stream/StreamConnection.java b/components/net/sf/briar/transport/stream/StreamConnection.java index 51c2e1ee133c329c0ac24c6a509c8e08ea533318..126e69c4b4ee000d7bc2205c5e276ae8591287f3 100644 --- a/components/net/sf/briar/transport/stream/StreamConnection.java +++ b/components/net/sf/briar/transport/stream/StreamConnection.java @@ -12,16 +12,15 @@ import java.util.logging.Logger; import net.sf.briar.api.ContactId; import net.sf.briar.api.FormatException; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.event.BatchReceivedEvent; 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.MessagesAddedEvent; import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; -import net.sf.briar.api.db.event.TransportsUpdatedEvent; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.MessageId; @@ -30,6 +29,7 @@ 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.SubscriptionUpdate; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; @@ -56,7 +56,7 @@ abstract class StreamConnection implements DatabaseListener { protected final DatabaseComponent db; protected final ProtocolReaderFactory protoReaderFactory; protected final ProtocolWriterFactory protoWriterFactory; - protected final TransportId transportId; + protected final TransportIndex transportIndex; protected final ContactId contactId; protected final StreamTransportConnection connection; @@ -69,14 +69,15 @@ abstract class StreamConnection implements DatabaseListener { StreamConnection(ConnectionReaderFactory connReaderFactory, ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ProtocolReaderFactory protoReaderFactory, - ProtocolWriterFactory protoWriterFactory, TransportId transportId, - ContactId contactId, StreamTransportConnection connection) { + ProtocolWriterFactory protoWriterFactory, + TransportIndex transportIndex, ContactId contactId, + StreamTransportConnection connection) { this.connReaderFactory = connReaderFactory; this.connWriterFactory = connWriterFactory; this.db = db; this.protoReaderFactory = protoReaderFactory; this.protoWriterFactory = protoWriterFactory; - this.transportId = transportId; + this.transportIndex = transportIndex; this.contactId = contactId; this.connection = connection; } @@ -108,7 +109,7 @@ abstract class StreamConnection implements DatabaseListener { writerFlags |= Flags.SUBSCRIPTIONS_UPDATED; notifyAll(); } - } else if(e instanceof TransportsUpdatedEvent) { + } else if(e instanceof LocalTransportsUpdatedEvent) { writerFlags |= Flags.TRANSPORTS_UPDATED; notifyAll(); } diff --git a/components/net/sf/briar/transport/stream/StreamConnectionFactoryImpl.java b/components/net/sf/briar/transport/stream/StreamConnectionFactoryImpl.java index 21d300e46c14f5be114eb3d9c1bffcaebc4d2003..c3aecf5c54e4235b3337b3babad147d257a2bc96 100644 --- a/components/net/sf/briar/transport/stream/StreamConnectionFactoryImpl.java +++ b/components/net/sf/briar/transport/stream/StreamConnectionFactoryImpl.java @@ -1,9 +1,9 @@ package net.sf.briar.transport.stream; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionWriterFactory; @@ -32,11 +32,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory { this.protoWriterFactory = protoWriterFactory; } - public void createIncomingConnection(TransportId t, ContactId c, + public void createIncomingConnection(TransportIndex i, ContactId c, StreamTransportConnection s, byte[] encryptedIv) { final StreamConnection conn = new IncomingStreamConnection( connReaderFactory, connWriterFactory, db, protoReaderFactory, - protoWriterFactory, t, c, s, encryptedIv); + protoWriterFactory, i, c, s, encryptedIv); Runnable write = new Runnable() { public void run() { conn.write(); @@ -51,11 +51,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory { new Thread(read).start(); } - public void createOutgoingConnection(TransportId t, ContactId c, + public void createOutgoingConnection(TransportIndex i, ContactId c, StreamTransportConnection s) { final StreamConnection conn = new OutgoingStreamConnection( connReaderFactory, connWriterFactory, db, protoReaderFactory, - protoWriterFactory, t, c, s); + protoWriterFactory, i, c, s); Runnable write = new Runnable() { public void run() { conn.write(); diff --git a/test/net/sf/briar/ProtocolIntegrationTest.java b/test/net/sf/briar/ProtocolIntegrationTest.java index 3da633eba2e8ed87cc9f43377cdaa76d489ebfe2..9f47b2af6545392c20adf56bcb1919c90caf7285 100644 --- a/test/net/sf/briar/ProtocolIntegrationTest.java +++ b/test/net/sf/briar/ProtocolIntegrationTest.java @@ -15,8 +15,6 @@ import java.util.LinkedHashMap; import java.util.Map; import junit.framework.TestCase; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Author; @@ -26,17 +24,20 @@ import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageId; 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.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.protocol.writers.OfferWriter; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.protocol.writers.RequestWriter; @@ -52,6 +53,8 @@ import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.serial.SerialModule; import net.sf.briar.transport.TransportModule; +import net.sf.briar.transport.batch.TransportBatchModule; +import net.sf.briar.transport.stream.TransportStreamModule; import org.junit.Test; @@ -69,7 +72,7 @@ public class ProtocolIntegrationTest extends TestCase { private final ProtocolWriterFactory protocolWriterFactory; private final CryptoComponent crypto; private final byte[] aliceSecret, bobSecret; - private final TransportId transportId = new TransportId(123); + private final TransportIndex transportIndex = new TransportIndex(13); private final long connection = 12345L; private final Author author; private final Group group, group1; @@ -77,14 +80,15 @@ public class ProtocolIntegrationTest extends TestCase { private final String authorName = "Alice"; private final String subject = "Hello"; private final String messageBody = "Hello world"; - private final Map<TransportId, TransportProperties> transports; + private final Collection<Transport> transports; public ProtocolIntegrationTest() throws Exception { super(); Injector i = Guice.createInjector(new CryptoModule(), new DatabaseModule(), new ProtocolModule(), new ProtocolWritersModule(), new SerialModule(), - new TestDatabaseModule(), new TransportModule()); + new TestDatabaseModule(), new TransportBatchModule(), + new TransportModule(), new TransportStreamModule()); connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); @@ -120,9 +124,11 @@ public class ProtocolIntegrationTest extends TestCase { message3 = messageEncoder.encodeMessage(null, group1, groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(), subject, messageBody.getBytes("UTF-8")); - TransportProperties p = - new TransportProperties(Collections.singletonMap("bar", "baz")); - transports = Collections.singletonMap(transportId, p); + // Create some transports + TransportId transportId = new TransportId(TestUtils.getRandomId()); + Transport transport = new Transport(transportId, transportIndex, + Collections.singletonMap("bar", "baz")); + transports = Collections.singletonList(transport); } @Test @@ -134,7 +140,7 @@ public class ProtocolIntegrationTest extends TestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(); // Use Alice's secret for writing ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, - Long.MAX_VALUE, transportId, connection, aliceSecret); + Long.MAX_VALUE, transportIndex, connection, aliceSecret); OutputStream out1 = w.getOutputStream(); AckWriter a = protocolWriterFactory.createAckWriter(out1); @@ -188,7 +194,7 @@ public class ProtocolIntegrationTest extends TestCase { assertEquals(16, offset); // Use Bob's secret for reading ConnectionReader r = connectionReaderFactory.createConnectionReader(in, - transportId, encryptedIv, bobSecret); + transportIndex, encryptedIv, bobSecret); in = r.getInputStream(); ProtocolReader protocolReader = protocolReaderFactory.createProtocolReader(in); diff --git a/test/net/sf/briar/db/DatabaseComponentImplTest.java b/test/net/sf/briar/db/DatabaseComponentImplTest.java index a7771e174eb9272ef8bdda1193a7dbc9d2b8138b..0c0e2f45ee15dfa63c4a0f38b86e1ccf32629b29 100644 --- a/test/net/sf/briar/db/DatabaseComponentImplTest.java +++ b/test/net/sf/briar/db/DatabaseComponentImplTest.java @@ -48,7 +48,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest { oneOf(database).startTransaction(); will(returnValue(txn)); oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP); - will(returnValue(Collections.emptySet())); + will(returnValue(Collections.emptyList())); oneOf(database).commitTransaction(txn); // As if by magic, some free space has appeared oneOf(database).getFreeSpace(); diff --git a/test/net/sf/briar/db/DatabaseComponentTest.java b/test/net/sf/briar/db/DatabaseComponentTest.java index 8b9332217c5f6644146a383de7a7b8425941b7ed..8cabf9a2c1b1b07c0c4547b2508c89d74c18cd47 100644 --- a/test/net/sf/briar/db/DatabaseComponentTest.java +++ b/test/net/sf/briar/db/DatabaseComponentTest.java @@ -10,7 +10,6 @@ import junit.framework.TestCase; import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.MessageHeader; @@ -21,7 +20,7 @@ import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; -import net.sf.briar.api.db.event.TransportsUpdatedEvent; +import net.sf.briar.api.db.event.TransportAddedEvent; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.Batch; @@ -33,6 +32,9 @@ import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.ProtocolConstants; import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; @@ -61,7 +63,8 @@ public abstract class DatabaseComponentTest extends TestCase { private final Message message, privateMessage; private final Group group; private final TransportId transportId; - private final Map<TransportId, TransportProperties> transports; + private final TransportIndex localIndex, remoteIndex; + private final Collection<Transport> transports; private final Map<ContactId, TransportProperties> remoteProperties; private final byte[] secret; @@ -82,11 +85,15 @@ public abstract class DatabaseComponentTest extends TestCase { privateMessage = new TestMessage(messageId, null, null, null, subject, timestamp, raw); group = new TestGroup(groupId, "The really exciting group", null); - transportId = new TransportId(123); - TransportProperties p = - new TransportProperties(Collections.singletonMap("foo", "bar")); - transports = Collections.singletonMap(transportId, p); - remoteProperties = Collections.singletonMap(contactId, p); + transportId = new TransportId(TestUtils.getRandomId()); + localIndex = new TransportIndex(0); + remoteIndex = new TransportIndex(13); + TransportProperties properties = new TransportProperties( + Collections.singletonMap("foo", "bar")); + remoteProperties = Collections.singletonMap(contactId, properties); + Transport transport = new Transport(transportId, localIndex, + properties); + transports = Collections.singletonList(transport); secret = new byte[123]; } @@ -124,17 +131,17 @@ public abstract class DatabaseComponentTest extends TestCase { // setRating(authorId, Rating.GOOD) again oneOf(database).setRating(txn, authorId, Rating.GOOD); will(returnValue(Rating.GOOD)); - // addContact(transports) - oneOf(database).addContact(txn, transports, secret); + // addContact() + oneOf(database).addContact(txn, secret); will(returnValue(contactId)); oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class))); // getContacts() oneOf(database).getContacts(txn); will(returnValue(Collections.singletonList(contactId))); - // getConnectionWindow(contactId, 123) + // getConnectionWindow(contactId, 13) oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).getConnectionWindow(txn, contactId, transportId); + oneOf(database).getConnectionWindow(txn, contactId, remoteIndex); will(returnValue(connectionWindow)); // getSharedSecret(contactId) oneOf(database).containsContact(txn, contactId); @@ -170,10 +177,10 @@ public abstract class DatabaseComponentTest extends TestCase { // unsubscribe(groupId) again oneOf(database).containsSubscription(txn, groupId); will(returnValue(false)); - // setConnectionWindow(contactId, 123, connectionWindow) + // setConnectionWindow(contactId, 13, connectionWindow) oneOf(database).containsContact(txn, contactId); will(returnValue(true)); - oneOf(database).setConnectionWindow(txn, contactId, transportId, + oneOf(database).setConnectionWindow(txn, contactId, remoteIndex, connectionWindow); // removeContact(contactId) oneOf(database).removeContact(txn, contactId); @@ -189,10 +196,10 @@ public abstract class DatabaseComponentTest extends TestCase { assertEquals(Rating.UNRATED, db.getRating(authorId)); db.setRating(authorId, Rating.GOOD); // First time - listeners called db.setRating(authorId, Rating.GOOD); // Second time - not called - assertEquals(contactId, db.addContact(transports, secret)); + assertEquals(contactId, db.addContact(secret)); assertEquals(Collections.singletonList(contactId), db.getContacts()); assertEquals(connectionWindow, - db.getConnectionWindow(contactId, transportId)); + db.getConnectionWindow(contactId, remoteIndex)); assertEquals(secret, db.getSharedSecret(contactId)); assertEquals(remoteProperties, db.getRemoteProperties(transportId)); db.subscribe(group); // First time - listeners called @@ -201,7 +208,7 @@ public abstract class DatabaseComponentTest extends TestCase { assertEquals(Collections.singletonList(groupId), db.getSubscriptions()); db.unsubscribe(groupId); // First time - listeners called db.unsubscribe(groupId); // Second time - not called - db.setConnectionWindow(contactId, transportId, connectionWindow); + db.setConnectionWindow(contactId, remoteIndex, connectionWindow); db.removeContact(contactId); db.removeListener(listener); db.close(); @@ -540,7 +547,7 @@ public abstract class DatabaseComponentTest extends TestCase { } catch(NoSuchContactException expected) {} try { - db.getConnectionWindow(contactId, transportId); + db.getConnectionWindow(contactId, remoteIndex); fail(); } catch(NoSuchContactException expected) {} @@ -580,7 +587,7 @@ public abstract class DatabaseComponentTest extends TestCase { } catch(NoSuchContactException expected) {} try { - db.setConnectionWindow(contactId, transportId, null); + db.setConnectionWindow(contactId, remoteIndex, null); fail(); } catch(NoSuchContactException expected) {} @@ -1379,7 +1386,7 @@ public abstract class DatabaseComponentTest extends TestCase { with(any(long.class))); oneOf(database).commitTransaction(txn); oneOf(listener).eventOccurred(with(any( - TransportsUpdatedEvent.class))); + TransportAddedEvent.class))); }}); DatabaseComponent db = createDatabaseComponent(database, cleaner); diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java index 904b04714a8bdb426d952c47d75a0bb0e0abb8ca..e8d0248fa5251326b23109442fc774ee2cbed157 100644 --- a/test/net/sf/briar/db/H2DatabaseTest.java +++ b/test/net/sf/briar/db/H2DatabaseTest.java @@ -10,7 +10,6 @@ import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.Map; -import java.util.Map.Entry; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -22,7 +21,6 @@ import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.crypto.Password; import net.sf.briar.api.db.DbException; @@ -35,12 +33,18 @@ 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.MessageId; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.protocol.ProtocolModule; +import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.serial.SerialModule; import net.sf.briar.transport.TransportModule; +import net.sf.briar.transport.batch.TransportBatchModule; +import net.sf.briar.transport.stream.TransportStreamModule; import org.apache.commons.io.FileSystemUtils; import org.junit.After; @@ -75,17 +79,20 @@ public class H2DatabaseTest extends TestCase { private final Message message, privateMessage; private final Group group; private final TransportId transportId; + private final TransportIndex localIndex, remoteIndex; private final TransportProperties properties; - private final Map<TransportId, TransportProperties> transports; private final Map<ContactId, TransportProperties> remoteProperties; + private final Collection<Transport> remoteTransports; private final Map<Group, Long> subscriptions; private final byte[] secret; public H2DatabaseTest() throws Exception { super(); Injector i = Guice.createInjector(new CryptoModule(), - new DatabaseModule(), new ProtocolModule(), new SerialModule(), - new TransportModule(), new TestDatabaseModule(testDir)); + new DatabaseModule(), new ProtocolModule(), + new ProtocolWritersModule(), new SerialModule(), + new TransportBatchModule(), new TransportModule(), + new TransportStreamModule(), new TestDatabaseModule(testDir)); connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class); groupFactory = i.getInstance(GroupFactory.class); authorId = new AuthorId(TestUtils.getRandomId()); @@ -104,11 +111,15 @@ public class H2DatabaseTest extends TestCase { privateMessage = new TestMessage(privateMessageId, null, null, null, subject, timestamp, raw); group = groupFactory.createGroup(groupId, "Group name", null); - transportId = new TransportId(0); + transportId = new TransportId(TestUtils.getRandomId()); + localIndex = new TransportIndex(1); + remoteIndex = new TransportIndex(13); properties = new TransportProperties( Collections.singletonMap("foo", "bar")); - transports = Collections.singletonMap(transportId, properties); remoteProperties = Collections.singletonMap(contactId, properties); + Transport remoteTransport = new Transport(transportId, remoteIndex, + properties); + remoteTransports = Collections.singletonList(remoteTransport); subscriptions = Collections.singletonMap(group, 0L); secret = new byte[123]; } @@ -124,7 +135,7 @@ public class H2DatabaseTest extends TestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); assertFalse(db.containsContact(txn, contactId)); - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsSubscription(txn, groupId)); db.addSubscription(txn, group); @@ -142,8 +153,6 @@ public class H2DatabaseTest extends TestCase { db = open(true); txn = db.startTransaction(); assertTrue(db.containsContact(txn, contactId)); - assertEquals(remoteProperties, - db.getRemoteProperties(txn, transportId)); assertTrue(db.containsSubscription(txn, groupId)); assertTrue(db.containsMessage(txn, messageId)); byte[] raw1 = db.getMessage(txn, messageId); @@ -182,20 +191,20 @@ public class H2DatabaseTest extends TestCase { // Create three contacts assertFalse(db.containsContact(txn, contactId)); - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); assertTrue(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId1)); - assertEquals(contactId1, db.addContact(txn, transports, secret)); + assertEquals(contactId1, db.addContact(txn, secret)); assertTrue(db.containsContact(txn, contactId1)); assertFalse(db.containsContact(txn, contactId2)); - assertEquals(contactId2, db.addContact(txn, transports, secret)); + assertEquals(contactId2, db.addContact(txn, secret)); assertTrue(db.containsContact(txn, contactId2)); // Delete the contact with the highest ID db.removeContact(txn, contactId2); assertFalse(db.containsContact(txn, contactId2)); // Add another contact - a new ID should be created assertFalse(db.containsContact(txn, contactId3)); - assertEquals(contactId3, db.addContact(txn, transports, secret)); + assertEquals(contactId3, db.addContact(txn, secret)); assertTrue(db.containsContact(txn, contactId3)); db.commitTransaction(txn); @@ -242,7 +251,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addPrivateMessage(txn, privateMessage, contactId); // Removing the contact should remove the message @@ -261,7 +270,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addPrivateMessage(txn, privateMessage, contactId); // The message has no status yet, so it should not be sendable @@ -300,7 +309,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and store a private message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addPrivateMessage(txn, privateMessage, contactId); db.setStatus(txn, contactId, privateMessageId, Status.NEW); @@ -328,7 +337,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -366,7 +375,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -408,7 +417,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.addGroupMessage(txn, message); @@ -447,7 +456,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.addGroupMessage(txn, message); @@ -482,7 +491,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -513,7 +522,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, subscriptions, 1); db.addGroupMessage(txn, message); @@ -546,7 +555,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and some batches to ack - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId1); @@ -573,7 +582,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and receive the same batch twice - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId); @@ -599,7 +608,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); @@ -624,8 +633,8 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add two contacts, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); - ContactId contactId1 = db.addContact(txn, transports, secret); + assertEquals(contactId, db.addContact(txn, secret)); + ContactId contactId1 = db.addContact(txn, secret); db.addSubscription(txn, group); db.addGroupMessage(txn, message); @@ -647,7 +656,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -686,7 +695,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -731,12 +740,12 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { db.addOutstandingBatch(txn, contactId, ids[i], - Collections.<MessageId>emptySet()); + Collections.<MessageId>emptyList()); Thread.sleep(5); } @@ -771,12 +780,12 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); // Add some outstanding batches, a few ms apart for(int i = 0; i < ids.length; i++) { db.addOutstandingBatch(txn, contactId, ids[i], - Collections.<MessageId>emptySet()); + Collections.<MessageId>emptyList()); Thread.sleep(5); } @@ -991,44 +1000,56 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact with some transport properties - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); + db.setTransports(txn, contactId, remoteTransports, 1); assertEquals(remoteProperties, db.getRemoteProperties(txn, transportId)); // Replace the transport properties TransportProperties properties1 = new TransportProperties(Collections.singletonMap("baz", "bam")); - Map<TransportId, TransportProperties> transports1 = - Collections.singletonMap(transportId, properties1); + Transport remoteTransport1 = + new Transport(transportId, remoteIndex, properties1); + Collection<Transport> remoteTransports1 = + Collections.singletonList(remoteTransport1); Map<ContactId, TransportProperties> remoteProperties1 = Collections.singletonMap(contactId, properties1); - db.setTransports(txn, contactId, transports1, 1); + db.setTransports(txn, contactId, remoteTransports1, 2); assertEquals(remoteProperties1, db.getRemoteProperties(txn, transportId)); // Remove the transport properties - db.setTransports(txn, contactId, - Collections.<TransportId, TransportProperties>emptyMap(), 2); + db.setTransports(txn, contactId, Collections.<Transport>emptyList(), 3); assertEquals(Collections.emptyMap(), db.getRemoteProperties(txn, transportId)); + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testLocalTransports() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Allocate a transport index + assertEquals(localIndex, db.addTransport(txn, transportId)); + // Set the local transport properties - for(Entry<TransportId, TransportProperties> e : transports.entrySet()) { - db.setLocalProperties(txn, e.getKey(), e.getValue()); - } - assertEquals(transports, db.getLocalTransports(txn)); + db.setLocalProperties(txn, transportId, properties); + assertEquals(Collections.singletonList(properties), + db.getLocalTransports(txn)); - // Remove the local transport properties - for(TransportId t : transports.keySet()) { - db.setLocalProperties(txn, t, new TransportProperties()); - } - assertEquals(Collections.emptyMap(), db.getLocalTransports(txn)); + // Remove the local transport properties - the transport itself will + // not be removed + db.setLocalProperties(txn, transportId, new TransportProperties()); + assertEquals(Collections.singletonList(Collections.emptyMap()), + db.getLocalTransports(txn)); db.commitTransaction(txn); db.close(); } - @Test public void testUpdateTransportConfig() throws Exception { TransportConfig config = @@ -1039,6 +1060,9 @@ public class H2DatabaseTest extends TestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); + // Allocate a transport index + assertEquals(localIndex, db.addTransport(txn, transportId)); + // Set the transport config db.setConfig(txn, transportId, config); assertEquals(config, db.getConfig(txn, transportId)); @@ -1049,8 +1073,7 @@ public class H2DatabaseTest extends TestCase { // Remove the transport config db.setConfig(txn, transportId, new TransportConfig()); - assertEquals(Collections.emptyMap(), - db.getConfig(txn, transportId)); + assertEquals(Collections.emptyMap(), db.getConfig(txn, transportId)); db.commitTransaction(txn); db.close(); @@ -1062,27 +1085,32 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact with some transport properties - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); + db.setTransports(txn, contactId, remoteTransports, 1); assertEquals(remoteProperties, db.getRemoteProperties(txn, transportId)); // Replace the transport properties using a timestamp of 2 TransportProperties properties1 = new TransportProperties(Collections.singletonMap("baz", "bam")); - Map<TransportId, TransportProperties> transports1 = - Collections.singletonMap(transportId, properties1); + Transport remoteTransport1 = + new Transport(transportId, remoteIndex, properties1); + Collection<Transport> remoteTransports1 = + Collections.singletonList(remoteTransport1); Map<ContactId, TransportProperties> remoteProperties1 = Collections.singletonMap(contactId, properties1); - db.setTransports(txn, contactId, transports1, 2); + db.setTransports(txn, contactId, remoteTransports1, 2); assertEquals(remoteProperties1, db.getRemoteProperties(txn, transportId)); // Try to replace the transport properties using a timestamp of 1 TransportProperties properties2 = new TransportProperties(Collections.singletonMap("quux", "etc")); - Map<TransportId, TransportProperties> transports2 = - Collections.singletonMap(transportId, properties2); - db.setTransports(txn, contactId, transports2, 1); + Transport remoteTransport2 = + new Transport(transportId, remoteIndex, properties2); + Collection<Transport> remoteTransports2 = + Collections.singletonList(remoteTransport2); + db.setTransports(txn, contactId, remoteTransports2, 1); // The old properties should still be there assertEquals(remoteProperties1, @@ -1100,10 +1128,8 @@ public class H2DatabaseTest extends TestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); - // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); - - // Add some subscriptions + // Add a contact with some subscriptions + assertEquals(contactId, db.addContact(txn, secret)); db.setSubscriptions(txn, contactId, subscriptions, 1); assertEquals(Collections.singletonList(group), db.getSubscriptions(txn, contactId)); @@ -1127,10 +1153,8 @@ public class H2DatabaseTest extends TestCase { Database<Connection> db = open(false); Connection txn = db.startTransaction(); - // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); - - // Add some subscriptions + // Add a contact with some subscriptions + assertEquals(contactId, db.addContact(txn, secret)); db.setSubscriptions(txn, contactId, subscriptions, 2); assertEquals(Collections.singletonList(group), db.getSubscriptions(txn, contactId)); @@ -1154,7 +1178,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1172,7 +1196,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, subscriptions, 1); db.addGroupMessage(txn, message); @@ -1195,7 +1219,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setSubscriptions(txn, contactId, subscriptions, 1); db.addGroupMessage(txn, message); @@ -1218,7 +1242,7 @@ public class H2DatabaseTest extends TestCase { // Add a contact, subscribe to a group and store a message - // the message is older than the contact's subscription - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); Map<Group, Long> subs = Collections.singletonMap(group, timestamp + 1); @@ -1242,7 +1266,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1267,7 +1291,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1286,7 +1310,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact with a subscription - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.setSubscriptions(txn, contactId, subscriptions, 1); // There's no local subscription for the group @@ -1303,7 +1327,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); db.setStatus(txn, contactId, messageId, Status.NEW); @@ -1322,7 +1346,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.addGroupMessage(txn, message); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1342,7 +1366,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1364,7 +1388,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact, subscribe to a group and store a message - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setSubscriptions(txn, contactId, subscriptions, 1); @@ -1385,7 +1409,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); // The group should not be visible to the contact assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); @@ -1394,7 +1418,7 @@ public class H2DatabaseTest extends TestCase { assertEquals(Collections.singletonList(contactId), db.getVisibility(txn, groupId)); // Make the group invisible again - db.setVisibility(txn, groupId, Collections.<ContactId>emptySet()); + db.setVisibility(txn, groupId, Collections.<ContactId>emptyList()); assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); db.commitTransaction(txn); @@ -1408,10 +1432,10 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); - // Get the connection window for a new transport + assertEquals(contactId, db.addContact(txn, secret)); + // Get the connection window for a new index ConnectionWindow w = db.getConnectionWindow(txn, contactId, - transportId); + remoteIndex); // The connection window should exist and be in the initial state assertNotNull(w); assertEquals(0L, w.getCentre()); @@ -1427,19 +1451,19 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); - // Get the connection window for a new transport + assertEquals(contactId, db.addContact(txn, secret)); + // Get the connection window for a new index ConnectionWindow w = db.getConnectionWindow(txn, contactId, - transportId); + remoteIndex); // The connection window should exist and be in the initial state assertNotNull(w); assertEquals(0L, w.getCentre()); assertEquals(0, w.getBitmap()); // Update the connection window and store it w.setSeen(5L); - db.setConnectionWindow(txn, contactId, transportId, w); + db.setConnectionWindow(txn, contactId, remoteIndex, w); // Check that the connection window was stored - w = db.getConnectionWindow(txn, contactId, transportId); + w = db.getConnectionWindow(txn, contactId, remoteIndex); assertNotNull(w); assertEquals(6L, w.getCentre()); assertTrue(w.isSeen(5L)); @@ -1527,7 +1551,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); // A message with a private parent should return null @@ -1576,7 +1600,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); // The subscription and transport timestamps should be initialised to 0 assertEquals(0L, db.getSubscriptionsModified(txn, contactId)); @@ -1606,7 +1630,7 @@ public class H2DatabaseTest extends TestCase { Connection txn = db.startTransaction(); // Add a contact and subscribe to a group - assertEquals(contactId, db.addContact(txn, transports, secret)); + assertEquals(contactId, db.addContact(txn, secret)); db.addSubscription(txn, group); // Store a couple of messages diff --git a/test/net/sf/briar/db/TestGroup.java b/test/net/sf/briar/db/TestGroup.java index eac3763807fced4d9333c3b8ae2aaafbf829d39d..40617162e27e380f46610f0a5e8e78e14279c893 100644 --- a/test/net/sf/briar/db/TestGroup.java +++ b/test/net/sf/briar/db/TestGroup.java @@ -1,10 +1,7 @@ package net.sf.briar.db; -import java.io.IOException; - import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupId; -import net.sf.briar.api.serial.Writer; public class TestGroup implements Group { @@ -29,8 +26,4 @@ public class TestGroup implements Group { public byte[] getPublicKey() { return publicKey; } - - public void writeTo(Writer w) throws IOException { - throw new UnsupportedOperationException(); - } } diff --git a/test/net/sf/briar/invitation/InvitationWorkerTest.java b/test/net/sf/briar/invitation/InvitationWorkerTest.java index 524412b0e6976d23a39a4a029b577a01dce47589..e6dc78b87dabc4f11386ac26be8b4e28cb1b96b6 100644 --- a/test/net/sf/briar/invitation/InvitationWorkerTest.java +++ b/test/net/sf/briar/invitation/InvitationWorkerTest.java @@ -4,18 +4,19 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; import junit.framework.TestCase; import net.sf.briar.TestUtils; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; import net.sf.briar.api.invitation.InvitationCallback; import net.sf.briar.api.invitation.InvitationParameters; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.WriterFactory; @@ -109,11 +110,12 @@ public class InvitationWorkerTest extends TestCase { private void testInstallerCreation(final boolean createExe, final boolean createJar) throws IOException, DbException { - TransportProperties properties = - new TransportProperties(Collections.singletonMap("foo", "bar")); - TransportId transportId = new TransportId(123); - final Map<TransportId, TransportProperties> transports = - Collections.singletonMap(transportId, properties); + TransportId transportId = new TransportId(TestUtils.getRandomId()); + TransportIndex transportIndex = new TransportIndex(13); + Transport transport = new Transport(transportId, transportIndex, + Collections.singletonMap("foo", "bar")); + final Collection<Transport> transports = + Collections.singletonList(transport); final File setup = new File(testDir, "setup.dat"); TestUtils.createFile(setup, "foo bar baz"); final File invitation = new File(testDir, "invitation.dat"); @@ -148,7 +150,7 @@ public class InvitationWorkerTest extends TestCase { will(returnValue(transports)); oneOf(writerFactory).createWriter(with(any(OutputStream.class))); will(returnValue(writer)); - oneOf(writer).writeMap(transports); + oneOf(writer).writeList(transports); oneOf(params).shouldCreateExe(); will(returnValue(createExe)); oneOf(params).shouldCreateJar(); diff --git a/test/net/sf/briar/plugins/PluginManagerImplTest.java b/test/net/sf/briar/plugins/PluginManagerImplTest.java index 373e0678ef61263cf1dc437844ec3c72b6460529..a9f89b050dc5e3fef122bd57cf98911c30a8b8e8 100644 --- a/test/net/sf/briar/plugins/PluginManagerImplTest.java +++ b/test/net/sf/briar/plugins/PluginManagerImplTest.java @@ -1,11 +1,12 @@ package net.sf.briar.plugins; -import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicInteger; import junit.framework.TestCase; -import net.sf.briar.api.TransportId; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionDispatcher; import net.sf.briar.api.ui.UiCallback; @@ -22,7 +23,12 @@ public class PluginManagerImplTest extends TestCase { final ConnectionDispatcher dispatcher = context.mock(ConnectionDispatcher.class); final UiCallback uiCallback = context.mock(UiCallback.class); + final AtomicInteger index = new AtomicInteger(0); context.checking(new Expectations() {{ + allowing(db).getLocalIndex(with(any(TransportId.class))); + will(returnValue(null)); + allowing(db).addTransport(with(any(TransportId.class))); + will(returnValue(new TransportIndex(index.getAndIncrement()))); allowing(db).getLocalProperties(with(any(TransportId.class))); will(returnValue(new TransportProperties())); allowing(db).getRemoteProperties(with(any(TransportId.class))); @@ -30,10 +36,9 @@ public class PluginManagerImplTest extends TestCase { allowing(db).setLocalProperties(with(any(TransportId.class)), with(any(TransportProperties.class))); }}); - Executor executor = new ImmediateExecutor(); Poller poller = new PollerImpl(); - PluginManagerImpl p = - new PluginManagerImpl(executor, db, poller, dispatcher, uiCallback); + PluginManagerImpl p = new PluginManagerImpl(db, poller, dispatcher, + uiCallback); // The Bluetooth plugin will not start without a Bluetooth device, so // we expect two plugins to be started assertEquals(2, p.startPlugins()); diff --git a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java index 3cf909647896322670a2be6a81d57cc0a18b4bb9..bb174a80b05af35697077e1f234034d96cf15951 100644 --- a/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java +++ b/test/net/sf/briar/plugins/file/RemovableDrivePluginTest.java @@ -1,5 +1,7 @@ package net.sf.briar.plugins.file; +import static org.junit.Assert.assertArrayEquals; + import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; @@ -47,8 +49,8 @@ public class RemovableDrivePluginTest extends TestCase { RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, callback, finder, monitor); - assertEquals(RemovableDrivePlugin.TRANSPORT_ID, - plugin.getId().getInt()); + assertArrayEquals(RemovableDrivePlugin.TRANSPORT_ID, + plugin.getId().getBytes()); context.assertIsSatisfied(); } diff --git a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java index c502c27b081bea45422513aacc053a6243b430d5..5efbfdc1aa6f471614be768e8f07c51f14ce8bc0 100644 --- a/test/net/sf/briar/protocol/ProtocolReadWriteTest.java +++ b/test/net/sf/briar/protocol/ProtocolReadWriteTest.java @@ -3,28 +3,30 @@ package net.sf.briar.protocol; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.BitSet; +import java.util.Collection; import java.util.Collections; import java.util.Map; import junit.framework.TestCase; import net.sf.briar.TestUtils; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageEncoder; 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.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.protocol.writers.OfferWriter; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.protocol.writers.RequestWriter; @@ -50,8 +52,7 @@ public class ProtocolReadWriteTest extends TestCase { private final String messageBody = "Hello world"; private final BitSet bitSet; private final Map<Group, Long> subscriptions; - private final TransportId transportId; - private final Map<TransportId, TransportProperties> transports; + private final Collection<Transport> transports; private final long timestamp = System.currentTimeMillis(); public ProtocolReadWriteTest() throws Exception { @@ -71,10 +72,11 @@ public class ProtocolReadWriteTest extends TestCase { bitSet.set(3); bitSet.set(7); subscriptions = Collections.singletonMap(group, 123L); - transportId = new TransportId(123); - TransportProperties p = - new TransportProperties(Collections.singletonMap("bar", "baz")); - transports = Collections.singletonMap(transportId, p); + TransportId transportId = new TransportId(TestUtils.getRandomId()); + TransportIndex transportIndex = new TransportIndex(13); + Transport transport = new Transport(transportId, transportIndex, + Collections.singletonMap("bar", "baz")); + transports = Collections.singletonList(transport); } @Test diff --git a/test/net/sf/briar/protocol/writers/ConstantsTest.java b/test/net/sf/briar/protocol/writers/ConstantsTest.java index 372d32dc48d0b551bc328a46350c8b21a1d50798..d158040b39435f3e1796c11502731bc5efa9c740 100644 --- a/test/net/sf/briar/protocol/writers/ConstantsTest.java +++ b/test/net/sf/briar/protocol/writers/ConstantsTest.java @@ -1,14 +1,25 @@ package net.sf.briar.protocol.writers; +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_GROUPS; +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.HashMap; import java.util.Map; import junit.framework.TestCase; import net.sf.briar.TestUtils; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.AuthorFactory; @@ -16,14 +27,14 @@ import net.sf.briar.api.protocol.BatchId; import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.MessageId; -import net.sf.briar.api.protocol.ProtocolConstants; -import net.sf.briar.api.protocol.SubscriptionUpdate; -import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.BatchWriter; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.protocol.writers.OfferWriter; import net.sf.briar.api.protocol.writers.SubscriptionWriter; import net.sf.briar.api.protocol.writers.TransportWriter; @@ -50,7 +61,8 @@ public class ConstantsTest extends TestCase { public ConstantsTest() throws Exception { super(); Injector i = Guice.createInjector(new CryptoModule(), - new ProtocolModule(), new SerialModule()); + new ProtocolModule(), new ProtocolWritersModule(), + new SerialModule()); writerFactory = i.getInstance(WriterFactory.class); crypto = i.getInstance(CryptoComponent.class); serial = i.getInstance(SerialComponent.class); @@ -61,7 +73,7 @@ public class ConstantsTest extends TestCase { @Test public void testBatchesFitIntoLargeAck() throws Exception { - testBatchesFitIntoAck(ProtocolConstants.MAX_PACKET_LENGTH); + testBatchesFitIntoAck(MAX_PACKET_LENGTH); } @Test @@ -94,32 +106,32 @@ public class ConstantsTest extends TestCase { @Test public void testMessageFitsIntoBatch() throws Exception { // Create a maximum-length group - String groupName = createRandomString(Group.MAX_NAME_LENGTH); - byte[] groupPublic = new byte[Group.MAX_PUBLIC_KEY_LENGTH]; + String groupName = createRandomString(MAX_GROUP_NAME_LENGTH); + byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH]; Group group = groupFactory.createGroup(groupName, groupPublic); // Create a maximum-length author - String authorName = createRandomString(Author.MAX_NAME_LENGTH); - byte[] authorPublic = new byte[Author.MAX_PUBLIC_KEY_LENGTH]; + String authorName = createRandomString(MAX_AUTHOR_NAME_LENGTH); + byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH]; Author author = authorFactory.createAuthor(authorName, authorPublic); // Create a maximum-length message PrivateKey groupPrivate = crypto.generateKeyPair().getPrivate(); PrivateKey authorPrivate = crypto.generateKeyPair().getPrivate(); - String subject = createRandomString(Message.MAX_SUBJECT_LENGTH); - byte[] body = new byte[Message.MAX_BODY_LENGTH]; + String subject = createRandomString(MAX_SUBJECT_LENGTH); + byte[] body = new byte[MAX_BODY_LENGTH]; Message message = messageEncoder.encodeMessage(null, group, groupPrivate, author, authorPrivate, subject, body); // Add the message to a batch - ByteArrayOutputStream out = new ByteArrayOutputStream( - ProtocolConstants.MAX_PACKET_LENGTH); + ByteArrayOutputStream out = + new ByteArrayOutputStream(MAX_PACKET_LENGTH); BatchWriter b = new BatchWriterImpl(out, serial, writerFactory, crypto.getMessageDigest()); assertTrue(b.writeMessage(message.getSerialised())); b.finish(); // Check the size of the serialised batch - assertTrue(out.size() > UniqueId.LENGTH + Group.MAX_NAME_LENGTH + - Group.MAX_PUBLIC_KEY_LENGTH + Author.MAX_NAME_LENGTH + - Author.MAX_PUBLIC_KEY_LENGTH + Message.MAX_BODY_LENGTH); - assertTrue(out.size() <= ProtocolConstants.MAX_PACKET_LENGTH); + assertTrue(out.size() > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH + + MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH + + MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH); + assertTrue(out.size() <= MAX_PACKET_LENGTH); } @Test @@ -136,7 +148,7 @@ public class ConstantsTest extends TestCase { @Test public void testMessagesFitIntoLargeOffer() throws Exception { - testMessagesFitIntoOffer(ProtocolConstants.MAX_PACKET_LENGTH); + testMessagesFitIntoOffer(MAX_PACKET_LENGTH); } @Test @@ -169,52 +181,49 @@ public class ConstantsTest extends TestCase { @Test public void testSubscriptionsFitIntoUpdate() throws Exception { // Create the maximum number of maximum-length subscriptions - Map<Group, Long> subs = - new HashMap<Group, Long>(SubscriptionUpdate.MAX_SUBS_PER_UPDATE); - byte[] publicKey = new byte[Group.MAX_PUBLIC_KEY_LENGTH]; - for(int i = 0; i < SubscriptionUpdate.MAX_SUBS_PER_UPDATE; i++) { - String name = createRandomString(Group.MAX_NAME_LENGTH); + Map<Group, Long> subs = new HashMap<Group, Long>(MAX_GROUPS); + byte[] publicKey = new byte[MAX_PUBLIC_KEY_LENGTH]; + for(int i = 0; i < MAX_GROUPS; i++) { + String name = createRandomString(MAX_GROUP_NAME_LENGTH); Group group = groupFactory.createGroup(name, publicKey); subs.put(group, Long.MAX_VALUE); } // Add the subscriptions to an update - ByteArrayOutputStream out = new ByteArrayOutputStream( - ProtocolConstants.MAX_PACKET_LENGTH); + ByteArrayOutputStream out = + new ByteArrayOutputStream(MAX_PACKET_LENGTH); SubscriptionWriter s = new SubscriptionWriterImpl(out, writerFactory); s.writeSubscriptions(subs, Long.MAX_VALUE); // Check the size of the serialised update - assertTrue(out.size() > SubscriptionUpdate.MAX_SUBS_PER_UPDATE * - (Group.MAX_NAME_LENGTH + Group.MAX_PUBLIC_KEY_LENGTH + 8) + 8); - assertTrue(out.size() <= ProtocolConstants.MAX_PACKET_LENGTH); + assertTrue(out.size() > MAX_GROUPS * + (MAX_GROUP_NAME_LENGTH + MAX_PUBLIC_KEY_LENGTH + 8) + 8); + assertTrue(out.size() <= MAX_PACKET_LENGTH); } @Test public void testTransportsFitIntoUpdate() throws Exception { // Create the maximum number of plugins, each with the maximum number // of maximum-length properties - Map<TransportId, TransportProperties> transports = - new HashMap<TransportId, TransportProperties>(); - for(int i = 0; i < TransportUpdate.MAX_PLUGINS_PER_UPDATE; i++) { - TransportProperties p = new TransportProperties(); - for(int j = 0; j < TransportUpdate.MAX_PROPERTIES_PER_PLUGIN; j++) { - String key = createRandomString( - TransportUpdate.MAX_KEY_OR_VALUE_LENGTH); - String value = createRandomString( - TransportUpdate.MAX_KEY_OR_VALUE_LENGTH); - p.put(key, value); + Collection<Transport> transports = new ArrayList<Transport>(); + for(int i = 0; i < MAX_TRANSPORTS; i++) { + TransportId id = new TransportId(TestUtils.getRandomId()); + TransportIndex index = new TransportIndex(i); + Transport t = new Transport(id, index); + for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) { + String key = createRandomString(MAX_PROPERTY_LENGTH); + String value = createRandomString(MAX_PROPERTY_LENGTH); + t.put(key, value); } - transports.put(new TransportId(i), p); + transports.add(t); } // Add the transports to an update - ByteArrayOutputStream out = new ByteArrayOutputStream( - ProtocolConstants.MAX_PACKET_LENGTH); + ByteArrayOutputStream out = + new ByteArrayOutputStream(MAX_PACKET_LENGTH); TransportWriter t = new TransportWriterImpl(out, writerFactory); t.writeTransports(transports, Long.MAX_VALUE); // Check the size of the serialised update - assertTrue(out.size() > TransportUpdate.MAX_PLUGINS_PER_UPDATE * - (4 + TransportUpdate.MAX_PROPERTIES_PER_PLUGIN * - TransportUpdate.MAX_KEY_OR_VALUE_LENGTH * 2) + 8); - assertTrue(out.size() <= ProtocolConstants.MAX_PACKET_LENGTH); + 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 { diff --git a/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java b/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java index e017074d3ea52841cb25e35d8cba5dd6da21f803..93ed7180c8b3ab74220536469acb6237046c72f7 100644 --- a/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java +++ b/test/net/sf/briar/protocol/writers/RequestWriterImplTest.java @@ -42,9 +42,9 @@ public class RequestWriterImplTest extends TestCase { b.set(12); b.set(15); r.writeRequest(b, 16); - // Short user tag 10, short bytes with length 2, 0xD959 + // Short user tag 8, short bytes with length 2, 0xD959 byte[] output = out.toByteArray(); - assertEquals("CA" + "92" + "D959", StringUtils.toHexString(output)); + assertEquals("C8" + "92" + "D959", StringUtils.toHexString(output)); } @Test @@ -63,8 +63,8 @@ public class RequestWriterImplTest extends TestCase { b.set(11); b.set(12); r.writeRequest(b, 13); - // Short user tag 10, short bytes with length 2, 0x59D8 + // Short user tag 8, short bytes with length 2, 0x59D8 byte[] output = out.toByteArray(); - assertEquals("CA" + "92" + "59D8", StringUtils.toHexString(output)); + assertEquals("C8" + "92" + "59D8", StringUtils.toHexString(output)); } } diff --git a/test/net/sf/briar/serial/WriterImplTest.java b/test/net/sf/briar/serial/WriterImplTest.java index 8bd773a2e9cc1a3fe4a4a4e8b3de55edd7567a9c..7fe9b285072144c97dad2a30b367d690715b2404 100644 --- a/test/net/sf/briar/serial/WriterImplTest.java +++ b/test/net/sf/briar/serial/WriterImplTest.java @@ -4,14 +4,11 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import junit.framework.TestCase; -import net.sf.briar.api.serial.Writable; -import net.sf.briar.api.serial.Writer; import net.sf.briar.util.StringUtils; import org.junit.Before; @@ -284,20 +281,6 @@ public class WriterImplTest extends TestCase { checkContents("EF" + "20" + "EF" + "FF"); } - @Test - public void testWriteCollectionOfWritables() throws IOException { - Writable writable = new Writable() { - public void writeTo(Writer w) throws IOException { - w.writeUserDefinedId(0); - w.writeString("foo"); - } - }; - w.writeList(Collections.singleton(writable)); - // SHORT_LIST tag, length 1, SHORT_USER tag (3 bits), 0 (5 bits), - // "foo" as short string - checkContents("A" + "1" + "C0" + "83666F6F"); - } - private void checkContents(String hex) throws IOException { out.flush(); out.close(); diff --git a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java index dc3cbb4c33ccb4ba49bcde2ad59828a2d59b373d..20361bac56f93cdf7492eb290efcf760a4fcb82c 100644 --- a/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionDecrypterImplTest.java @@ -11,8 +11,8 @@ import javax.crypto.spec.IvParameterSpec; import junit.framework.TestCase; import net.sf.briar.TestUtils; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.crypto.CryptoModule; import org.apache.commons.io.output.ByteArrayOutputStream; @@ -27,7 +27,7 @@ public class ConnectionDecrypterImplTest extends TestCase { private final Cipher ivCipher, frameCipher; private final SecretKey ivKey, frameKey; - private final TransportId transportId = new TransportId(123); + private final TransportIndex transportIndex = new TransportIndex(13); private final long connection = 12345L; public ConnectionDecrypterImplTest() { @@ -52,7 +52,7 @@ public class ConnectionDecrypterImplTest extends TestCase { private void testDecryption(boolean initiator) throws Exception { // Calculate the plaintext and ciphertext for the IV - byte[] iv = IvEncoder.encodeIv(initiator, transportId, connection); + byte[] iv = IvEncoder.encodeIv(initiator, transportIndex, connection); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); byte[] encryptedIv = ivCipher.doFinal(iv); assertEquals(IV_LENGTH, encryptedIv.length); @@ -85,7 +85,7 @@ public class ConnectionDecrypterImplTest extends TestCase { ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); // Use a ConnectionDecrypter to decrypt the ciphertext ConnectionDecrypter d = new ConnectionDecrypterImpl(in, - IvEncoder.encodeIv(initiator, transportId, connection), + IvEncoder.encodeIv(initiator, transportIndex, connection), frameCipher, frameKey); // First frame byte[] decrypted = new byte[ciphertext.length]; diff --git a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java index 3f81230dbcdaa7c92fe1ab1cb493845850079e95..9f191da7f2e67880f2bdb4002ed9de1ea04f2547 100644 --- a/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java +++ b/test/net/sf/briar/transport/ConnectionEncrypterImplTest.java @@ -10,8 +10,8 @@ import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import junit.framework.TestCase; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.crypto.CryptoModule; import org.junit.Test; @@ -25,7 +25,7 @@ public class ConnectionEncrypterImplTest extends TestCase { private final Cipher ivCipher, frameCipher; private final SecretKey ivKey, frameKey; - private final TransportId transportId = new TransportId(123); + private final TransportIndex transportIndex = new TransportIndex(13); private final long connection = 12345L; public ConnectionEncrypterImplTest() { @@ -50,7 +50,7 @@ public class ConnectionEncrypterImplTest extends TestCase { private void testEncryption(boolean initiator) throws Exception { // Calculate the expected ciphertext for the IV - byte[] iv = IvEncoder.encodeIv(initiator, transportId, connection); + byte[] iv = IvEncoder.encodeIv(initiator, transportIndex, connection); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); byte[] encryptedIv = ivCipher.doFinal(iv); assertEquals(IV_LENGTH, encryptedIv.length); @@ -82,7 +82,7 @@ public class ConnectionEncrypterImplTest extends TestCase { byte[] expected = out.toByteArray(); // Use a ConnectionEncrypter to encrypt the plaintext out.reset(); - iv = IvEncoder.encodeIv(initiator, transportId, connection); + iv = IvEncoder.encodeIv(initiator, transportIndex, connection); ConnectionEncrypter e = new ConnectionEncrypterImpl(out, Long.MAX_VALUE, iv, ivCipher, frameCipher, ivKey, frameKey); e.getOutputStream().write(plaintext); diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java index bd5d1b0cef48542f557d7ccc3b636883353137de..b95460a59e29c5bb0aefeacbab3d8a4a1bb58f25 100644 --- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java +++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java @@ -9,10 +9,14 @@ import javax.crypto.Cipher; import javax.crypto.SecretKey; import junit.framework.TestCase; +import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.crypto.CryptoModule; @@ -29,6 +33,8 @@ public class ConnectionRecogniserImplTest extends TestCase { private final ContactId contactId; private final byte[] secret; private final TransportId transportId; + private final TransportIndex localIndex, remoteIndex; + private final Collection<Transport> transports; private final ConnectionWindow connectionWindow; public ConnectionRecogniserImplTest() { @@ -37,7 +43,12 @@ public class ConnectionRecogniserImplTest extends TestCase { crypto = i.getInstance(CryptoComponent.class); contactId = new ContactId(1); secret = new byte[18]; - transportId = new TransportId(123); + transportId = new TransportId(TestUtils.getRandomId()); + localIndex = new TransportIndex(13); + remoteIndex = new TransportIndex(7); + Transport transport = new Transport(transportId, localIndex, + Collections.singletonMap("foo", "bar")); + transports = Collections.singletonList(transport); connectionWindow = new ConnectionWindowImpl(0L, 0); } @@ -47,15 +58,20 @@ public class ConnectionRecogniserImplTest extends TestCase { final DatabaseComponent db = context.mock(DatabaseComponent.class); context.checking(new Expectations() {{ oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); + // Initialise + oneOf(db).getLocalTransports(); + will(returnValue(transports)); oneOf(db).getContacts(); will(returnValue(Collections.singleton(contactId))); oneOf(db).getSharedSecret(contactId); will(returnValue(secret)); - oneOf(db).getConnectionWindow(contactId, transportId); + oneOf(db).getRemoteIndex(contactId, transportId); + will(returnValue(remoteIndex)); + oneOf(db).getConnectionWindow(contactId, remoteIndex); will(returnValue(connectionWindow)); }}); final ConnectionRecogniserImpl c = - new ConnectionRecogniserImpl(transportId, crypto, db); + new ConnectionRecogniserImpl(crypto, db); assertNull(c.acceptConnection(new byte[IV_LENGTH])); context.assertIsSatisfied(); } @@ -66,26 +82,41 @@ public class ConnectionRecogniserImplTest extends TestCase { SecretKey ivKey = crypto.deriveIncomingIvKey(secret); Cipher ivCipher = crypto.getIvCipher(); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); - byte[] iv = IvEncoder.encodeIv(true, transportId, 3L); + byte[] iv = IvEncoder.encodeIv(true, remoteIndex, 3L); byte[] encryptedIv = ivCipher.doFinal(iv); Mockery context = new Mockery(); final DatabaseComponent db = context.mock(DatabaseComponent.class); context.checking(new Expectations() {{ oneOf(db).addListener(with(any(ConnectionRecogniserImpl.class))); + // Initialise + oneOf(db).getLocalTransports(); + will(returnValue(transports)); oneOf(db).getContacts(); will(returnValue(Collections.singleton(contactId))); oneOf(db).getSharedSecret(contactId); will(returnValue(secret)); - oneOf(db).getConnectionWindow(contactId, transportId); + oneOf(db).getRemoteIndex(contactId, transportId); + will(returnValue(remoteIndex)); + oneOf(db).getConnectionWindow(contactId, remoteIndex); will(returnValue(connectionWindow)); - oneOf(db).setConnectionWindow(contactId, transportId, + // Update the window + oneOf(db).getConnectionWindow(contactId, remoteIndex); + will(returnValue(connectionWindow)); + oneOf(db).setConnectionWindow(contactId, remoteIndex, connectionWindow); + oneOf(db).getSharedSecret(contactId); + will(returnValue(secret)); }}); final ConnectionRecogniserImpl c = - new ConnectionRecogniserImpl(transportId, crypto, db); + new ConnectionRecogniserImpl(crypto, db); // First time - the IV should be expected - assertEquals(contactId, c.acceptConnection(encryptedIv)); + ConnectionContext ctx = c.acceptConnection(encryptedIv); + assertNotNull(ctx); + assertEquals(contactId, ctx.getContactId()); + assertEquals(transportId, ctx.getTransportId()); + assertEquals(remoteIndex, ctx.getTransportIndex()); + assertEquals(3L, ctx.getConnectionNumber()); // Second time - the IV should no longer be expected assertNull(c.acceptConnection(encryptedIv)); // The window should have advanced diff --git a/test/net/sf/briar/transport/ConnectionWriterTest.java b/test/net/sf/briar/transport/ConnectionWriterTest.java index 6640628089f85bb8726e1bdedff954736819ffad..e62318701783b21c83ac2eefbbced5b35cd7dd42 100644 --- a/test/net/sf/briar/transport/ConnectionWriterTest.java +++ b/test/net/sf/briar/transport/ConnectionWriterTest.java @@ -7,13 +7,16 @@ import java.io.ByteArrayOutputStream; import junit.framework.TestCase; import net.sf.briar.TestDatabaseModule; -import net.sf.briar.api.TransportId; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.db.DatabaseModule; import net.sf.briar.protocol.ProtocolModule; +import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.serial.SerialModule; +import net.sf.briar.transport.batch.TransportBatchModule; +import net.sf.briar.transport.stream.TransportStreamModule; import org.junit.Test; @@ -24,14 +27,16 @@ public class ConnectionWriterTest extends TestCase { private final ConnectionWriterFactory connectionWriterFactory; private final byte[] secret = new byte[100]; - private final TransportId transportId = new TransportId(123); + private final TransportIndex transportIndex = new TransportIndex(13); private final long connection = 12345L; public ConnectionWriterTest() throws Exception { super(); Injector i = Guice.createInjector(new CryptoModule(), - new DatabaseModule(), new ProtocolModule(), new SerialModule(), - new TestDatabaseModule(), new TransportModule()); + new DatabaseModule(), new ProtocolModule(), + new ProtocolWritersModule(), new SerialModule(), + new TestDatabaseModule(), new TransportBatchModule(), + new TransportModule(), new TransportStreamModule()); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); } @@ -40,7 +45,7 @@ public class ConnectionWriterTest extends TestCase { ByteArrayOutputStream out = new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, - MIN_CONNECTION_LENGTH, transportId, connection, secret); + MIN_CONNECTION_LENGTH, transportIndex, connection, secret); // Check that the connection writer thinks there's room for a packet long capacity = w.getRemainingCapacity(); assertTrue(capacity >= MAX_PACKET_LENGTH); diff --git a/test/net/sf/briar/transport/FrameReadWriteTest.java b/test/net/sf/briar/transport/FrameReadWriteTest.java index 8ed227dd58fdf3b45f9b8fb850b2bfef0c36ec9e..03d61462254b92cf84a359c2b6947c2b49d13849 100644 --- a/test/net/sf/briar/transport/FrameReadWriteTest.java +++ b/test/net/sf/briar/transport/FrameReadWriteTest.java @@ -14,8 +14,8 @@ import javax.crypto.Mac; import javax.crypto.SecretKey; import junit.framework.TestCase; -import net.sf.briar.api.TransportId; import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.TransportIndex; import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionWriter; import net.sf.briar.crypto.CryptoModule; @@ -33,7 +33,7 @@ public class FrameReadWriteTest extends TestCase { private final Mac mac; private final Random random; private final byte[] secret = new byte[100]; - private final TransportId transportId = new TransportId(123); + private final TransportIndex transportIndex = new TransportIndex(13); private final long connection = 12345L; public FrameReadWriteTest() { @@ -62,7 +62,7 @@ public class FrameReadWriteTest extends TestCase { private void testWriteAndRead(boolean initiator) throws Exception { // Create and encrypt the IV - byte[] iv = IvEncoder.encodeIv(initiator, transportId, connection); + byte[] iv = IvEncoder.encodeIv(initiator, transportIndex, connection); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey); byte[] encryptedIv = ivCipher.doFinal(iv); assertEquals(IV_LENGTH, encryptedIv.length); @@ -90,7 +90,7 @@ public class FrameReadWriteTest extends TestCase { // Decrypt the IV ivCipher.init(Cipher.DECRYPT_MODE, ivKey); byte[] recoveredIv = ivCipher.doFinal(recoveredEncryptedIv); - iv = IvEncoder.encodeIv(initiator, transportId, connection); + iv = IvEncoder.encodeIv(initiator, transportIndex, connection); assertArrayEquals(iv, recoveredIv); // Read the frames back ConnectionDecrypter decrypter = new ConnectionDecrypterImpl(in, iv, diff --git a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java index b6d0ab5e80a75338d202acbd69ef00cd74c8602c..c3e17d8d31891fdc9fe7e5fa507dc49c3d05c820 100644 --- a/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java +++ b/test/net/sf/briar/transport/batch/BatchConnectionReadWriteTest.java @@ -7,28 +7,30 @@ import java.io.ByteArrayOutputStream; import java.io.File; import java.io.InputStream; import java.io.OutputStream; +import java.util.Collection; import java.util.Collections; -import java.util.Map; import junit.framework.TestCase; import net.sf.briar.TestDatabaseModule; import net.sf.briar.TestUtils; import net.sf.briar.api.ContactId; -import net.sf.briar.api.TransportId; -import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.protocol.Message; -import net.sf.briar.api.protocol.MessageEncoder; import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportIndex; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.writers.MessageEncoder; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportWriter; +import net.sf.briar.api.transport.ConnectionContext; import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionRecogniser; -import net.sf.briar.api.transport.ConnectionRecogniserFactory; import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.crypto.CryptoModule; import net.sf.briar.db.DatabaseModule; @@ -36,6 +38,7 @@ import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.writers.ProtocolWritersModule; import net.sf.briar.serial.SerialModule; import net.sf.briar.transport.TransportModule; +import net.sf.briar.transport.stream.TransportStreamModule; import org.junit.After; import org.junit.Before; @@ -49,15 +52,16 @@ public class BatchConnectionReadWriteTest extends TestCase { private final File testDir = TestUtils.getTestDirectory(); private final File aliceDir = new File(testDir, "alice"); private final File bobDir = new File(testDir, "bob"); - private final TransportId transportId = new TransportId(123); - private final Map<TransportId, TransportProperties> transports = - Collections.emptyMap(); + private final TransportId transportId; + private final TransportIndex transportIndex; private final byte[] aliceSecret, bobSecret; private Injector alice, bob; public BatchConnectionReadWriteTest() throws Exception { super(); + transportId = new TransportId(TestUtils.getRandomId()); + transportIndex = new TransportIndex(1); // Create matching secrets for Alice and Bob aliceSecret = new byte[100]; aliceSecret[16] = (byte) 1; @@ -71,12 +75,14 @@ public class BatchConnectionReadWriteTest extends TestCase { alice = Guice.createInjector(new CryptoModule(), new DatabaseModule(), new ProtocolModule(), new ProtocolWritersModule(), new SerialModule(), new TestDatabaseModule(aliceDir), - new TransportModule()); + new TransportBatchModule(), new TransportModule(), + new TransportStreamModule()); // Create Bob's injector bob = Guice.createInjector(new CryptoModule(), new DatabaseModule(), new ProtocolModule(), new ProtocolWritersModule(), new SerialModule(), new TestDatabaseModule(bobDir), - new TransportModule()); + new TransportBatchModule(), new TransportModule(), + new TransportStreamModule()); } @Test @@ -96,7 +102,7 @@ public class BatchConnectionReadWriteTest extends TestCase { DatabaseComponent db = alice.getInstance(DatabaseComponent.class); db.open(false); // Add Bob as a contact and send him a message - ContactId contactId = db.addContact(transports, aliceSecret); + ContactId contactId = db.addContact(aliceSecret); String subject = "Hello"; byte[] messageBody = "Hi Bob!".getBytes("UTF-8"); MessageEncoder encoder = alice.getInstance(MessageEncoder.class); @@ -110,7 +116,8 @@ public class BatchConnectionReadWriteTest extends TestCase { alice.getInstance(ProtocolWriterFactory.class); BatchTransportWriter writer = new TestBatchTransportWriter(out); OutgoingBatchConnection batchOut = new OutgoingBatchConnection( - connFactory, db, protoFactory, transportId, contactId, writer); + connFactory, db, protoFactory, transportIndex, contactId, + writer); // Write whatever needs to be written batchOut.write(); // Close Alice's database @@ -127,18 +134,33 @@ public class BatchConnectionReadWriteTest extends TestCase { MessageListener listener = new MessageListener(); db.addListener(listener); // Add Alice as a contact - ContactId contactId = db.addContact(transports, bobSecret); + ContactId contactId = db.addContact(bobSecret); + // Add the transport + assertEquals(transportIndex, db.addTransport(transportId)); + // Fake a transport update from Alice + TransportUpdate transportUpdate = new TransportUpdate() { + + public Collection<Transport> getTransports() { + Transport t = new Transport(transportId, transportIndex); + return Collections.singletonList(t); + } + + public long getTimestamp() { + return System.currentTimeMillis(); + } + }; + db.receiveTransportUpdate(contactId, transportUpdate); // Create a connection recogniser and recognise the connection ByteArrayInputStream in = new ByteArrayInputStream(b); - ConnectionRecogniserFactory recFactory = - bob.getInstance(ConnectionRecogniserFactory.class); - ConnectionRecogniser rec = - recFactory.createConnectionRecogniser(transportId); + ConnectionRecogniser rec = bob.getInstance(ConnectionRecogniser.class); byte[] encryptedIv = new byte[IV_LENGTH]; int read = in.read(encryptedIv); assertEquals(encryptedIv.length, read); - ContactId accepted = rec.acceptConnection(encryptedIv); - assertEquals(contactId, accepted); + ConnectionContext ctx = rec.acceptConnection(encryptedIv); + assertNotNull(ctx); + assertEquals(contactId, ctx.getContactId()); + assertEquals(transportId, ctx.getTransportId()); + assertEquals(transportIndex, ctx.getTransportIndex()); // Create an incoming batch connection ConnectionReaderFactory connFactory = bob.getInstance(ConnectionReaderFactory.class); @@ -146,8 +168,8 @@ public class BatchConnectionReadWriteTest extends TestCase { bob.getInstance(ProtocolReaderFactory.class); BatchTransportReader reader = new TestBatchTransportReader(in); IncomingBatchConnection batchIn = new IncomingBatchConnection( - connFactory, db, protoFactory, transportId, contactId, reader, - encryptedIv); + connFactory, db, protoFactory, transportIndex, contactId, + reader, encryptedIv); // No messages should have been added yet assertFalse(listener.messagesAdded); // Read whatever needs to be read