Split transport identifiers into two: TransportId (globally unique)

and TransportIndex (locally unique).

This is the first step towards forward secrecy. Also removed the
Writable interface and unnecessary user-defined types, moved various
constants to ProtocolConstants and renamed some classes.
This commit is contained in:
akwizgran
2011-11-14 21:40:05 +00:00
parent 7d09102c4d
commit 73aa7d14d7
113 changed files with 1610 additions and 1121 deletions

View File

@@ -1,6 +1,9 @@
package net.sf.briar.api; 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 { public class ContactId {
private final int id; private final int id;
@@ -23,9 +26,4 @@ public class ContactId {
public int hashCode() { public int hashCode() {
return id; return id;
} }
@Override
public String toString() {
return String.valueOf(id);
}
} }

View File

@@ -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;
}
}

View File

@@ -7,7 +7,6 @@ import java.util.Map;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.event.DatabaseListener; import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.protocol.Ack; 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.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; import net.sf.briar.api.protocol.writers.BatchWriter;
@@ -51,11 +53,10 @@ public interface DatabaseComponent {
void removeListener(DatabaseListener d); void removeListener(DatabaseListener d);
/** /**
* Adds a new contact to the database with the given transport properties * Adds a new contact to the database with the given secret and returns an
* and shared secret, returns an ID for the contact. * ID for the contact.
*/ */
ContactId addContact(Map<TransportId, TransportProperties> transports, ContactId addContact(byte[] secret) throws DbException;
byte[] secret) throws DbException;
/** Adds a locally generated group message to the database. */ /** Adds a locally generated group message to the database. */
void addLocalGroupMessage(Message m) throws DbException; void addLocalGroupMessage(Message m) throws DbException;
@@ -63,6 +64,12 @@ public interface DatabaseComponent {
/** Adds a locally generated private message to the database. */ /** Adds a locally generated private message to the database. */
void addLocalPrivateMessage(Message m, ContactId c) throws DbException; 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. * Generates an acknowledgement for the given contact.
* @return True if any batch IDs were added to the acknowledgement. * @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 * Returns an outgoing connection number for the given contact and
* transport. * 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 * Returns the connection reordering window for the given contact and
* transport. * transport.
*/ */
ConnectionWindow getConnectionWindow(ContactId c, TransportId t) ConnectionWindow getConnectionWindow(ContactId c, TransportIndex i)
throws DbException; throws DbException;
/** Returns the IDs of all contacts. */ /** Returns the IDs of all contacts. */
Collection<ContactId> getContacts() throws DbException; 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. */ /** Returns the local transport properties for the given transport. */
TransportProperties getLocalProperties(TransportId t) throws DbException; TransportProperties getLocalProperties(TransportId t) throws DbException;
/** Returns all local transport properties. */ /** Returns all local transports. */
Map<TransportId, TransportProperties> getLocalTransports() Collection<Transport> getLocalTransports() throws DbException;
throws DbException;
/** Returns the headers of all messages in the given group. */ /** Returns the headers of all messages in the given group. */
Collection<MessageHeader> getMessageHeaders(GroupId g) throws DbException; Collection<MessageHeader> getMessageHeaders(GroupId g) throws DbException;
@@ -134,6 +146,13 @@ public interface DatabaseComponent {
/** Returns the user's rating for the given author. */ /** Returns the user's rating for the given author. */
Rating getRating(AuthorId a) throws DbException; 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. */ /** Returns all remote transport properties for the given transport. */
Map<ContactId, TransportProperties> getRemoteProperties(TransportId t) Map<ContactId, TransportProperties> getRemoteProperties(TransportId t)
throws DbException; throws DbException;
@@ -191,8 +210,8 @@ public interface DatabaseComponent {
* Sets the connection reordering window for the given contact and * Sets the connection reordering window for the given contact and
* transport. * transport.
*/ */
void setConnectionWindow(ContactId c, TransportId t, ConnectionWindow w) void setConnectionWindow(ContactId c, TransportIndex i,
throws DbException; ConnectionWindow w) throws DbException;
/** /**
* Sets the local transport properties for the given transport, replacing * Sets the local transport properties for the given transport, replacing

View File

@@ -3,9 +3,15 @@ package net.sf.briar.api.db.event;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
/** An event that is broadcast when a contact is removed. */ /** 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) { public ContactRemovedEvent(ContactId contactId) {
super(contactId); this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
} }
} }

View File

@@ -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 {
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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 {
}

View File

@@ -4,6 +4,5 @@ import java.util.concurrent.Executor;
public interface BatchPluginFactory { public interface BatchPluginFactory {
BatchPlugin createPlugin(Executor executor, BatchPlugin createPlugin(Executor executor, BatchPluginCallback callback);
BatchPluginCallback callback);
} }

View File

@@ -2,7 +2,7 @@ package net.sf.briar.api.plugins;
import java.io.IOException; import java.io.IOException;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.TransportId;
public interface Plugin { public interface Plugin {

View File

@@ -3,14 +3,13 @@ package net.sf.briar.api.plugins;
public interface PluginManager { public interface PluginManager {
/** /**
* Starts all the plugins the manager knows about and returns the number of * Starts the plugins and returns the number of plugins successfully
* plugins successfully started. * started.
*/ */
int startPlugins(); int startPlugins();
/** /**
* Stops all the plugins started by startPlugins() and returns the number * Stops the plugins and returns the number of plugins successfully stopped.
* of plugins successfully stopped.
*/ */
int stopPlugins(); int stopPlugins();
} }

View File

@@ -4,6 +4,5 @@ import java.util.concurrent.Executor;
public interface StreamPluginFactory { public interface StreamPluginFactory {
StreamPlugin createPlugin(Executor executor, StreamPlugin createPlugin(Executor executor, StreamPluginCallback callback);
StreamPluginCallback callback);
} }

View File

@@ -1,15 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import net.sf.briar.api.serial.Writable;
/** A pseudonymous author of messages. */ /** A pseudonymous author of messages. */
public interface Author extends Writable { public interface Author {
/** 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;
/** Returns the author's unique identifier. */ /** Returns the author's unique identifier. */
AuthorId getId(); AuthorId getId();

View File

@@ -1,10 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import net.sf.briar.api.serial.Writer;
/** Type-safe wrapper for a byte array that uniquely identifies an author. */ /** Type-safe wrapper for a byte array that uniquely identifies an author. */
public class AuthorId extends UniqueId { public class AuthorId extends UniqueId {
@@ -12,11 +9,6 @@ public class AuthorId extends UniqueId {
super(id); super(id);
} }
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.AUTHOR_ID);
w.writeBytes(id);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(o instanceof AuthorId) if(o instanceof AuthorId)

View File

@@ -1,10 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import net.sf.briar.api.serial.Writer;
/** /**
* Type-safe wrapper for a byte array that uniquely identifies a batch of * Type-safe wrapper for a byte array that uniquely identifies a batch of
* messages. * messages.
@@ -15,11 +12,6 @@ public class BatchId extends UniqueId {
super(id); super(id);
} }
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.BATCH_ID);
w.writeBytes(id);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(o instanceof BatchId) if(o instanceof BatchId)

View File

@@ -1,15 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import net.sf.briar.api.serial.Writable;
/** A group to which users may subscribe. */ /** A group to which users may subscribe. */
public interface Group extends Writable { public interface Group {
/** 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;
/** Returns the group's unique identifier. */ /** Returns the group's unique identifier. */
GroupId getId(); GroupId getId();

View File

@@ -1,10 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import java.io.IOException;
import java.util.Arrays; 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 * Type-safe wrapper for a byte array that uniquely identifies a group to which
* users may subscribe. * users may subscribe.
@@ -15,11 +12,6 @@ public class GroupId extends UniqueId {
super(id); super(id);
} }
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.GROUP_ID);
w.writeBytes(id);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(o instanceof GroupId) if(o instanceof GroupId)

View File

@@ -2,23 +2,6 @@ package net.sf.briar.api.protocol;
public interface Message { 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. */ /** Returns the message's unique identifier. */
MessageId getId(); MessageId getId();

View File

@@ -1,10 +1,7 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import net.sf.briar.api.serial.Writer;
/** Type-safe wrapper for a byte array that uniquely identifies a message. */ /** Type-safe wrapper for a byte array that uniquely identifies a message. */
public class MessageId extends UniqueId { public class MessageId extends UniqueId {
@@ -12,11 +9,6 @@ public class MessageId extends UniqueId {
super(id); super(id);
} }
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.MESSAGE_ID);
w.writeBytes(id);
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(o instanceof MessageId) if(o instanceof MessageId)

View File

@@ -11,4 +11,41 @@ public interface ProtocolConstants {
*/ */
static final int MAX_PACKET_LENGTH = static final int MAX_PACKET_LENGTH =
TransportConstants.MIN_CONNECTION_LENGTH - 1024; 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;
} }

View File

@@ -5,9 +5,6 @@ import java.util.Map;
/** A packet updating the sender's subscriptions. */ /** A packet updating the sender's subscriptions. */
public interface SubscriptionUpdate { 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. */ /** Returns the subscriptions contained in the update. */
Map<Group, Long> getSubscriptions(); Map<Group, Long> getSubscriptions();

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -1,24 +1,12 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol;
import java.util.Map; import java.util.Collection;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties;
/** A packet updating the sender's transport properties. */ /** A packet updating the sender's transport properties. */
public interface TransportUpdate { public interface TransportUpdate {
/** The maximum length of a property's key or value in UTF-8 bytes. */ /** Returns the transports contained in the update. */
static final int MAX_KEY_OR_VALUE_LENGTH = 100; Collection<Transport> getTransports();
/** 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 update's timestamp. Updates that are older than the newest * Returns the update's timestamp. Updates that are older than the newest

View File

@@ -5,16 +5,14 @@ public interface Types {
static final int ACK = 0; static final int ACK = 0;
static final int AUTHOR = 1; static final int AUTHOR = 1;
static final int AUTHOR_ID = 2; static final int BATCH = 2;
static final int BATCH = 3; static final int BATCH_ID = 3;
static final int BATCH_ID = 4; static final int GROUP = 4;
static final int GROUP = 5; static final int MESSAGE = 5;
static final int GROUP_ID = 6; static final int MESSAGE_ID = 6;
static final int MESSAGE = 7; static final int OFFER = 7;
static final int MESSAGE_ID = 8; static final int REQUEST = 8;
static final int OFFER = 9; static final int SUBSCRIPTION_UPDATE = 9;
static final int REQUEST = 10; static final int TRANSPORT = 10;
static final int SUBSCRIPTION_UPDATE = 11; static final int TRANSPORT_UPDATE = 11;
static final int TRANSPORT_PROPERTIES = 12;
static final int TRANSPORT_UPDATE = 13;
} }

View File

@@ -2,9 +2,7 @@ package net.sf.briar.api.protocol;
import java.util.Arrays; import java.util.Arrays;
import net.sf.briar.api.serial.Writable; public abstract class UniqueId {
public abstract class UniqueId implements Writable {
/** The length of a unique identifier in bytes. */ /** The length of a unique identifier in bytes. */
public static final int LENGTH = 32; public static final int LENGTH = 32;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -1,9 +1,14 @@
package net.sf.briar.api.protocol; package net.sf.briar.api.protocol.writers;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.PrivateKey; 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 { public interface MessageEncoder {
/** Encodes a private message. */ /** Encodes a private message. */

View File

@@ -1,15 +1,14 @@
package net.sf.briar.api.protocol.writers; package net.sf.briar.api.protocol.writers;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Collection;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.TransportProperties;
/** An interface for creating a transport update. */ /** An interface for creating a transport update. */
public interface TransportWriter { public interface TransportWriter {
/** Writes the contents of the update. */ /** Writes the contents of the update. */
void writeTransports(Map<TransportId, TransportProperties> transports, void writeTransports(Collection<Transport> transports, long timestamp)
long timestamp) throws IOException; throws IOException;
} }

View File

@@ -1,8 +0,0 @@
package net.sf.briar.api.serial;
import java.io.IOException;
public interface Writable {
void writeTo(Writer w) throws IOException;
}

View File

@@ -1,13 +1,13 @@
package net.sf.briar.api.transport; package net.sf.briar.api.transport;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.TransportIndex;
public interface BatchConnectionFactory { public interface BatchConnectionFactory {
void createIncomingConnection(TransportId t, ContactId c, void createIncomingConnection(TransportIndex i, ContactId c,
BatchTransportReader r, byte[] encryptedIv); BatchTransportReader r, byte[] encryptedIv);
void createOutgoingConnection(TransportId t, ContactId c, void createOutgoingConnection(TransportIndex i, ContactId c,
BatchTransportWriter w); BatchTransportWriter w);
} }

View File

@@ -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();
}

View File

@@ -1,17 +1,17 @@
package net.sf.briar.api.transport; package net.sf.briar.api.transport;
import net.sf.briar.api.ContactId; 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 { public interface ConnectionDispatcher {
void dispatchReader(TransportId t, BatchTransportReader r); void dispatchReader(TransportId t, BatchTransportReader r);
void dispatchWriter(TransportId t, ContactId c, void dispatchWriter(TransportIndex i, ContactId c, BatchTransportWriter w);
BatchTransportWriter w);
void dispatchIncomingConnection(TransportId t, StreamTransportConnection s); void dispatchIncomingConnection(TransportId t, StreamTransportConnection s);
void dispatchOutgoingConnection(TransportId t, ContactId c, void dispatchOutgoingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s); StreamTransportConnection s);
} }

View File

@@ -2,7 +2,7 @@ package net.sf.briar.api.transport;
import java.io.InputStream; import java.io.InputStream;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.TransportIndex;
public interface ConnectionReaderFactory { public interface ConnectionReaderFactory {
@@ -10,13 +10,13 @@ public interface ConnectionReaderFactory {
* Creates a connection reader for a batch-mode connection or the * Creates a connection reader for a batch-mode connection or the
* initiator's side of a stream-mode connection. * initiator's side of a stream-mode connection.
*/ */
ConnectionReader createConnectionReader(InputStream in, TransportId t, ConnectionReader createConnectionReader(InputStream in, TransportIndex i,
byte[] encryptedIv, byte[] secret); byte[] encryptedIv, byte[] secret);
/** /**
* Creates a connection reader for the responder's side of a stream-mode * Creates a connection reader for the responder's side of a stream-mode
* connection. * connection.
*/ */
ConnectionReader createConnectionReader(InputStream in, TransportId t, ConnectionReader createConnectionReader(InputStream in, TransportIndex i,
long connection, byte[] secret); long connection, byte[] secret);
} }

View File

@@ -1,18 +1,16 @@
package net.sf.briar.api.transport; package net.sf.briar.api.transport;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
/** /**
* Maintains a transport plugin's connection reordering window and decides * Maintains the connection reordering windows and decides whether incoming
* whether incoming connections should be accepted or rejected. * connections should be accepted or rejected.
*/ */
public interface ConnectionRecogniser { public interface ConnectionRecogniser {
/** /**
* Returns the ID of the contact who created the encrypted IV if the * Returns the connection's context if the connection should be accepted,
* connection should be accepted, or null if the connection should be * or null if the connection should be rejected.
* rejected.
*/ */
ContactId acceptConnection(byte[] encryptedIv) throws DbException; ConnectionContext acceptConnection(byte[] encryptedIv) throws DbException;
} }

View File

@@ -1,8 +0,0 @@
package net.sf.briar.api.transport;
import net.sf.briar.api.TransportId;
public interface ConnectionRecogniserFactory {
ConnectionRecogniser createConnectionRecogniser(TransportId t);
}

View File

@@ -2,7 +2,7 @@ package net.sf.briar.api.transport;
import java.io.OutputStream; import java.io.OutputStream;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.TransportIndex;
public interface ConnectionWriterFactory { public interface ConnectionWriterFactory {
@@ -11,12 +11,12 @@ public interface ConnectionWriterFactory {
* initiator's side of a stream-mode connection. * initiator's side of a stream-mode connection.
*/ */
ConnectionWriter createConnectionWriter(OutputStream out, long capacity, 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 * Creates a connection writer for the responder's side of a stream-mode
* connection. * connection.
*/ */
ConnectionWriter createConnectionWriter(OutputStream out, long capacity, ConnectionWriter createConnectionWriter(OutputStream out, long capacity,
TransportId t, byte[] encryptedIv, byte[] secret); TransportIndex i, byte[] encryptedIv, byte[] secret);
} }

View File

@@ -1,13 +1,13 @@
package net.sf.briar.api.transport; package net.sf.briar.api.transport;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.TransportIndex;
public interface StreamConnectionFactory { public interface StreamConnectionFactory {
void createIncomingConnection(TransportId t, ContactId c, void createIncomingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s, byte[] encryptedIv); StreamTransportConnection s, byte[] encryptedIv);
void createOutgoingConnection(TransportId t, ContactId c, void createOutgoingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s); StreamTransportConnection s);
} }

View File

@@ -7,7 +7,6 @@ import java.util.Map;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; 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.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; 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.ConnectionWindow;
/** /**
@@ -78,14 +80,12 @@ interface Database<T> {
void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException; void addBatchToAck(T txn, ContactId c, BatchId b) throws DbException;
/** /**
* Adds a new contact to the database with the given transport properties * Adds a new contact to the database with the given secret and returns an
* and secret, and returns an ID for the contact. * ID for the contact.
* <p> * <p>
* Locking: contact write, transport write. * Locking: contact write.
*/ */
ContactId addContact(T txn, ContactId addContact(T txn, byte[] secret) throws DbException;
Map<TransportId, TransportProperties> transports, byte[] secret)
throws DbException;
/** /**
* Returns false if the given message is already in the database. Otherwise * 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; 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. * Returns true if the database contains the given contact.
* <p> * <p>
@@ -179,7 +187,7 @@ interface Database<T> {
* <p> * <p>
* Locking: contact read, window write. * Locking: contact read, window write.
*/ */
long getConnectionNumber(T txn, ContactId c, TransportId t) long getConnectionNumber(T txn, ContactId c, TransportIndex i)
throws DbException; throws DbException;
/** /**
@@ -188,7 +196,7 @@ interface Database<T> {
* <p> * <p>
* Locking: contact read, window read. * Locking: contact read, window read.
*/ */
ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportId t) ConnectionWindow getConnectionWindow(T txn, ContactId c, TransportIndex i)
throws DbException; throws DbException;
/** /**
@@ -216,6 +224,14 @@ interface Database<T> {
*/ */
MessageId getGroupMessageParent(T txn, MessageId m) throws DbException; 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. * Returns the local transport properties for the given transport.
* <p> * <p>
@@ -225,12 +241,11 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Returns all local transport properties. * Returns all local transports.
* <p> * <p>
* Locking: transport read. * Locking: transport read.
*/ */
Map<TransportId, TransportProperties> getLocalTransports(T txn) Collection<Transport> getLocalTransports(T txn) throws DbException;
throws DbException;
/** /**
* Returns the IDs of any batches sent to the given contact that should now * 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; 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. * Returns all remote properties for the given transport.
* <p> * <p>
@@ -456,7 +480,7 @@ interface Database<T> {
* Removes a contact (and all associated state) from the database. * Removes a contact (and all associated state) from the database.
* <p> * <p>
* Locking: contact write, message write, messageFlag write, * 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; void removeContact(T txn, ContactId c) throws DbException;
@@ -501,7 +525,7 @@ interface Database<T> {
* <p> * <p>
* Locking: contact read, window write. * 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; ConnectionWindow w) throws DbException;
/** /**
@@ -591,15 +615,13 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Sets the transport properties for the given contact, replacing any * Sets the transports for the given contact, replacing any existing
* existing properties unless the existing properties have a newer * transports unless the existing transports have a newer timestamp.
* timestamp.
* <p> * <p>
* Locking: contact read, transport write. * Locking: contact read, transport write.
*/ */
void setTransports(T txn, ContactId c, void setTransports(T txn, ContactId c, Collection<Transport> transports,
Map<TransportId, TransportProperties> transports, long timestamp) long timestamp) throws DbException;
throws DbException;
/** /**
* Records the time at which the local transports were last modified. * Records the time at which the local transports were last modified.

View File

@@ -23,7 +23,6 @@ import net.sf.briar.api.Bytes;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException; 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.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; 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.MessagesAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent; 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.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.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch; 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.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; import net.sf.briar.api.protocol.writers.BatchWriter;
@@ -131,27 +135,19 @@ DatabaseCleaner.Callback {
} }
} }
public ContactId addContact( public ContactId addContact(byte[] secret) throws DbException {
Map<TransportId, TransportProperties> transports, byte[] secret)
throws DbException {
if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact"); if(LOG.isLoggable(Level.FINE)) LOG.fine("Adding contact");
ContactId c; ContactId c;
contactLock.writeLock().lock(); contactLock.writeLock().lock();
try { try {
transportLock.writeLock().lock(); T txn = db.startTransaction();
try { try {
T txn = db.startTransaction(); c = db.addContact(txn, secret);
try { db.commitTransaction(txn);
c = db.addContact(txn, transports, secret); if(LOG.isLoggable(Level.FINE)) LOG.fine("Added contact " + c);
db.commitTransaction(txn); } catch(DbException e) {
if(LOG.isLoggable(Level.FINE)) db.abortTransaction(txn);
LOG.fine("Added contact " + c); throw e;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
transportLock.writeLock().unlock();
} }
} finally { } finally {
contactLock.writeLock().unlock(); 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, public boolean generateAck(ContactId c, AckWriter a) throws DbException,
IOException { IOException {
contactLock.readLock().lock(); contactLock.readLock().lock();
@@ -630,7 +646,7 @@ DatabaseCleaner.Callback {
public void generateTransportUpdate(ContactId c, TransportWriter t) public void generateTransportUpdate(ContactId c, TransportWriter t)
throws DbException, IOException { throws DbException, IOException {
Map<TransportId, TransportProperties> transports = null; Collection<Transport> transports = null;
long timestamp = 0L; long timestamp = 0L;
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -683,7 +699,7 @@ DatabaseCleaner.Callback {
} }
} }
public long getConnectionNumber(ContactId c, TransportId t) public long getConnectionNumber(ContactId c, TransportIndex i)
throws DbException { throws DbException {
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -692,7 +708,7 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
long outgoing = db.getConnectionNumber(txn, c, t); long outgoing = db.getConnectionNumber(txn, c, i);
db.commitTransaction(txn); db.commitTransaction(txn);
return outgoing; return outgoing;
} catch(DbException e) { } 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 { throws DbException {
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -716,7 +732,7 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
ConnectionWindow w = db.getConnectionWindow(txn, c, t); ConnectionWindow w = db.getConnectionWindow(txn, c, i);
db.commitTransaction(txn); db.commitTransaction(txn);
return w; return w;
} catch(DbException e) { } 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) public TransportProperties getLocalProperties(TransportId t)
throws DbException { throws DbException {
transportLock.readLock().lock(); transportLock.readLock().lock();
@@ -766,14 +799,12 @@ DatabaseCleaner.Callback {
} }
} }
public Map<TransportId, TransportProperties> getLocalTransports() public Collection<Transport> getLocalTransports() throws DbException {
throws DbException {
transportLock.readLock().lock(); transportLock.readLock().lock();
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
Map<TransportId, TransportProperties> transports = Collection<Transport> transports = db.getLocalTransports(txn);
db.getLocalTransports(txn);
db.commitTransaction(txn); db.commitTransaction(txn);
return transports; return transports;
} catch(DbException e) { } 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( public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException { TransportId t) throws DbException {
contactLock.readLock().lock(); contactLock.readLock().lock();
@@ -1135,6 +1190,8 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock
callListeners(new SubscriptionsUpdatedEvent(Collections.singleton(c)));
} }
public void receiveTransportUpdate(ContactId c, TransportUpdate t) public void receiveTransportUpdate(ContactId c, TransportUpdate t)
@@ -1147,8 +1204,7 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
Map<TransportId, TransportProperties> transports = Collection<Transport> transports = t.getTransports();
t.getTransports();
db.setTransports(txn, c, transports, t.getTimestamp()); db.setTransports(txn, c, transports, t.getTimestamp());
if(LOG.isLoggable(Level.FINE)) if(LOG.isLoggable(Level.FINE))
LOG.fine("Received " + transports.size() LOG.fine("Received " + transports.size()
@@ -1164,6 +1220,8 @@ DatabaseCleaner.Callback {
} finally { } finally {
contactLock.readLock().unlock(); contactLock.readLock().unlock();
} }
// Call the listeners outside the lock
callListeners(new RemoteTransportsUpdatedEvent(c));
} }
public void removeContact(ContactId c) throws DbException { public void removeContact(ContactId c) throws DbException {
@@ -1180,13 +1238,18 @@ DatabaseCleaner.Callback {
try { try {
transportLock.writeLock().lock(); transportLock.writeLock().lock();
try { try {
T txn = db.startTransaction(); windowLock.writeLock().lock();
try { try {
db.removeContact(txn, c); T txn = db.startTransaction();
db.commitTransaction(txn); try {
} catch(DbException e) { db.removeContact(txn, c);
db.abortTransaction(txn); db.commitTransaction(txn);
throw e; } catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
} }
} finally { } finally {
transportLock.writeLock().unlock(); 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 { ConnectionWindow w) throws DbException {
contactLock.readLock().lock(); contactLock.readLock().lock();
try { try {
@@ -1236,7 +1299,7 @@ DatabaseCleaner.Callback {
try { try {
T txn = db.startTransaction(); T txn = db.startTransaction();
try { try {
db.setConnectionWindow(txn, c, t, w); db.setConnectionWindow(txn, c, i, w);
db.commitTransaction(txn); db.commitTransaction(txn);
} catch(DbException e) { } catch(DbException e) {
db.abortTransaction(txn); db.abortTransaction(txn);
@@ -1270,7 +1333,7 @@ DatabaseCleaner.Callback {
transportLock.writeLock().unlock(); transportLock.writeLock().unlock();
} }
// Call the listeners outside the lock // 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 { public void setRating(AuthorId a, Rating r) throws DbException {
@@ -1430,6 +1493,7 @@ DatabaseCleaner.Callback {
} finally { } finally {
subscriptionLock.writeLock().unlock(); subscriptionLock.writeLock().unlock();
} }
// Listeners will be notified when the group's visibility is set
} }
public void unsubscribe(GroupId g) throws DbException { public void unsubscribe(GroupId g) throws DbException {

View File

@@ -20,7 +20,6 @@ import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.MessageHeader; 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.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; 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.ConnectionWindow;
import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.api.transport.ConnectionWindowFactory;
import net.sf.briar.util.FileUtils; import net.sf.briar.util.FileUtils;
@@ -169,38 +172,56 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String INDEX_STATUSES_BY_CONTACT = private static final String INDEX_STATUSES_BY_CONTACT =
"CREATE INDEX statusesByContact ON statuses (contactId)"; "CREATE INDEX statusesByContact ON statuses (contactId)";
private static final String CREATE_TRANSPORTS =
"CREATE TABLE transports"
+ " (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_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 = private static final String CREATE_CONTACT_TRANSPORTS =
"CREATE TABLE contactTransports" "CREATE TABLE contactTransports"
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " transportId 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," + " key VARCHAR NOT NULL,"
+ " value VARCHAR NOT NULL," + " value VARCHAR NOT NULL,"
+ " PRIMARY KEY (contactId, transportId, key)," + " PRIMARY KEY (contactId, transportId, key),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_TRANSPORTS =
"CREATE TABLE transports"
+ " (transportId INT 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,"
+ " key VARCHAR NOT NULL,"
+ " value VARCHAR NOT NULL,"
+ " PRIMARY KEY (transportId, key))";
private static final String CREATE_CONNECTION_WINDOWS = private static final String CREATE_CONNECTION_WINDOWS =
"CREATE TABLE connectionWindows" "CREATE TABLE connectionWindows"
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " transportId INT NOT NULL," + " index INT NOT NULL,"
+ " centre BIGINT NOT NULL," + " centre BIGINT NOT NULL,"
+ " bitmap INT NOT NULL," + " bitmap INT NOT NULL,"
+ " outgoing BIGINT NOT NULL," + " outgoing BIGINT NOT NULL,"
+ " PRIMARY KEY (contactId, transportId)," + " PRIMARY KEY (contactId, index),"
+ " FOREIGN KEY (contactId) REFERENCES contacts (contactId)" + " FOREIGN KEY (contactId) REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -316,9 +337,11 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(insertTypeNames(CREATE_STATUSES));
s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT); s.executeUpdate(INDEX_STATUSES_BY_CONTACT);
s.executeUpdate(insertTypeNames(CREATE_CONTACT_TRANSPORTS));
s.executeUpdate(insertTypeNames(CREATE_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_CONNECTION_WINDOWS));
s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS)); s.executeUpdate(insertTypeNames(CREATE_SUBSCRIPTION_TIMESTAMPS));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_TIMESTAMPS));
@@ -478,8 +501,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public ContactId addContact(Connection txn, public ContactId addContact(Connection txn, byte[] secret)
Map<TransportId, TransportProperties> transports, byte[] secret)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
@@ -502,29 +524,6 @@ abstract class JdbcDatabase implements Database<Connection> {
if(rs.next()) throw new DbStateException(); if(rs.next()) throw new DbStateException();
rs.close(); rs.close();
ps.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 // Initialise the subscription timestamps
sql = "INSERT INTO subscriptionTimestamps" sql = "INSERT INTO subscriptionTimestamps"
+ " (contactId, sent, received, modified)" + " (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) public boolean containsContact(Connection txn, ContactId c)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -836,10 +873,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT key, value FROM transportConfig" String sql = "SELECT key, value FROM transportConfigs"
+ " WHERE transportId = ?"; + " WHERE transportId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
TransportConfig c = new TransportConfig(); TransportConfig c = new TransportConfig();
while(rs.next()) c.put(rs.getString(1), rs.getString(2)); 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, public long getConnectionNumber(Connection txn, ContactId c,
TransportId t) throws DbException { TransportIndex i) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT outgoing FROM connectionWindows" String sql = "SELECT outgoing FROM connectionWindows"
+ " WHERE contactId = ? AND transportId = ?"; + " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, t.getInt()); ps.setInt(2, i.getInt());
rs = ps.executeQuery(); rs = ps.executeQuery();
if(rs.next()) { if(rs.next()) {
// A connection window row exists - update it // A connection window row exists - update it
@@ -871,11 +908,11 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close(); rs.close();
ps.close(); ps.close();
sql = "UPDATE connectionWindows SET outgoing = ?" sql = "UPDATE connectionWindows SET outgoing = ?"
+ " WHERE contactId = ? AND transportId = ?"; + " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setLong(1, outgoing + 1); ps.setLong(1, outgoing + 1);
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
ps.setInt(3, t.getInt()); ps.setInt(3, i.getInt());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -885,11 +922,11 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close(); rs.close();
ps.close(); ps.close();
sql = "INSERT INTO connectionWindows" sql = "INSERT INTO connectionWindows"
+ " (contactId, transportId, centre, bitmap, outgoing)" + " (contactId, index, centre, bitmap, outgoing)"
+ " VALUES(?, ?, ZERO(), ZERO(), ZERO())"; + " VALUES(?, ?, ZERO(), ZERO(), ZERO())";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, t.getInt()); ps.setInt(2, i.getInt());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
@@ -903,15 +940,15 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
public ConnectionWindow getConnectionWindow(Connection txn, ContactId c, public ConnectionWindow getConnectionWindow(Connection txn, ContactId c,
TransportId t) throws DbException { TransportIndex i) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT centre, bitmap FROM connectionWindows" String sql = "SELECT centre, bitmap FROM connectionWindows"
+ " WHERE contactId = ? AND transportId = ?"; + " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, t.getInt()); ps.setInt(2, i.getInt());
rs = ps.executeQuery(); rs = ps.executeQuery();
long centre = 0L; long centre = 0L;
int bitmap = 0; 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) public TransportProperties getLocalProperties(Connection txn, TransportId t)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT key, value FROM transports" String sql = "SELECT key, value FROM transportProperties"
+ " WHERE transportId = ?"; + " WHERE transportId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
TransportProperties p = new TransportProperties(); TransportProperties p = new TransportProperties();
while(rs.next()) p.put(rs.getString(1), rs.getString(2)); 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( public Collection<Transport> getLocalTransports(Connection txn)
Connection txn) throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT transportId, key, value FROM transports" String sql = "SELECT transports.transportId, index, key, value"
+ " ORDER BY transportId"; + " FROM transports LEFT OUTER JOIN transportProperties"
+ " ON transports.transportId"
+ " = transportProperties.transportId"
+ " ORDER BY transports.transportId";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<TransportId, TransportProperties> transports = Collection<Transport> transports = new ArrayList<Transport>();
new HashMap<TransportId, TransportProperties>();
TransportProperties p = null;
TransportId lastId = null; TransportId lastId = null;
Transport t = null;
while(rs.next()) { while(rs.next()) {
TransportId id = new TransportId(rs.getInt(1)); TransportId id = new TransportId(rs.getBytes(1));
if(!id.equals(lastId)) { if(!id.equals(lastId)) {
p = new TransportProperties(); t = new Transport(id, new TransportIndex(rs.getInt(2)));
transports.put(id, p); 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(); rs.close();
ps.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( public Map<ContactId, TransportProperties> getRemoteProperties(
Connection txn, TransportId t) throws DbException { Connection txn, TransportId t) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT contactId, key, value FROM contactTransports" String sql = "SELECT contactId, key, value"
+ " FROM contactTransportProperties"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
+ " ORDER BY contactId"; + " ORDER BY contactId";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<ContactId, TransportProperties> properties = Map<ContactId, TransportProperties> properties =
new HashMap<ContactId, TransportProperties>(); new HashMap<ContactId, TransportProperties>();
TransportProperties p = null;
ContactId lastId = null; ContactId lastId = null;
TransportProperties p = null;
while(rs.next()) { while(rs.next()) {
ContactId id = new ContactId(rs.getInt(1)); ContactId id = new ContactId(rs.getInt(1));
if(!id.equals(lastId)) { if(!id.equals(lastId)) {
@@ -2034,16 +2127,16 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Delete any existing config for the given transport // 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 = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
ps.executeUpdate(); ps.executeUpdate();
ps.close(); ps.close();
// Store the new config // Store the new config
sql = "INSERT INTO transportConfig (transportId, key, value)" sql = "INSERT INTO transportConfigs (transportId, key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
for(Entry<String, String> e : c.entrySet()) { for(Entry<String, String> e : c.entrySet()) {
ps.setString(2, e.getKey()); ps.setString(2, e.getKey());
ps.setString(3, e.getValue()); ps.setString(3, e.getValue());
@@ -2063,15 +2156,15 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
public void setConnectionWindow(Connection txn, ContactId c, public void setConnectionWindow(Connection txn, ContactId c,
TransportId t, ConnectionWindow w) throws DbException { TransportIndex i, ConnectionWindow w) throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT NULL FROM connectionWindows" String sql = "SELECT NULL FROM connectionWindows"
+ " WHERE contactId = ? AND transportId = ?"; + " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, t.getInt()); ps.setInt(2, i.getInt());
rs = ps.executeQuery(); rs = ps.executeQuery();
boolean found = rs.next(); boolean found = rs.next();
if(rs.next()) throw new DbStateException(); if(rs.next()) throw new DbStateException();
@@ -2080,23 +2173,23 @@ abstract class JdbcDatabase implements Database<Connection> {
if(found) { if(found) {
// A connection window row exists - update it // A connection window row exists - update it
sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?" sql = "UPDATE connectionWindows SET centre = ?, bitmap = ?"
+ " WHERE contactId = ? AND transportId = ?"; + " WHERE contactId = ? AND index = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setLong(1, w.getCentre()); ps.setLong(1, w.getCentre());
ps.setInt(2, w.getBitmap()); ps.setInt(2, w.getBitmap());
ps.setInt(3, c.getInt()); ps.setInt(3, c.getInt());
ps.setInt(4, t.getInt()); ps.setInt(4, i.getInt());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException(); if(affected != 1) throw new DbStateException();
ps.close(); ps.close();
} else { } else {
// No connection window row exists - create one // No connection window row exists - create one
sql = "INSERT INTO connectionWindows" sql = "INSERT INTO connectionWindows"
+ " (contactId, transportId, centre, bitmap, outgoing)" + " (contactId, index, centre, bitmap, outgoing)"
+ " VALUES(?, ?, ?, ?, ZERO())"; + " VALUES(?, ?, ?, ?, ZERO())";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, t.getInt()); ps.setInt(2, i.getInt());
ps.setLong(3, w.getCentre()); ps.setLong(3, w.getCentre());
ps.setInt(4, w.getBitmap()); ps.setInt(4, w.getBitmap());
int affected = ps.executeUpdate(); int affected = ps.executeUpdate();
@@ -2115,16 +2208,17 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Delete any existing properties for the given transport // 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 = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
ps.executeUpdate(); ps.executeUpdate();
ps.close(); ps.close();
// Store the new properties // Store the new properties
sql = "INSERT INTO transports (transportId, key, value)" sql = "INSERT INTO transportProperties (transportId, key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, t.getInt()); ps.setBytes(1, t.getBytes());
for(Entry<String, String> e : p.entrySet()) { for(Entry<String, String> e : p.entrySet()) {
ps.setString(2, e.getKey()); ps.setString(2, e.getKey());
ps.setString(3, e.getValue()); ps.setString(3, e.getValue());
@@ -2504,7 +2598,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
public void setTransports(Connection txn, ContactId c, public void setTransports(Connection txn, ContactId c,
Map<TransportId, TransportProperties> transports, long timestamp) Collection<Transport> transports, long timestamp)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
@@ -2527,24 +2621,45 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.executeUpdate(); ps.executeUpdate();
ps.close(); 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 // Store the new transports
sql = "INSERT INTO contactTransports" sql = "INSERT INTO contactTransports"
+ " (contactId, transportId, key, value)" + " (contactId, transportId, index) VALUES (?, ?, ?)";
+ " 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 = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
int batchSize = 0; int batchSize = 0;
for(Entry<TransportId, TransportProperties> e for(Transport t : transports) {
: transports.entrySet()) { ps.setBytes(2, t.getId().getBytes());
ps.setInt(2, e.getKey().getInt()); for(Entry<String, String> e1 : t.entrySet()) {
for(Entry<String, String> e1 : e.getValue().entrySet()) {
ps.setString(3, e1.getKey()); ps.setString(3, e1.getKey());
ps.setString(4, e1.getValue()); ps.setString(4, e1.getValue());
ps.addBatch(); ps.addBatch();
batchSize++; batchSize++;
} }
} }
int[] batchAffected = ps.executeBatch(); batchAffected = ps.executeBatch();
if(batchAffected.length != batchSize) throw new DbStateException(); if(batchAffected.length != batchSize) throw new DbStateException();
for(int i = 0; i < batchAffected.length; i++) { for(int i = 0; i < batchAffected.length; i++) {
if(batchAffected[i] != 1) throw new DbStateException(); if(batchAffected[i] != 1) throw new DbStateException();

View File

@@ -5,15 +5,14 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.List; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.invitation.InvitationCallback; import net.sf.briar.api.invitation.InvitationCallback;
import net.sf.briar.api.invitation.InvitationParameters; 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.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
import net.sf.briar.util.FileUtils; import net.sf.briar.util.FileUtils;
@@ -72,7 +71,7 @@ class InvitationWorker implements Runnable {
File invitationDat = new File(dir, "invitation.dat"); File invitationDat = new File(dir, "invitation.dat");
callback.encryptingFile(invitationDat); callback.encryptingFile(invitationDat);
// FIXME: Create a real invitation // FIXME: Create a real invitation
Map<TransportId, TransportProperties> transports; Collection<Transport> transports;
try { try {
transports = db.getLocalTransports(); transports = db.getLocalTransports();
} catch(DbException e) { } catch(DbException e) {
@@ -80,7 +79,7 @@ class InvitationWorker implements Runnable {
} }
FileOutputStream out = new FileOutputStream(invitationDat); FileOutputStream out = new FileOutputStream(invitationDat);
Writer w = writerFactory.createWriter(out); Writer w = writerFactory.createWriter(out);
w.writeMap(transports); w.writeList(transports);
out.flush(); out.flush();
out.close(); out.close();
return invitationDat; return invitationDat;

View File

@@ -8,12 +8,12 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException; 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.StreamPlugin;
import net.sf.briar.api.plugins.StreamPluginCallback; import net.sf.briar.api.plugins.StreamPluginCallback;
import net.sf.briar.api.plugins.StreamPluginFactory; 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.BatchTransportReader;
import net.sf.briar.api.transport.BatchTransportWriter; import net.sf.briar.api.transport.BatchTransportWriter;
import net.sf.briar.api.transport.ConnectionDispatcher; import net.sf.briar.api.transport.ConnectionDispatcher;
@@ -49,26 +51,32 @@ class PluginManagerImpl implements PluginManager {
"net.sf.briar.plugins.socket.SimpleSocketPluginFactory" "net.sf.briar.plugins.socket.SimpleSocketPluginFactory"
}; };
private final Executor executor; private static final int THREAD_POOL_SIZE = 5;
private final DatabaseComponent db; private final DatabaseComponent db;
private final Poller poller; private final Poller poller;
private final ConnectionDispatcher dispatcher; private final ConnectionDispatcher dispatcher;
private final UiCallback uiCallback; private final UiCallback uiCallback;
private final Executor executor;
private final List<BatchPlugin> batchPlugins; private final List<BatchPlugin> batchPlugins;
private final List<StreamPlugin> streamPlugins; private final List<StreamPlugin> streamPlugins;
@Inject @Inject
PluginManagerImpl(Executor executor, DatabaseComponent db, Poller poller, PluginManagerImpl(DatabaseComponent db, Poller poller,
ConnectionDispatcher dispatcher, UiCallback uiCallback) { ConnectionDispatcher dispatcher, UiCallback uiCallback) {
this.executor = executor;
this.db = db; this.db = db;
this.poller = poller; this.poller = poller;
this.dispatcher = dispatcher; this.dispatcher = dispatcher;
this.uiCallback = uiCallback; this.uiCallback = uiCallback;
executor = new ScheduledThreadPoolExecutor(THREAD_POOL_SIZE);
batchPlugins = new ArrayList<BatchPlugin>(); batchPlugins = new ArrayList<BatchPlugin>();
streamPlugins = new ArrayList<StreamPlugin>(); streamPlugins = new ArrayList<StreamPlugin>();
} }
public synchronized int getPluginCount() {
return batchPlugins.size() + streamPlugins.size();
}
public synchronized int startPlugins() { public synchronized int startPlugins() {
Set<TransportId> ids = new HashSet<TransportId>(); Set<TransportId> ids = new HashSet<TransportId>();
// Instantiate and start the batch plugins // Instantiate and start the batch plugins
@@ -81,8 +89,8 @@ class PluginManagerImpl implements PluginManager {
BatchPlugin plugin = factory.createPlugin(executor, callback); BatchPlugin plugin = factory.createPlugin(executor, callback);
if(plugin == null) { if(plugin == null) {
if(LOG.isLoggable(Level.INFO)) if(LOG.isLoggable(Level.INFO))
LOG.info(factory.getClass().getSimpleName() + LOG.info(factory.getClass().getSimpleName()
" did not create a plugin"); + " did not create a plugin");
continue; continue;
} }
TransportId id = plugin.getId(); TransportId id = plugin.getId();
@@ -91,7 +99,14 @@ class PluginManagerImpl implements PluginManager {
LOG.warning("Duplicate transport ID: " + id); LOG.warning("Duplicate transport ID: " + id);
continue; 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(); plugin.start();
batchPlugins.add(plugin); batchPlugins.add(plugin);
} catch(ClassCastException e) { } catch(ClassCastException e) {
@@ -122,7 +137,14 @@ class PluginManagerImpl implements PluginManager {
LOG.warning("Duplicate transport ID: " + id); LOG.warning("Duplicate transport ID: " + id);
continue; 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(); plugin.start();
streamPlugins.add(plugin); streamPlugins.add(plugin);
} catch(ClassCastException e) { } catch(ClassCastException e) {
@@ -138,7 +160,7 @@ class PluginManagerImpl implements PluginManager {
plugins.addAll(batchPlugins); plugins.addAll(batchPlugins);
plugins.addAll(streamPlugins); plugins.addAll(streamPlugins);
poller.startPolling(plugins); poller.startPolling(plugins);
// Return the number of plugins started // Return the number of plugins successfully started
return batchPlugins.size() + streamPlugins.size(); return batchPlugins.size() + streamPlugins.size();
} }
@@ -164,17 +186,19 @@ class PluginManagerImpl implements PluginManager {
} }
} }
streamPlugins.clear(); streamPlugins.clear();
// Return the number of plugins stopped // Return the number of plugins successfully stopped
return stopped; return stopped;
} }
private abstract class PluginCallbackImpl implements PluginCallback { private abstract class PluginCallbackImpl implements PluginCallback {
protected volatile TransportId id = null; protected volatile TransportId id = null;
protected volatile TransportIndex index = null;
protected void setId(TransportId id) { protected void init(TransportId id, TransportIndex index) {
assert this.id == null; assert this.id == null && this.index == null;
this.id = id; this.id = id;
this.index = index;
} }
public TransportConfig getConfig() { public TransportConfig getConfig() {
@@ -219,20 +243,20 @@ class PluginManagerImpl implements PluginManager {
public void setLocalProperties(TransportProperties p) { public void setLocalProperties(TransportProperties p) {
assert id != null; assert id != null;
if(p.size() > TransportUpdate.MAX_PROPERTIES_PER_PLUGIN) { if(p.size() > ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT) {
if(LOG.isLoggable(Level.WARNING)) if(LOG.isLoggable(Level.WARNING))
LOG.warning("Plugin " + id + " set too many properties"); LOG.warning("Plugin " + id + " set too many properties");
return; return;
} }
for(String s : p.keySet()) { 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)) if(LOG.isLoggable(Level.WARNING))
LOG.warning("Plugin " + id + " set long key: " + s); LOG.warning("Plugin " + id + " set long key: " + s);
return; return;
} }
} }
for(String s : p.values()) { 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)) if(LOG.isLoggable(Level.WARNING))
LOG.warning("Plugin " + id + " set long value: " + s); LOG.warning("Plugin " + id + " set long value: " + s);
return; return;
@@ -267,8 +291,8 @@ class PluginManagerImpl implements PluginManager {
} }
public void writerCreated(ContactId c, BatchTransportWriter w) { public void writerCreated(ContactId c, BatchTransportWriter w) {
assert id != null; assert index != null;
dispatcher.dispatchWriter(id, c, w); dispatcher.dispatchWriter(index, c, w);
} }
} }
@@ -282,8 +306,8 @@ class PluginManagerImpl implements PluginManager {
public void outgoingConnectionCreated(ContactId c, public void outgoingConnectionCreated(ContactId c,
StreamTransportConnection s) { StreamTransportConnection s) {
assert id != null; assert index != null;
dispatcher.dispatchOutgoingConnection(id, c, s); dispatcher.dispatchOutgoingConnection(index, c, s);
} }
} }
} }

View File

@@ -73,8 +73,6 @@ class PollerImpl implements Poller, Runnable {
public int compareTo(PollTime p) { public int compareTo(PollTime p) {
if(time < p.time) return -1; if(time < p.time) return -1;
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; return 0;
} }
} }

View File

@@ -18,10 +18,10 @@ import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier; import javax.microedition.io.StreamConnectionNotifier;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.plugins.StreamPlugin; import net.sf.briar.api.plugins.StreamPlugin;
import net.sf.briar.api.plugins.StreamPluginCallback; 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.api.transport.StreamTransportConnection;
import net.sf.briar.plugins.AbstractPlugin; import net.sf.briar.plugins.AbstractPlugin;
import net.sf.briar.util.OsUtils; import net.sf.briar.util.OsUtils;
@@ -29,7 +29,9 @@ import net.sf.briar.util.StringUtils;
class BluetoothPlugin extends AbstractPlugin implements StreamPlugin { 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 TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG = private static final Logger LOG =

View File

@@ -9,13 +9,16 @@ import java.util.concurrent.Executor;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.plugins.BatchPluginCallback; import net.sf.briar.api.plugins.BatchPluginCallback;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.StringUtils;
class RemovableDrivePlugin extends FilePlugin class RemovableDrivePlugin extends FilePlugin
implements RemovableDriveMonitor.Callback { 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 TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG = private static final Logger LOG =

View File

@@ -14,14 +14,17 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.plugins.StreamPluginCallback; 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.api.transport.StreamTransportConnection;
import net.sf.briar.util.StringUtils;
class SimpleSocketPlugin extends SocketPlugin { 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 TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG = private static final Logger LOG =

View File

@@ -32,8 +32,7 @@ abstract class SocketPlugin extends AbstractPlugin implements StreamPlugin {
protected abstract SocketAddress getLocalSocketAddress(); protected abstract SocketAddress getLocalSocketAddress();
protected abstract SocketAddress getRemoteSocketAddress(ContactId c); protected abstract SocketAddress getRemoteSocketAddress(ContactId c);
protected SocketPlugin(Executor executor, protected SocketPlugin(Executor executor, StreamPluginCallback callback) {
StreamPluginCallback callback) {
super(executor); super(executor);
this.callback = callback; this.callback = callback;
} }

View File

@@ -8,6 +8,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.AuthorId; 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.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
@@ -28,7 +29,9 @@ class AuthorFactoryImpl implements AuthorFactory {
throws IOException { throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); 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 messageDigest = crypto.getMessageDigest();
messageDigest.reset(); messageDigest.reset();
messageDigest.update(out.toByteArray()); messageDigest.update(out.toByteArray());

View File

@@ -1,11 +1,7 @@
package net.sf.briar.protocol; package net.sf.briar.protocol;
import java.io.IOException;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorId; 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 { class AuthorImpl implements Author {
@@ -30,10 +26,4 @@ class AuthorImpl implements Author {
public byte[] getPublicKey() { public byte[] getPublicKey() {
return publicKey; return publicKey;
} }
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.AUTHOR);
w.writeString(name);
w.writeBytes(publicKey);
}
} }

View File

@@ -7,6 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Author; import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.AuthorFactory; import net.sf.briar.api.protocol.AuthorFactory;
import net.sf.briar.api.protocol.AuthorId; 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.protocol.Types;
import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.Reader;
@@ -28,8 +29,8 @@ class AuthorReader implements ObjectReader<Author> {
// Read and digest the data // Read and digest the data
r.addConsumer(digesting); r.addConsumer(digesting);
r.readUserDefinedId(Types.AUTHOR); r.readUserDefinedId(Types.AUTHOR);
String name = r.readString(Author.MAX_NAME_LENGTH); String name = r.readString(ProtocolConstants.MAX_AUTHOR_NAME_LENGTH);
byte[] publicKey = r.readBytes(Author.MAX_PUBLIC_KEY_LENGTH); byte[] publicKey = r.readBytes(ProtocolConstants.MAX_PUBLIC_KEY_LENGTH);
r.removeConsumer(digesting); r.removeConsumer(digesting);
// Build and return the author // Build and return the author
AuthorId id = new AuthorId(messageDigest.digest()); AuthorId id = new AuthorId(messageDigest.digest());

View File

@@ -8,6 +8,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId; 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.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
@@ -27,7 +28,10 @@ class GroupFactoryImpl implements GroupFactory {
public Group createGroup(String name, byte[] publicKey) throws IOException { public Group createGroup(String name, byte[] publicKey) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out); 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 messageDigest = crypto.getMessageDigest();
messageDigest.reset(); messageDigest.reset();
messageDigest.update(out.toByteArray()); messageDigest.update(out.toByteArray());

View File

@@ -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);
}
}

View File

@@ -1,11 +1,7 @@
package net.sf.briar.protocol; package net.sf.briar.protocol;
import java.io.IOException;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; 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 { class GroupImpl implements Group {
@@ -31,13 +27,6 @@ class GroupImpl implements Group {
return publicKey; 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 @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof Group && id.equals(((Group) o).getId()); return o instanceof Group && id.equals(((Group) o).getId());

View File

@@ -7,6 +7,7 @@ import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.GroupId; 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.protocol.Types;
import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.Reader;
@@ -28,10 +29,10 @@ class GroupReader implements ObjectReader<Group> {
// Read and digest the data // Read and digest the data
r.addConsumer(digesting); r.addConsumer(digesting);
r.readUserDefinedId(Types.GROUP); r.readUserDefinedId(Types.GROUP);
String name = r.readString(Group.MAX_NAME_LENGTH); String name = r.readString(ProtocolConstants.MAX_GROUP_NAME_LENGTH);
byte[] publicKey = null; byte[] publicKey = null;
if(r.hasNull()) r.readNull(); 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); r.removeConsumer(digesting);
// Build and return the group // Build and return the group
GroupId id = new GroupId(messageDigest.digest()); GroupId id = new GroupId(messageDigest.digest());

View File

@@ -14,10 +14,12 @@ import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; 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.MessageId;
import net.sf.briar.api.protocol.ProtocolConstants; import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.Types; 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.Consumer;
import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
@@ -30,14 +32,19 @@ class MessageEncoderImpl implements MessageEncoder {
private final SecureRandom random; private final SecureRandom random;
private final MessageDigest messageDigest; private final MessageDigest messageDigest;
private final WriterFactory writerFactory; private final WriterFactory writerFactory;
private final AuthorWriter authorWriter;
private final GroupWriter groupWriter;
@Inject @Inject
MessageEncoderImpl(CryptoComponent crypto, WriterFactory writerFactory) { MessageEncoderImpl(CryptoComponent crypto, WriterFactory writerFactory,
AuthorWriter authorWriter, GroupWriter groupWriter) {
authorSignature = crypto.getSignature(); authorSignature = crypto.getSignature();
groupSignature = crypto.getSignature(); groupSignature = crypto.getSignature();
random = crypto.getSecureRandom(); random = crypto.getSecureRandom();
messageDigest = crypto.getMessageDigest(); messageDigest = crypto.getMessageDigest();
this.writerFactory = writerFactory; this.writerFactory = writerFactory;
this.authorWriter = authorWriter;
this.groupWriter = groupWriter;
} }
public Message encodeMessage(MessageId parent, String subject, byte[] body) public Message encodeMessage(MessageId parent, String subject, byte[] body)
@@ -74,9 +81,9 @@ class MessageEncoderImpl implements MessageEncoder {
if((group == null || group.getPublicKey() == null) != if((group == null || group.getPublicKey() == null) !=
(groupKey == null)) (groupKey == null))
throw new IllegalArgumentException(); 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(); throw new IllegalArgumentException();
if(body.length > Message.MAX_BODY_LENGTH) if(body.length > ProtocolConstants.MAX_BODY_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -102,15 +109,15 @@ class MessageEncoderImpl implements MessageEncoder {
// Write the message // Write the message
w.writeUserDefinedId(Types.MESSAGE); w.writeUserDefinedId(Types.MESSAGE);
if(parent == null) w.writeNull(); if(parent == null) w.writeNull();
else parent.writeTo(w); else w.writeBytes(parent.getBytes());
if(group == null) w.writeNull(); if(group == null) w.writeNull();
else group.writeTo(w); else groupWriter.writeGroup(w, group);
if(author == null) w.writeNull(); if(author == null) w.writeNull();
else author.writeTo(w); else authorWriter.writeAuthor(w, author);
w.writeString(subject); w.writeString(subject);
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
w.writeInt64(timestamp); w.writeInt64(timestamp);
byte[] salt = new byte[Message.SALT_LENGTH]; byte[] salt = new byte[ProtocolConstants.SALT_LENGTH];
random.nextBytes(salt); random.nextBytes(salt);
w.writeBytes(salt); w.writeBytes(salt);
w.writeBytes(body); w.writeBytes(body);
@@ -121,7 +128,7 @@ class MessageEncoderImpl implements MessageEncoder {
} else { } else {
w.removeConsumer(authorConsumer); w.removeConsumer(authorConsumer);
byte[] sig = authorSignature.sign(); byte[] sig = authorSignature.sign();
if(sig.length > Message.MAX_SIGNATURE_LENGTH) if(sig.length > ProtocolConstants.MAX_SIGNATURE_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
w.writeBytes(sig); w.writeBytes(sig);
} }
@@ -131,7 +138,7 @@ class MessageEncoderImpl implements MessageEncoder {
} else { } else {
w.removeConsumer(groupConsumer); w.removeConsumer(groupConsumer);
byte[] sig = groupSignature.sign(); byte[] sig = groupSignature.sign();
if(sig.length > Message.MAX_SIGNATURE_LENGTH) if(sig.length > ProtocolConstants.MAX_SIGNATURE_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
w.writeBytes(sig); w.writeBytes(sig);
} }

View File

@@ -4,6 +4,7 @@ import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; import net.sf.briar.api.protocol.MessageId;
import net.sf.briar.api.protocol.ProtocolConstants;
/** A simple in-memory implementation of a message. */ /** A simple in-memory implementation of a message. */
class MessageImpl implements Message { class MessageImpl implements Message {
@@ -21,7 +22,7 @@ class MessageImpl implements Message {
int bodyStart, int bodyLength) { int bodyStart, int bodyLength) {
if(bodyStart + bodyLength > raw.length) if(bodyStart + bodyLength > raw.length)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if(bodyLength > Message.MAX_BODY_LENGTH) if(bodyLength > ProtocolConstants.MAX_BODY_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
this.id = id; this.id = id;
this.parent = parent; this.parent = parent;

View File

@@ -77,15 +77,15 @@ class MessageReader implements ObjectReader<Message> {
r.removeObjectReader(Types.AUTHOR); r.removeObjectReader(Types.AUTHOR);
} }
// Read the subject // Read the subject
String subject = r.readString(Message.MAX_SUBJECT_LENGTH); String subject = r.readString(ProtocolConstants.MAX_SUBJECT_LENGTH);
// Read the timestamp // Read the timestamp
long timestamp = r.readInt64(); long timestamp = r.readInt64();
if(timestamp < 0L) throw new FormatException(); if(timestamp < 0L) throw new FormatException();
// Read the salt // Read the salt
byte[] salt = r.readBytes(Message.SALT_LENGTH); byte[] salt = r.readBytes(ProtocolConstants.SALT_LENGTH);
if(salt.length != Message.SALT_LENGTH) throw new FormatException(); if(salt.length != ProtocolConstants.SALT_LENGTH) throw new FormatException();
// Read the message body // 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 // Record the offset of the body within the message
int bodyStart = (int) counting.getCount() - body.length; int bodyStart = (int) counting.getCount() - body.length;
// Record the length of the data covered by the author's signature // 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 // Read the author's signature, if there is one
byte[] authorSig = null; byte[] authorSig = null;
if(author == null) r.readNull(); 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 // Record the length of the data covered by the group's signature
int signedByGroup = (int) counting.getCount(); int signedByGroup = (int) counting.getCount();
// Read the group's signature, if there is one // Read the group's signature, if there is one
byte[] groupSig = null; byte[] groupSig = null;
if(group == null || group.getPublicKey() == null) r.readNull(); 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 // That's all, folks
r.removeConsumer(counting); r.removeConsumer(counting);
r.removeConsumer(copying); r.removeConsumer(copying);

View File

@@ -9,13 +9,13 @@ import net.sf.briar.api.protocol.BatchId;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.Message; 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.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
import net.sf.briar.api.protocol.writers.MessageEncoder;
import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.ObjectReader;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -33,8 +33,8 @@ public class ProtocolModule extends AbstractModule {
bind(OfferFactory.class).to(OfferFactoryImpl.class); bind(OfferFactory.class).to(OfferFactoryImpl.class);
bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class); bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class);
bind(RequestFactory.class).to(RequestFactoryImpl.class); bind(RequestFactory.class).to(RequestFactoryImpl.class);
bind(SubscriptionFactory.class).to(SubscriptionFactoryImpl.class); bind(SubscriptionUpdateFactory.class).to(SubscriptionUpdateFactoryImpl.class);
bind(TransportFactory.class).to(TransportFactoryImpl.class); bind(TransportUpdateFactory.class).to(TransportUpdateFactoryImpl.class);
} }
@Provides @Provides
@@ -94,13 +94,13 @@ public class ProtocolModule extends AbstractModule {
@Provides @Provides
ObjectReader<SubscriptionUpdate> getSubscriptionReader( ObjectReader<SubscriptionUpdate> getSubscriptionReader(
ObjectReader<Group> groupReader, ObjectReader<Group> groupReader,
SubscriptionFactory subscriptionFactory) { SubscriptionUpdateFactory subscriptionFactory) {
return new SubscriptionReader(groupReader, subscriptionFactory); return new SubscriptionUpdateReader(groupReader, subscriptionFactory);
} }
@Provides @Provides
ObjectReader<TransportUpdate> getTransportReader( ObjectReader<TransportUpdate> getTransportReader(
TransportFactory transportFactory) { TransportUpdateFactory transportFactory) {
return new TransportReader(transportFactory); return new TransportUpdateReader(transportFactory);
} }
} }

View File

@@ -5,7 +5,7 @@ import java.util.Map;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
interface SubscriptionFactory { interface SubscriptionUpdateFactory {
SubscriptionUpdate createSubscriptions(Map<Group, Long> subs, SubscriptionUpdate createSubscriptions(Map<Group, Long> subs,
long timestamp); long timestamp);

View File

@@ -5,7 +5,7 @@ import java.util.Map;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.SubscriptionUpdate; import net.sf.briar.api.protocol.SubscriptionUpdate;
class SubscriptionFactoryImpl implements SubscriptionFactory { class SubscriptionUpdateFactoryImpl implements SubscriptionUpdateFactory {
public SubscriptionUpdate createSubscriptions(Map<Group, Long> subs, public SubscriptionUpdate createSubscriptions(Map<Group, Long> subs,
long timestamp) { long timestamp) {

View File

@@ -12,13 +12,13 @@ import net.sf.briar.api.serial.Consumer;
import net.sf.briar.api.serial.ObjectReader; import net.sf.briar.api.serial.ObjectReader;
import net.sf.briar.api.serial.Reader; import net.sf.briar.api.serial.Reader;
class SubscriptionReader implements ObjectReader<SubscriptionUpdate> { class SubscriptionUpdateReader implements ObjectReader<SubscriptionUpdate> {
private final ObjectReader<Group> groupReader; private final ObjectReader<Group> groupReader;
private final SubscriptionFactory subscriptionFactory; private final SubscriptionUpdateFactory subscriptionFactory;
SubscriptionReader(ObjectReader<Group> groupReader, SubscriptionUpdateReader(ObjectReader<Group> groupReader,
SubscriptionFactory subscriptionFactory) { SubscriptionUpdateFactory subscriptionFactory) {
this.groupReader = groupReader; this.groupReader = groupReader;
this.subscriptionFactory = subscriptionFactory; this.subscriptionFactory = subscriptionFactory;
} }

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -1,23 +1,22 @@
package net.sf.briar.protocol; package net.sf.briar.protocol;
import java.util.Map; import java.util.Collection;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.protocol.TransportUpdate; import net.sf.briar.api.protocol.TransportUpdate;
class TransportUpdateImpl implements TransportUpdate { class TransportUpdateImpl implements TransportUpdate {
private final Map<TransportId, TransportProperties> transports; private final Collection<Transport> transports;
private final long timestamp; private final long timestamp;
TransportUpdateImpl(Map<TransportId, TransportProperties> transports, TransportUpdateImpl(Collection<Transport> transports,
long timestamp) { long timestamp) {
this.transports = transports; this.transports = transports;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public Map<TransportId, TransportProperties> getTransports() { public Collection<Transport> getTransports() {
return transports; return transports;
} }

View File

@@ -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);
}
}
}

View File

@@ -41,7 +41,8 @@ class AckWriterImpl implements AckWriter {
int overhead = started ? footerLength : headerLength + footerLength; int overhead = started ? footerLength : headerLength + footerLength;
if(capacity < idLength + overhead) return false; if(capacity < idLength + overhead) return false;
if(!started) start(); if(!started) start();
b.writeTo(w); w.writeUserDefinedId(Types.BATCH_ID);
w.writeBytes(b.getBytes());
capacity -= idLength; capacity -= idLength;
return true; return true;
} }

View File

@@ -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());
}
}

View File

@@ -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);
}
}

View File

@@ -41,7 +41,8 @@ class OfferWriterImpl implements OfferWriter {
int overhead = started ? footerLength : headerLength + footerLength; int overhead = started ? footerLength : headerLength + footerLength;
if(capacity < idLength + overhead) return false; if(capacity < idLength + overhead) return false;
if(!started) start(); if(!started) start();
m.writeTo(w); w.writeUserDefinedId(Types.MESSAGE_ID);
w.writeBytes(m.getBytes());
capacity -= idLength; capacity -= idLength;
return true; return true;
} }

View File

@@ -1,5 +1,7 @@
package net.sf.briar.protocol.writers; 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 net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
@@ -8,6 +10,8 @@ public class ProtocolWritersModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(AuthorWriter.class).to(AuthorWriterImpl.class);
bind(GroupWriter.class).to(GroupWriterImpl.class);
bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class); bind(ProtocolWriterFactory.class).to(ProtocolWriterFactoryImpl.class);
} }
} }

View File

@@ -3,9 +3,11 @@ package net.sf.briar.protocol.writers;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.Types; 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.protocol.writers.SubscriptionWriter;
import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory; import net.sf.briar.api.serial.WriterFactory;
@@ -14,16 +16,23 @@ class SubscriptionWriterImpl implements SubscriptionWriter {
private final OutputStream out; private final OutputStream out;
private final Writer w; private final Writer w;
private final GroupWriter groupWriter;
SubscriptionWriterImpl(OutputStream out, WriterFactory writerFactory) { SubscriptionWriterImpl(OutputStream out, WriterFactory writerFactory) {
this.out = out; this.out = out;
w = writerFactory.createWriter(out); w = writerFactory.createWriter(out);
groupWriter = new GroupWriterImpl();
} }
public void writeSubscriptions(Map<Group, Long> subs, long timestamp) public void writeSubscriptions(Map<Group, Long> subs, long timestamp)
throws IOException { throws IOException {
w.writeUserDefinedId(Types.SUBSCRIPTION_UPDATE); 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); w.writeInt64(timestamp);
out.flush(); out.flush();
} }

View File

@@ -2,11 +2,9 @@ package net.sf.briar.protocol.writers;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Map; import java.util.Collection;
import java.util.Map.Entry;
import net.sf.briar.api.TransportId; import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.protocol.Types; import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.protocol.writers.TransportWriter; import net.sf.briar.api.protocol.writers.TransportWriter;
import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.Writer;
@@ -22,15 +20,15 @@ class TransportWriterImpl implements TransportWriter {
w = writerFactory.createWriter(out); w = writerFactory.createWriter(out);
} }
public void writeTransports( public void writeTransports(Collection<Transport> transports,
Map<TransportId, TransportProperties> transports, long timestamp) long timestamp) throws IOException {
throws IOException {
w.writeUserDefinedId(Types.TRANSPORT_UPDATE); w.writeUserDefinedId(Types.TRANSPORT_UPDATE);
w.writeListStart(); w.writeListStart();
for(Entry<TransportId, TransportProperties> e : transports.entrySet()) { for(Transport p : transports) {
w.writeUserDefinedId(Types.TRANSPORT_PROPERTIES); w.writeUserDefinedId(Types.TRANSPORT);
w.writeInt32(e.getKey().getInt()); w.writeBytes(p.getId().getBytes());
w.writeMap(e.getValue()); w.writeInt32(p.getIndex().getInt());
w.writeMap(p);
} }
w.writeListEnd(); w.writeListEnd();
w.writeInt64(timestamp); w.writeInt64(timestamp);

View File

@@ -10,7 +10,6 @@ import java.util.Map.Entry;
import net.sf.briar.api.Bytes; import net.sf.briar.api.Bytes;
import net.sf.briar.api.serial.Consumer; import net.sf.briar.api.serial.Consumer;
import net.sf.briar.api.serial.Writable;
import net.sf.briar.api.serial.Writer; import net.sf.briar.api.serial.Writer;
class WriterImpl implements Writer { class WriterImpl implements Writer {
@@ -139,8 +138,7 @@ class WriterImpl implements Writer {
} }
private void writeObject(Object o) throws IOException { private void writeObject(Object o) throws IOException {
if(o instanceof Writable) ((Writable) o).writeTo(this); if(o instanceof Boolean) writeBoolean((Boolean) o);
else if(o instanceof Boolean) writeBoolean((Boolean) o);
else if(o instanceof Byte) writeIntAny((Byte) o); else if(o instanceof Byte) writeIntAny((Byte) o);
else if(o instanceof Short) writeIntAny((Short) o); else if(o instanceof Short) writeIntAny((Short) o);
else if(o instanceof Integer) writeIntAny((Integer) o); else if(o instanceof Integer) writeIntAny((Integer) o);

View File

@@ -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;
}
}

View File

@@ -2,41 +2,41 @@ package net.sf.briar.transport;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.db.DbException; 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.BatchConnectionFactory;
import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportReader;
import net.sf.briar.api.transport.BatchTransportWriter; 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.ConnectionDispatcher;
import net.sf.briar.api.transport.ConnectionRecogniser; 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.StreamConnectionFactory;
import net.sf.briar.api.transport.StreamTransportConnection; import net.sf.briar.api.transport.StreamTransportConnection;
import net.sf.briar.api.transport.TransportConstants; import net.sf.briar.api.transport.TransportConstants;
import com.google.inject.Inject;
public class ConnectionDispatcherImpl implements ConnectionDispatcher { public class ConnectionDispatcherImpl implements ConnectionDispatcher {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ConnectionDispatcherImpl.class.getName()); Logger.getLogger(ConnectionDispatcherImpl.class.getName());
private final ConnectionRecogniserFactory recFactory; private final ConnectionRecogniser recogniser;
private final BatchConnectionFactory batchConnFactory; private final BatchConnectionFactory batchConnFactory;
private final StreamConnectionFactory streamConnFactory; private final StreamConnectionFactory streamConnFactory;
private final Map<TransportId, ConnectionRecogniser> recognisers;
ConnectionDispatcherImpl(ConnectionRecogniserFactory recFactory, @Inject
ConnectionDispatcherImpl(ConnectionRecogniser recogniser,
BatchConnectionFactory batchConnFactory, BatchConnectionFactory batchConnFactory,
StreamConnectionFactory streamConnFactory) { StreamConnectionFactory streamConnFactory) {
this.recFactory = recFactory; this.recogniser = recogniser;
this.batchConnFactory = batchConnFactory; this.batchConnFactory = batchConnFactory;
this.streamConnFactory = streamConnFactory; this.streamConnFactory = streamConnFactory;
recognisers = new HashMap<TransportId, ConnectionRecogniser>();
} }
public void dispatchReader(TransportId t, BatchTransportReader r) { public void dispatchReader(TransportId t, BatchTransportReader r) {
@@ -49,21 +49,27 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
r.dispose(false); r.dispose(false);
return; return;
} }
// Get the contact ID, or null if the IV wasn't expected // Get the connection context, or null if the IV wasn't expected
ContactId c; ConnectionContext ctx;
try { try {
ConnectionRecogniser rec = getRecogniser(t); ctx = recogniser.acceptConnection(encryptedIv);
c = rec.acceptConnection(encryptedIv);
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage()); if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
r.dispose(false); r.dispose(false);
return; return;
} }
if(c == null) { if(ctx == null) {
r.dispose(false); r.dispose(false);
return; 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 { private byte[] readIv(InputStream in) throws IOException {
@@ -77,20 +83,9 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
return b; return b;
} }
private ConnectionRecogniser getRecogniser(TransportId t) { public void dispatchWriter(TransportIndex i, ContactId c,
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,
BatchTransportWriter w) { BatchTransportWriter w) {
batchConnFactory.createOutgoingConnection(t, c, w); batchConnFactory.createOutgoingConnection(i, c, w);
} }
public void dispatchIncomingConnection(TransportId t, public void dispatchIncomingConnection(TransportId t,
@@ -104,25 +99,31 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
s.dispose(false); s.dispose(false);
return; return;
} }
// Get the contact ID, or null if the IV wasn't expected // Get the connection context, or null if the IV wasn't expected
ContactId c; ConnectionContext ctx;
try { try {
ConnectionRecogniser rec = getRecogniser(t); ctx = recogniser.acceptConnection(encryptedIv);
c = rec.acceptConnection(encryptedIv);
} catch(DbException e) { } catch(DbException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage()); if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
s.dispose(false); s.dispose(false);
return; return;
} }
if(c == null) { if(ctx == null) {
s.dispose(false); s.dispose(false);
return; return;
} }
streamConnFactory.createIncomingConnection(t, c, s, encryptedIv); if(!t.equals(ctx.getTransportId())) {
if(LOG.isLoggable(Level.WARNING))
LOG.warning("Connection has unexpected transport ID");
s.dispose(false);
return;
}
streamConnFactory.createIncomingConnection(ctx.getTransportIndex(),
ctx.getContactId(), s, encryptedIv);
} }
public void dispatchOutgoingConnection(TransportId t, ContactId c, public void dispatchOutgoingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s) { StreamTransportConnection s) {
streamConnFactory.createOutgoingConnection(t, c, s); streamConnFactory.createOutgoingConnection(i, c, s);
} }
} }

View File

@@ -9,8 +9,8 @@ import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.crypto.CryptoComponent; 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.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -26,7 +26,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
} }
public ConnectionReader createConnectionReader(InputStream in, public ConnectionReader createConnectionReader(InputStream in,
TransportId t, byte[] encryptedIv, byte[] secret) { TransportIndex i, byte[] encryptedIv, byte[] secret) {
// Decrypt the IV // Decrypt the IV
Cipher ivCipher = crypto.getIvCipher(); Cipher ivCipher = crypto.getIvCipher();
SecretKey ivKey = crypto.deriveIncomingIvKey(secret); SecretKey ivKey = crypto.deriveIncomingIvKey(secret);
@@ -42,21 +42,22 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
throw new IllegalArgumentException(badKey); throw new IllegalArgumentException(badKey);
} }
// Validate the IV // Validate the IV
if(!IvEncoder.validateIv(iv, true, t)) if(!IvEncoder.validateIv(iv, true, i))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// Copy the connection number // Copy the connection number
long connection = IvEncoder.getConnectionNumber(iv); long connection = IvEncoder.getConnectionNumber(iv);
return createConnectionReader(in, true, t, connection, secret); return createConnectionReader(in, true, i, connection, secret);
} }
public ConnectionReader createConnectionReader(InputStream in, public ConnectionReader createConnectionReader(InputStream in,
TransportId t, long connection, byte[] secret) { TransportIndex i, long connection, byte[] secret) {
return createConnectionReader(in, false, t, connection, secret); return createConnectionReader(in, false, i, connection, secret);
} }
private ConnectionReader createConnectionReader(InputStream in, private ConnectionReader createConnectionReader(InputStream in,
boolean initiator, TransportId t, long connection, byte[] secret) { boolean initiator, TransportIndex i, long connection,
byte[] iv = IvEncoder.encodeIv(initiator, t, connection); byte[] secret) {
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
// Create the decrypter // Create the decrypter
Cipher frameCipher = crypto.getFrameCipher(); Cipher frameCipher = crypto.getFrameCipher();
SecretKey frameKey = crypto.deriveIncomingFrameKey(secret); SecretKey frameKey = crypto.deriveIncomingFrameKey(secret);

View File

@@ -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);
}
}

View File

@@ -3,8 +3,13 @@ package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
@@ -13,128 +18,178 @@ import javax.crypto.SecretKey;
import net.sf.briar.api.Bytes; import net.sf.briar.api.Bytes;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.NoSuchContactException; 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.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; 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.ConnectionRecogniser;
import net.sf.briar.api.transport.ConnectionWindow; import net.sf.briar.api.transport.ConnectionWindow;
import com.google.inject.Inject;
class ConnectionRecogniserImpl implements ConnectionRecogniser, class ConnectionRecogniserImpl implements ConnectionRecogniser,
DatabaseListener { DatabaseListener {
private final TransportId id; private static final Logger LOG =
Logger.getLogger(ConnectionRecogniserImpl.class.getName());
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final DatabaseComponent db; private final DatabaseComponent db;
private final Map<Bytes, ContactId> ivToContact; private final Cipher ivCipher;
private final Map<Bytes, Long> ivToConnectionNumber; private final Map<Bytes, ConnectionContext> expected;
private final Map<ContactId, Map<Long, Bytes>> contactToIvs; private final Collection<TransportId> localTransportIds;
private final Map<ContactId, Cipher> contactToCipher;
private final Map<ContactId, ConnectionWindow> contactToWindow;
private boolean initialised = false; private boolean initialised = false;
ConnectionRecogniserImpl(TransportId id, CryptoComponent crypto, @Inject
DatabaseComponent db) { ConnectionRecogniserImpl(CryptoComponent crypto, DatabaseComponent db) {
this.id = id;
this.crypto = crypto; this.crypto = crypto;
this.db = db; this.db = db;
// FIXME: There's probably a tidier way of maintaining all this state ivCipher = crypto.getIvCipher();
ivToContact = new HashMap<Bytes, ContactId>(); expected = new HashMap<Bytes, ConnectionContext>();
ivToConnectionNumber = new HashMap<Bytes, Long>(); localTransportIds = new ArrayList<TransportId>();
contactToIvs = new HashMap<ContactId, Map<Long, Bytes>>();
contactToCipher = new HashMap<ContactId, Cipher>();
contactToWindow = new HashMap<ContactId, ConnectionWindow>();
db.addListener(this); db.addListener(this);
} }
private synchronized void initialise() throws DbException { private synchronized void initialise() throws DbException {
for(Transport t : db.getLocalTransports()) {
localTransportIds.add(t.getId());
}
for(ContactId c : db.getContacts()) { for(ContactId c : db.getContacts()) {
try { try {
// Initialise and store the contact's IV cipher calculateIvs(c);
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);
} catch(NoSuchContactException e) { } catch(NoSuchContactException e) {
// The contact was removed after the call to getContacts() // The contact was removed - clean up in eventOccurred()
continue;
} }
} }
initialised = true; initialised = true;
} }
private synchronized byte[] encryptIv(ContactId c, long connection) { private synchronized void calculateIvs(ContactId c) throws DbException {
byte[] iv = IvEncoder.encodeIv(true, id, connection); SecretKey ivKey = crypto.deriveIncomingIvKey(db.getSharedSecret(c));
Cipher cipher = contactToCipher.get(c); for(TransportId t : localTransportIds) {
assert cipher != null; 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 { try {
return cipher.doFinal(iv); ivCipher.init(Cipher.ENCRYPT_MODE, ivKey);
return ivCipher.doFinal(iv);
} catch(BadPaddingException badCipher) { } catch(BadPaddingException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);
} catch(IllegalBlockSizeException badCipher) { } catch(IllegalBlockSizeException badCipher) {
throw new RuntimeException(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 { throws DbException {
if(encryptedIv.length != IV_LENGTH) if(encryptedIv.length != IV_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
if(!initialised) initialise(); if(!initialised) initialise();
Bytes b = new Bytes(encryptedIv); ConnectionContext ctx = expected.remove(new Bytes(encryptedIv));
ContactId contactId = ivToContact.remove(b); if(ctx == null) return null; // The IV was not expected
Long connection = ivToConnectionNumber.remove(b); try {
assert (contactId == null) == (connection == null); ContactId c = ctx.getContactId();
if(contactId == null) return null; TransportIndex i = ctx.getTransportIndex();
// The IV was expected - update and save the connection window // Update the connection window
ConnectionWindow w = contactToWindow.get(contactId); ConnectionWindow w = db.getConnectionWindow(c, i);
assert w != null; w.setSeen(ctx.getConnectionNumber());
w.setSeen(connection); db.setConnectionWindow(c, i, w);
db.setConnectionWindow(contactId, id, w); // Update the set of expected IVs
// Update the set of expected IVs Iterator<ConnectionContext> it = expected.values().iterator();
Map<Long, Bytes> oldIvs = contactToIvs.remove(contactId); while(it.hasNext()) {
assert oldIvs != null; ConnectionContext ctx1 = it.next();
assert oldIvs.containsKey(connection); ContactId c1 = ctx1.getContactId();
Map<Long, Bytes> newIvs = new HashMap<Long, Bytes>(); TransportIndex i1 = ctx1.getTransportIndex();
for(Long unseen : w.getUnseenConnectionNumbers()) { if(c1.equals(c) && i1.equals(i)) it.remove();
Bytes expectedIv = oldIvs.get(unseen);
if(expectedIv == null) {
expectedIv = new Bytes(encryptIv(contactId, unseen));
ivToContact.put(expectedIv, contactId);
ivToConnectionNumber.put(expectedIv, connection);
} }
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 ctx;
return contactId;
} }
public void eventOccurred(DatabaseEvent e) { public void eventOccurred(DatabaseEvent e) {
// When the set of contacts changes we need to re-initialise everything if(e instanceof ContactRemovedEvent) {
if(e instanceof ContactAddedEvent || 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) { synchronized(this) {
initialised = false; 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) {
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
} }
} }
} }

View File

@@ -9,8 +9,8 @@ import javax.crypto.IllegalBlockSizeException;
import javax.crypto.Mac; import javax.crypto.Mac;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.crypto.CryptoComponent; 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.ConnectionWriter;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -26,13 +26,14 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
} }
public ConnectionWriter createConnectionWriter(OutputStream out, public ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, TransportId t, long connection, byte[] secret) { long capacity, TransportIndex i, long connection, byte[] secret) {
return createConnectionWriter(out, capacity, true, t, connection, return createConnectionWriter(out, capacity, true, i, connection,
secret); secret);
} }
public ConnectionWriter createConnectionWriter(OutputStream out, public ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, TransportId t, byte[] encryptedIv, byte[] secret) { long capacity, TransportIndex i, byte[] encryptedIv,
byte[] secret) {
// Decrypt the IV // Decrypt the IV
Cipher ivCipher = crypto.getIvCipher(); Cipher ivCipher = crypto.getIvCipher();
SecretKey ivKey = crypto.deriveIncomingIvKey(secret); SecretKey ivKey = crypto.deriveIncomingIvKey(secret);
@@ -48,23 +49,23 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
throw new RuntimeException(badKey); throw new RuntimeException(badKey);
} }
// Validate the IV // Validate the IV
if(!IvEncoder.validateIv(iv, true, t)) if(!IvEncoder.validateIv(iv, true, i))
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// Copy the connection number // Copy the connection number
long connection = IvEncoder.getConnectionNumber(iv); long connection = IvEncoder.getConnectionNumber(iv);
return createConnectionWriter(out, capacity, false, t, connection, return createConnectionWriter(out, capacity, false, i, connection,
secret); secret);
} }
private ConnectionWriter createConnectionWriter(OutputStream out, private ConnectionWriter createConnectionWriter(OutputStream out,
long capacity, boolean initiator, TransportId t, long connection, long capacity, boolean initiator, TransportIndex i, long connection,
byte[] secret) { byte[] secret) {
// Create the encrypter // Create the encrypter
Cipher ivCipher = crypto.getIvCipher(); Cipher ivCipher = crypto.getIvCipher();
Cipher frameCipher = crypto.getFrameCipher(); Cipher frameCipher = crypto.getFrameCipher();
SecretKey ivKey = crypto.deriveOutgoingIvKey(secret); SecretKey ivKey = crypto.deriveOutgoingIvKey(secret);
SecretKey frameKey = crypto.deriveOutgoingFrameKey(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, ConnectionEncrypter encrypter = new ConnectionEncrypterImpl(out,
capacity, iv, ivCipher, frameCipher, ivKey, frameKey); capacity, iv, ivCipher, frameCipher, ivKey, frameKey);
// Create the writer // Create the writer

View File

@@ -1,18 +1,18 @@
package net.sf.briar.transport; package net.sf.briar.transport;
import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; 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; import net.sf.briar.util.ByteUtils;
class IvEncoder { class IvEncoder {
static byte[] encodeIv(boolean initiator, TransportId transport, static byte[] encodeIv(boolean initiator, TransportIndex i,
long connection) { long connection) {
byte[] iv = new byte[IV_LENGTH]; byte[] iv = new byte[IV_LENGTH];
// Bit 31 is the initiator flag // Bit 31 is the initiator flag
if(initiator) iv[3] = 1; if(initiator) iv[3] = 1;
// Encode the transport identifier as an unsigned 16-bit integer // 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 // Encode the connection number as an unsigned 32-bit integer
ByteUtils.writeUint32(connection, iv, 6); ByteUtils.writeUint32(connection, iv, 6);
return iv; return iv;
@@ -24,16 +24,16 @@ class IvEncoder {
ByteUtils.writeUint32(frame, iv, 10); 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; if(iv.length != IV_LENGTH) return false;
// Check that the reserved bits are all zero // 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; 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 // Check that the initiator flag matches
if(initiator != getInitiatorFlag(iv)) return false; if(initiator != getInitiatorFlag(iv)) return false;
// Check that the transport ID matches // Check that the transport ID matches
if(t.getInt() != getTransportId(iv)) return false; if(i.getInt() != getTransportId(iv)) return false;
// The IV is valid // The IV is valid
return true; return true;
} }

View File

@@ -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);
}
}

View File

@@ -1,7 +1,8 @@
package net.sf.briar.transport; 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.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.ConnectionWindowFactory;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -11,10 +12,10 @@ public class TransportModule extends AbstractModule {
@Override @Override
protected void configure() { protected void configure() {
bind(ConnectionDispatcher.class).to(ConnectionDispatcherImpl.class);
bind(ConnectionReaderFactory.class).to( bind(ConnectionReaderFactory.class).to(
ConnectionReaderFactoryImpl.class); ConnectionReaderFactoryImpl.class);
bind(ConnectionRecogniserFactory.class).to( bind(ConnectionRecogniser.class).to(ConnectionRecogniserImpl.class);
ConnectionRecogniserFactoryImpl.class);
bind(ConnectionWindowFactory.class).to( bind(ConnectionWindowFactory.class).to(
ConnectionWindowFactoryImpl.class); ConnectionWindowFactoryImpl.class);
bind(ConnectionWriterFactory.class).to( bind(ConnectionWriterFactory.class).to(

View File

@@ -1,9 +1,9 @@
package net.sf.briar.transport.batch; package net.sf.briar.transport.batch;
import net.sf.briar.api.ContactId; 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.DatabaseComponent;
import net.sf.briar.api.protocol.ProtocolReaderFactory; 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.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.BatchConnectionFactory; import net.sf.briar.api.transport.BatchConnectionFactory;
import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportReader;
@@ -33,10 +33,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory {
this.protoWriterFactory = protoWriterFactory; this.protoWriterFactory = protoWriterFactory;
} }
public void createIncomingConnection(TransportId t, ContactId c, public void createIncomingConnection(TransportIndex i, ContactId c,
BatchTransportReader r, byte[] encryptedIv) { BatchTransportReader r, byte[] encryptedIv) {
final IncomingBatchConnection conn = new IncomingBatchConnection( final IncomingBatchConnection conn = new IncomingBatchConnection(
connReaderFactory, db, protoReaderFactory, t, c, r, connReaderFactory, db, protoReaderFactory, i, c, r,
encryptedIv); encryptedIv);
Runnable read = new Runnable() { Runnable read = new Runnable() {
public void run() { public void run() {
@@ -46,10 +46,10 @@ class BatchConnectionFactoryImpl implements BatchConnectionFactory {
new Thread(read).start(); new Thread(read).start();
} }
public void createOutgoingConnection(TransportId t, ContactId c, public void createOutgoingConnection(TransportIndex i, ContactId c,
BatchTransportWriter w) { BatchTransportWriter w) {
final OutgoingBatchConnection conn = new OutgoingBatchConnection( final OutgoingBatchConnection conn = new OutgoingBatchConnection(
connWriterFactory, db, protoWriterFactory, t, c, w); connWriterFactory, db, protoWriterFactory, i, c, w);
Runnable write = new Runnable() { Runnable write = new Runnable() {
public void run() { public void run() {
conn.write(); conn.write();

View File

@@ -6,7 +6,6 @@ import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.FormatException; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.protocol.Ack; 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.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.transport.BatchTransportReader; import net.sf.briar.api.transport.BatchTransportReader;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
@@ -27,19 +27,19 @@ class IncomingBatchConnection {
private final ConnectionReaderFactory connFactory; private final ConnectionReaderFactory connFactory;
private final DatabaseComponent db; private final DatabaseComponent db;
private final ProtocolReaderFactory protoFactory; private final ProtocolReaderFactory protoFactory;
private final TransportId transportId; private final TransportIndex transportIndex;
private final ContactId contactId; private final ContactId contactId;
private final BatchTransportReader reader; private final BatchTransportReader reader;
private final byte[] encryptedIv; private final byte[] encryptedIv;
IncomingBatchConnection(ConnectionReaderFactory connFactory, IncomingBatchConnection(ConnectionReaderFactory connFactory,
DatabaseComponent db, ProtocolReaderFactory protoFactory, DatabaseComponent db, ProtocolReaderFactory protoFactory,
TransportId transportId, ContactId contactId, TransportIndex transportIndex, ContactId contactId,
BatchTransportReader reader, byte[] encryptedIv) { BatchTransportReader reader, byte[] encryptedIv) {
this.connFactory = connFactory; this.connFactory = connFactory;
this.db = db; this.db = db;
this.protoFactory = protoFactory; this.protoFactory = protoFactory;
this.transportId = transportId; this.transportIndex = transportIndex;
this.contactId = contactId; this.contactId = contactId;
this.reader = reader; this.reader = reader;
this.encryptedIv = encryptedIv; this.encryptedIv = encryptedIv;
@@ -49,7 +49,8 @@ class IncomingBatchConnection {
try { try {
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
ConnectionReader conn = connFactory.createConnectionReader( ConnectionReader conn = connFactory.createConnectionReader(
reader.getInputStream(), transportId, encryptedIv, secret); reader.getInputStream(), transportIndex, encryptedIv,
secret);
ProtocolReader proto = protoFactory.createProtocolReader( ProtocolReader proto = protoFactory.createProtocolReader(
conn.getInputStream()); conn.getInputStream());
// Read packets until EOF // Read packets until EOF

View File

@@ -8,9 +8,9 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import net.sf.briar.api.ContactId; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; 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.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; import net.sf.briar.api.protocol.writers.BatchWriter;
import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
@@ -28,18 +28,18 @@ class OutgoingBatchConnection {
private final ConnectionWriterFactory connFactory; private final ConnectionWriterFactory connFactory;
private final DatabaseComponent db; private final DatabaseComponent db;
private final ProtocolWriterFactory protoFactory; private final ProtocolWriterFactory protoFactory;
private final TransportId transportId; private final TransportIndex transportIndex;
private final ContactId contactId; private final ContactId contactId;
private final BatchTransportWriter writer; private final BatchTransportWriter writer;
OutgoingBatchConnection(ConnectionWriterFactory connFactory, OutgoingBatchConnection(ConnectionWriterFactory connFactory,
DatabaseComponent db, ProtocolWriterFactory protoFactory, DatabaseComponent db, ProtocolWriterFactory protoFactory,
TransportId transportId, ContactId contactId, TransportIndex transportIndex, ContactId contactId,
BatchTransportWriter writer) { BatchTransportWriter writer) {
this.connFactory = connFactory; this.connFactory = connFactory;
this.db = db; this.db = db;
this.protoFactory = protoFactory; this.protoFactory = protoFactory;
this.transportId = transportId; this.transportIndex = transportIndex;
this.contactId = contactId; this.contactId = contactId;
this.writer = writer; this.writer = writer;
} }
@@ -47,10 +47,10 @@ class OutgoingBatchConnection {
void write() { void write() {
try { try {
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
long connection = db.getConnectionNumber(contactId, transportId); long connection = db.getConnectionNumber(contactId, transportIndex);
ConnectionWriter conn = connFactory.createConnectionWriter( ConnectionWriter conn = connFactory.createConnectionWriter(
writer.getOutputStream(), writer.getCapacity(), transportId, writer.getOutputStream(), writer.getCapacity(),
connection, secret); transportIndex, connection, secret);
OutputStream out = conn.getOutputStream(); OutputStream out = conn.getOutputStream();
// There should be enough space for a packet // There should be enough space for a packet
long capacity = conn.getRemainingCapacity(); long capacity = conn.getRemainingCapacity();

View File

@@ -3,10 +3,10 @@ package net.sf.briar.transport.stream;
import java.io.IOException; import java.io.IOException;
import net.sf.briar.api.ContactId; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.protocol.ProtocolReaderFactory; 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.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -21,11 +21,12 @@ public class IncomingStreamConnection extends StreamConnection {
IncomingStreamConnection(ConnectionReaderFactory connReaderFactory, IncomingStreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory, TransportId transportId, ProtocolWriterFactory protoWriterFactory,
ContactId contactId, StreamTransportConnection connection, TransportIndex transportIndex, ContactId contactId,
StreamTransportConnection connection,
byte[] encryptedIv) { byte[] encryptedIv) {
super(connReaderFactory, connWriterFactory, db, protoReaderFactory, super(connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, transportId, contactId, connection); protoWriterFactory, transportIndex, contactId, connection);
this.encryptedIv = encryptedIv; this.encryptedIv = encryptedIv;
} }
@@ -34,7 +35,8 @@ public class IncomingStreamConnection extends StreamConnection {
IOException { IOException {
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
return connReaderFactory.createConnectionReader( return connReaderFactory.createConnectionReader(
connection.getInputStream(), transportId, encryptedIv, secret); connection.getInputStream(), transportIndex, encryptedIv,
secret);
} }
@Override @Override
@@ -42,7 +44,7 @@ public class IncomingStreamConnection extends StreamConnection {
IOException { IOException {
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
return connWriterFactory.createConnectionWriter( return connWriterFactory.createConnectionWriter(
connection.getOutputStream(), Long.MAX_VALUE, transportId, connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
encryptedIv, secret); encryptedIv, secret);
} }
} }

View File

@@ -3,10 +3,10 @@ package net.sf.briar.transport.stream;
import java.io.IOException; import java.io.IOException;
import net.sf.briar.api.ContactId; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.protocol.ProtocolReaderFactory; 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.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.ConnectionReader; import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -21,22 +21,25 @@ public class OutgoingStreamConnection extends StreamConnection {
OutgoingStreamConnection(ConnectionReaderFactory connReaderFactory, OutgoingStreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory, TransportId transportId, ProtocolWriterFactory protoWriterFactory,
ContactId contactId, StreamTransportConnection connection) { TransportIndex transportIndex, ContactId contactId,
StreamTransportConnection connection) {
super(connReaderFactory, connWriterFactory, db, protoReaderFactory, super(connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, transportId, contactId, connection); protoWriterFactory, transportIndex, contactId, connection);
} }
@Override @Override
protected ConnectionReader createConnectionReader() throws DbException, protected ConnectionReader createConnectionReader() throws DbException,
IOException { IOException {
synchronized(this) { synchronized(this) {
if(connectionNum == -1L) if(connectionNum == -1L) {
connectionNum = db.getConnectionNumber(contactId, transportId); connectionNum = db.getConnectionNumber(contactId,
transportIndex);
}
} }
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
return connReaderFactory.createConnectionReader( return connReaderFactory.createConnectionReader(
connection.getInputStream(), transportId, connectionNum, connection.getInputStream(), transportIndex, connectionNum,
secret); secret);
} }
@@ -44,12 +47,14 @@ public class OutgoingStreamConnection extends StreamConnection {
protected ConnectionWriter createConnectionWriter() throws DbException, protected ConnectionWriter createConnectionWriter() throws DbException,
IOException { IOException {
synchronized(this) { synchronized(this) {
if(connectionNum == -1L) if(connectionNum == -1L) {
connectionNum = db.getConnectionNumber(contactId, transportId); connectionNum = db.getConnectionNumber(contactId,
transportIndex);
}
} }
byte[] secret = db.getSharedSecret(contactId); byte[] secret = db.getSharedSecret(contactId);
return connWriterFactory.createConnectionWriter( return connWriterFactory.createConnectionWriter(
connection.getOutputStream(), Long.MAX_VALUE, transportId, connection.getOutputStream(), Long.MAX_VALUE, transportIndex,
connectionNum, secret); connectionNum, secret);
} }
} }

View File

@@ -12,16 +12,15 @@ import java.util.logging.Logger;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.FormatException; 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.DatabaseComponent;
import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.event.BatchReceivedEvent; import net.sf.briar.api.db.event.BatchReceivedEvent;
import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent;
import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseEvent;
import net.sf.briar.api.db.event.DatabaseListener; 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.MessagesAddedEvent;
import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; 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.Ack;
import net.sf.briar.api.protocol.Batch; import net.sf.briar.api.protocol.Batch;
import net.sf.briar.api.protocol.MessageId; 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.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; import net.sf.briar.api.protocol.writers.BatchWriter;
@@ -56,7 +56,7 @@ abstract class StreamConnection implements DatabaseListener {
protected final DatabaseComponent db; protected final DatabaseComponent db;
protected final ProtocolReaderFactory protoReaderFactory; protected final ProtocolReaderFactory protoReaderFactory;
protected final ProtocolWriterFactory protoWriterFactory; protected final ProtocolWriterFactory protoWriterFactory;
protected final TransportId transportId; protected final TransportIndex transportIndex;
protected final ContactId contactId; protected final ContactId contactId;
protected final StreamTransportConnection connection; protected final StreamTransportConnection connection;
@@ -69,14 +69,15 @@ abstract class StreamConnection implements DatabaseListener {
StreamConnection(ConnectionReaderFactory connReaderFactory, StreamConnection(ConnectionReaderFactory connReaderFactory,
ConnectionWriterFactory connWriterFactory, DatabaseComponent db, ConnectionWriterFactory connWriterFactory, DatabaseComponent db,
ProtocolReaderFactory protoReaderFactory, ProtocolReaderFactory protoReaderFactory,
ProtocolWriterFactory protoWriterFactory, TransportId transportId, ProtocolWriterFactory protoWriterFactory,
ContactId contactId, StreamTransportConnection connection) { TransportIndex transportIndex, ContactId contactId,
StreamTransportConnection connection) {
this.connReaderFactory = connReaderFactory; this.connReaderFactory = connReaderFactory;
this.connWriterFactory = connWriterFactory; this.connWriterFactory = connWriterFactory;
this.db = db; this.db = db;
this.protoReaderFactory = protoReaderFactory; this.protoReaderFactory = protoReaderFactory;
this.protoWriterFactory = protoWriterFactory; this.protoWriterFactory = protoWriterFactory;
this.transportId = transportId; this.transportIndex = transportIndex;
this.contactId = contactId; this.contactId = contactId;
this.connection = connection; this.connection = connection;
} }
@@ -108,7 +109,7 @@ abstract class StreamConnection implements DatabaseListener {
writerFlags |= Flags.SUBSCRIPTIONS_UPDATED; writerFlags |= Flags.SUBSCRIPTIONS_UPDATED;
notifyAll(); notifyAll();
} }
} else if(e instanceof TransportsUpdatedEvent) { } else if(e instanceof LocalTransportsUpdatedEvent) {
writerFlags |= Flags.TRANSPORTS_UPDATED; writerFlags |= Flags.TRANSPORTS_UPDATED;
notifyAll(); notifyAll();
} }

View File

@@ -1,9 +1,9 @@
package net.sf.briar.transport.stream; package net.sf.briar.transport.stream;
import net.sf.briar.api.ContactId; 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.DatabaseComponent;
import net.sf.briar.api.protocol.ProtocolReaderFactory; 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.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.transport.ConnectionReaderFactory; import net.sf.briar.api.transport.ConnectionReaderFactory;
import net.sf.briar.api.transport.ConnectionWriterFactory; import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -32,11 +32,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory {
this.protoWriterFactory = protoWriterFactory; this.protoWriterFactory = protoWriterFactory;
} }
public void createIncomingConnection(TransportId t, ContactId c, public void createIncomingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s, byte[] encryptedIv) { StreamTransportConnection s, byte[] encryptedIv) {
final StreamConnection conn = new IncomingStreamConnection( final StreamConnection conn = new IncomingStreamConnection(
connReaderFactory, connWriterFactory, db, protoReaderFactory, connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, t, c, s, encryptedIv); protoWriterFactory, i, c, s, encryptedIv);
Runnable write = new Runnable() { Runnable write = new Runnable() {
public void run() { public void run() {
conn.write(); conn.write();
@@ -51,11 +51,11 @@ public class StreamConnectionFactoryImpl implements StreamConnectionFactory {
new Thread(read).start(); new Thread(read).start();
} }
public void createOutgoingConnection(TransportId t, ContactId c, public void createOutgoingConnection(TransportIndex i, ContactId c,
StreamTransportConnection s) { StreamTransportConnection s) {
final StreamConnection conn = new OutgoingStreamConnection( final StreamConnection conn = new OutgoingStreamConnection(
connReaderFactory, connWriterFactory, db, protoReaderFactory, connReaderFactory, connWriterFactory, db, protoReaderFactory,
protoWriterFactory, t, c, s); protoWriterFactory, i, c, s);
Runnable write = new Runnable() { Runnable write = new Runnable() {
public void run() { public void run() {
conn.write(); conn.write();

View File

@@ -15,8 +15,6 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import junit.framework.TestCase; 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.crypto.CryptoComponent;
import net.sf.briar.api.protocol.Ack; import net.sf.briar.api.protocol.Ack;
import net.sf.briar.api.protocol.Author; 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.Group;
import net.sf.briar.api.protocol.GroupFactory; import net.sf.briar.api.protocol.GroupFactory;
import net.sf.briar.api.protocol.Message; 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.MessageId;
import net.sf.briar.api.protocol.Offer; import net.sf.briar.api.protocol.Offer;
import net.sf.briar.api.protocol.ProtocolReader; import net.sf.briar.api.protocol.ProtocolReader;
import net.sf.briar.api.protocol.ProtocolReaderFactory; import net.sf.briar.api.protocol.ProtocolReaderFactory;
import net.sf.briar.api.protocol.Request; import net.sf.briar.api.protocol.Request;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.protocol.UniqueId; import net.sf.briar.api.protocol.UniqueId;
import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; 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.OfferWriter;
import net.sf.briar.api.protocol.writers.ProtocolWriterFactory; import net.sf.briar.api.protocol.writers.ProtocolWriterFactory;
import net.sf.briar.api.protocol.writers.RequestWriter; 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.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
import net.sf.briar.transport.TransportModule; import net.sf.briar.transport.TransportModule;
import net.sf.briar.transport.batch.TransportBatchModule;
import net.sf.briar.transport.stream.TransportStreamModule;
import org.junit.Test; import org.junit.Test;
@@ -69,7 +72,7 @@ public class ProtocolIntegrationTest extends TestCase {
private final ProtocolWriterFactory protocolWriterFactory; private final ProtocolWriterFactory protocolWriterFactory;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final byte[] aliceSecret, bobSecret; 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 long connection = 12345L;
private final Author author; private final Author author;
private final Group group, group1; private final Group group, group1;
@@ -77,14 +80,15 @@ public class ProtocolIntegrationTest extends TestCase {
private final String authorName = "Alice"; private final String authorName = "Alice";
private final String subject = "Hello"; private final String subject = "Hello";
private final String messageBody = "Hello world"; private final String messageBody = "Hello world";
private final Map<TransportId, TransportProperties> transports; private final Collection<Transport> transports;
public ProtocolIntegrationTest() throws Exception { public ProtocolIntegrationTest() throws Exception {
super(); super();
Injector i = Guice.createInjector(new CryptoModule(), Injector i = Guice.createInjector(new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new DatabaseModule(), new ProtocolModule(),
new ProtocolWritersModule(), new SerialModule(), new ProtocolWritersModule(), new SerialModule(),
new TestDatabaseModule(), new TransportModule()); new TestDatabaseModule(), new TransportBatchModule(),
new TransportModule(), new TransportStreamModule());
connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class); connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class);
connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class);
protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class);
@@ -120,9 +124,11 @@ public class ProtocolIntegrationTest extends TestCase {
message3 = messageEncoder.encodeMessage(null, group1, message3 = messageEncoder.encodeMessage(null, group1,
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(), groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
subject, messageBody.getBytes("UTF-8")); subject, messageBody.getBytes("UTF-8"));
TransportProperties p = // Create some transports
new TransportProperties(Collections.singletonMap("bar", "baz")); TransportId transportId = new TransportId(TestUtils.getRandomId());
transports = Collections.singletonMap(transportId, p); Transport transport = new Transport(transportId, transportIndex,
Collections.singletonMap("bar", "baz"));
transports = Collections.singletonList(transport);
} }
@Test @Test
@@ -134,7 +140,7 @@ public class ProtocolIntegrationTest extends TestCase {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
// Use Alice's secret for writing // Use Alice's secret for writing
ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out,
Long.MAX_VALUE, transportId, connection, aliceSecret); Long.MAX_VALUE, transportIndex, connection, aliceSecret);
OutputStream out1 = w.getOutputStream(); OutputStream out1 = w.getOutputStream();
AckWriter a = protocolWriterFactory.createAckWriter(out1); AckWriter a = protocolWriterFactory.createAckWriter(out1);
@@ -188,7 +194,7 @@ public class ProtocolIntegrationTest extends TestCase {
assertEquals(16, offset); assertEquals(16, offset);
// Use Bob's secret for reading // Use Bob's secret for reading
ConnectionReader r = connectionReaderFactory.createConnectionReader(in, ConnectionReader r = connectionReaderFactory.createConnectionReader(in,
transportId, encryptedIv, bobSecret); transportIndex, encryptedIv, bobSecret);
in = r.getInputStream(); in = r.getInputStream();
ProtocolReader protocolReader = ProtocolReader protocolReader =
protocolReaderFactory.createProtocolReader(in); protocolReaderFactory.createProtocolReader(in);

View File

@@ -48,7 +48,7 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP); oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
will(returnValue(Collections.emptySet())); will(returnValue(Collections.emptyList()));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// As if by magic, some free space has appeared // As if by magic, some free space has appeared
oneOf(database).getFreeSpace(); oneOf(database).getFreeSpace();

View File

@@ -10,7 +10,6 @@ import junit.framework.TestCase;
import net.sf.briar.TestUtils; import net.sf.briar.TestUtils;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.MessageHeader; 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.DatabaseListener;
import net.sf.briar.api.db.event.MessagesAddedEvent; import net.sf.briar.api.db.event.MessagesAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent; 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.Ack;
import net.sf.briar.api.protocol.AuthorId; import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Batch; 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.Offer;
import net.sf.briar.api.protocol.ProtocolConstants; import net.sf.briar.api.protocol.ProtocolConstants;
import net.sf.briar.api.protocol.SubscriptionUpdate; 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.TransportUpdate;
import net.sf.briar.api.protocol.writers.AckWriter; import net.sf.briar.api.protocol.writers.AckWriter;
import net.sf.briar.api.protocol.writers.BatchWriter; 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 Message message, privateMessage;
private final Group group; private final Group group;
private final TransportId transportId; 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 Map<ContactId, TransportProperties> remoteProperties;
private final byte[] secret; private final byte[] secret;
@@ -82,11 +85,15 @@ public abstract class DatabaseComponentTest extends TestCase {
privateMessage = new TestMessage(messageId, null, null, null, subject, privateMessage = new TestMessage(messageId, null, null, null, subject,
timestamp, raw); timestamp, raw);
group = new TestGroup(groupId, "The really exciting group", null); group = new TestGroup(groupId, "The really exciting group", null);
transportId = new TransportId(123); transportId = new TransportId(TestUtils.getRandomId());
TransportProperties p = localIndex = new TransportIndex(0);
new TransportProperties(Collections.singletonMap("foo", "bar")); remoteIndex = new TransportIndex(13);
transports = Collections.singletonMap(transportId, p); TransportProperties properties = new TransportProperties(
remoteProperties = Collections.singletonMap(contactId, p); Collections.singletonMap("foo", "bar"));
remoteProperties = Collections.singletonMap(contactId, properties);
Transport transport = new Transport(transportId, localIndex,
properties);
transports = Collections.singletonList(transport);
secret = new byte[123]; secret = new byte[123];
} }
@@ -124,17 +131,17 @@ public abstract class DatabaseComponentTest extends TestCase {
// setRating(authorId, Rating.GOOD) again // setRating(authorId, Rating.GOOD) again
oneOf(database).setRating(txn, authorId, Rating.GOOD); oneOf(database).setRating(txn, authorId, Rating.GOOD);
will(returnValue(Rating.GOOD)); will(returnValue(Rating.GOOD));
// addContact(transports) // addContact()
oneOf(database).addContact(txn, transports, secret); oneOf(database).addContact(txn, secret);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class))); oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class)));
// getContacts() // getContacts()
oneOf(database).getContacts(txn); oneOf(database).getContacts(txn);
will(returnValue(Collections.singletonList(contactId))); will(returnValue(Collections.singletonList(contactId)));
// getConnectionWindow(contactId, 123) // getConnectionWindow(contactId, 13)
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).getConnectionWindow(txn, contactId, transportId); oneOf(database).getConnectionWindow(txn, contactId, remoteIndex);
will(returnValue(connectionWindow)); will(returnValue(connectionWindow));
// getSharedSecret(contactId) // getSharedSecret(contactId)
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
@@ -170,10 +177,10 @@ public abstract class DatabaseComponentTest extends TestCase {
// unsubscribe(groupId) again // unsubscribe(groupId) again
oneOf(database).containsSubscription(txn, groupId); oneOf(database).containsSubscription(txn, groupId);
will(returnValue(false)); will(returnValue(false));
// setConnectionWindow(contactId, 123, connectionWindow) // setConnectionWindow(contactId, 13, connectionWindow)
oneOf(database).containsContact(txn, contactId); oneOf(database).containsContact(txn, contactId);
will(returnValue(true)); will(returnValue(true));
oneOf(database).setConnectionWindow(txn, contactId, transportId, oneOf(database).setConnectionWindow(txn, contactId, remoteIndex,
connectionWindow); connectionWindow);
// removeContact(contactId) // removeContact(contactId)
oneOf(database).removeContact(txn, contactId); oneOf(database).removeContact(txn, contactId);
@@ -189,10 +196,10 @@ public abstract class DatabaseComponentTest extends TestCase {
assertEquals(Rating.UNRATED, db.getRating(authorId)); assertEquals(Rating.UNRATED, db.getRating(authorId));
db.setRating(authorId, Rating.GOOD); // First time - listeners called db.setRating(authorId, Rating.GOOD); // First time - listeners called
db.setRating(authorId, Rating.GOOD); // Second time - not 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(Collections.singletonList(contactId), db.getContacts());
assertEquals(connectionWindow, assertEquals(connectionWindow,
db.getConnectionWindow(contactId, transportId)); db.getConnectionWindow(contactId, remoteIndex));
assertEquals(secret, db.getSharedSecret(contactId)); assertEquals(secret, db.getSharedSecret(contactId));
assertEquals(remoteProperties, db.getRemoteProperties(transportId)); assertEquals(remoteProperties, db.getRemoteProperties(transportId));
db.subscribe(group); // First time - listeners called db.subscribe(group); // First time - listeners called
@@ -201,7 +208,7 @@ public abstract class DatabaseComponentTest extends TestCase {
assertEquals(Collections.singletonList(groupId), db.getSubscriptions()); assertEquals(Collections.singletonList(groupId), db.getSubscriptions());
db.unsubscribe(groupId); // First time - listeners called db.unsubscribe(groupId); // First time - listeners called
db.unsubscribe(groupId); // Second time - not called db.unsubscribe(groupId); // Second time - not called
db.setConnectionWindow(contactId, transportId, connectionWindow); db.setConnectionWindow(contactId, remoteIndex, connectionWindow);
db.removeContact(contactId); db.removeContact(contactId);
db.removeListener(listener); db.removeListener(listener);
db.close(); db.close();
@@ -540,7 +547,7 @@ public abstract class DatabaseComponentTest extends TestCase {
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
try { try {
db.getConnectionWindow(contactId, transportId); db.getConnectionWindow(contactId, remoteIndex);
fail(); fail();
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
@@ -580,7 +587,7 @@ public abstract class DatabaseComponentTest extends TestCase {
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
try { try {
db.setConnectionWindow(contactId, transportId, null); db.setConnectionWindow(contactId, remoteIndex, null);
fail(); fail();
} catch(NoSuchContactException expected) {} } catch(NoSuchContactException expected) {}
@@ -1379,7 +1386,7 @@ public abstract class DatabaseComponentTest extends TestCase {
with(any(long.class))); with(any(long.class)));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
oneOf(listener).eventOccurred(with(any( oneOf(listener).eventOccurred(with(any(
TransportsUpdatedEvent.class))); TransportAddedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner); DatabaseComponent db = createDatabaseComponent(database, cleaner);

View File

@@ -10,7 +10,6 @@ import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit; 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.ContactId;
import net.sf.briar.api.Rating; import net.sf.briar.api.Rating;
import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportConfig;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties; import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.crypto.Password; import net.sf.briar.api.crypto.Password;
import net.sf.briar.api.db.DbException; 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.GroupId;
import net.sf.briar.api.protocol.Message; import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId; 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.ConnectionWindow;
import net.sf.briar.api.transport.ConnectionWindowFactory; import net.sf.briar.api.transport.ConnectionWindowFactory;
import net.sf.briar.crypto.CryptoModule; import net.sf.briar.crypto.CryptoModule;
import net.sf.briar.protocol.ProtocolModule; import net.sf.briar.protocol.ProtocolModule;
import net.sf.briar.protocol.writers.ProtocolWritersModule;
import net.sf.briar.serial.SerialModule; import net.sf.briar.serial.SerialModule;
import net.sf.briar.transport.TransportModule; 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.apache.commons.io.FileSystemUtils;
import org.junit.After; import org.junit.After;
@@ -75,17 +79,20 @@ public class H2DatabaseTest extends TestCase {
private final Message message, privateMessage; private final Message message, privateMessage;
private final Group group; private final Group group;
private final TransportId transportId; private final TransportId transportId;
private final TransportIndex localIndex, remoteIndex;
private final TransportProperties properties; private final TransportProperties properties;
private final Map<TransportId, TransportProperties> transports;
private final Map<ContactId, TransportProperties> remoteProperties; private final Map<ContactId, TransportProperties> remoteProperties;
private final Collection<Transport> remoteTransports;
private final Map<Group, Long> subscriptions; private final Map<Group, Long> subscriptions;
private final byte[] secret; private final byte[] secret;
public H2DatabaseTest() throws Exception { public H2DatabaseTest() throws Exception {
super(); super();
Injector i = Guice.createInjector(new CryptoModule(), Injector i = Guice.createInjector(new CryptoModule(),
new DatabaseModule(), new ProtocolModule(), new SerialModule(), new DatabaseModule(), new ProtocolModule(),
new TransportModule(), new TestDatabaseModule(testDir)); new ProtocolWritersModule(), new SerialModule(),
new TransportBatchModule(), new TransportModule(),
new TransportStreamModule(), new TestDatabaseModule(testDir));
connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class); connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class);
groupFactory = i.getInstance(GroupFactory.class); groupFactory = i.getInstance(GroupFactory.class);
authorId = new AuthorId(TestUtils.getRandomId()); authorId = new AuthorId(TestUtils.getRandomId());
@@ -104,11 +111,15 @@ public class H2DatabaseTest extends TestCase {
privateMessage = new TestMessage(privateMessageId, null, null, null, privateMessage = new TestMessage(privateMessageId, null, null, null,
subject, timestamp, raw); subject, timestamp, raw);
group = groupFactory.createGroup(groupId, "Group name", null); 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( properties = new TransportProperties(
Collections.singletonMap("foo", "bar")); Collections.singletonMap("foo", "bar"));
transports = Collections.singletonMap(transportId, properties);
remoteProperties = Collections.singletonMap(contactId, properties); remoteProperties = Collections.singletonMap(contactId, properties);
Transport remoteTransport = new Transport(transportId, remoteIndex,
properties);
remoteTransports = Collections.singletonList(remoteTransport);
subscriptions = Collections.singletonMap(group, 0L); subscriptions = Collections.singletonMap(group, 0L);
secret = new byte[123]; secret = new byte[123];
} }
@@ -124,7 +135,7 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsSubscription(txn, groupId)); assertFalse(db.containsSubscription(txn, groupId));
db.addSubscription(txn, group); db.addSubscription(txn, group);
@@ -142,8 +153,6 @@ public class H2DatabaseTest extends TestCase {
db = open(true); db = open(true);
txn = db.startTransaction(); txn = db.startTransaction();
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertEquals(remoteProperties,
db.getRemoteProperties(txn, transportId));
assertTrue(db.containsSubscription(txn, groupId)); assertTrue(db.containsSubscription(txn, groupId));
assertTrue(db.containsMessage(txn, messageId)); assertTrue(db.containsMessage(txn, messageId));
byte[] raw1 = db.getMessage(txn, messageId); byte[] raw1 = db.getMessage(txn, messageId);
@@ -182,20 +191,20 @@ public class H2DatabaseTest extends TestCase {
// Create three contacts // Create three contacts
assertFalse(db.containsContact(txn, contactId)); assertFalse(db.containsContact(txn, contactId));
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
assertTrue(db.containsContact(txn, contactId)); assertTrue(db.containsContact(txn, contactId));
assertFalse(db.containsContact(txn, contactId1)); assertFalse(db.containsContact(txn, contactId1));
assertEquals(contactId1, db.addContact(txn, transports, secret)); assertEquals(contactId1, db.addContact(txn, secret));
assertTrue(db.containsContact(txn, contactId1)); assertTrue(db.containsContact(txn, contactId1));
assertFalse(db.containsContact(txn, contactId2)); assertFalse(db.containsContact(txn, contactId2));
assertEquals(contactId2, db.addContact(txn, transports, secret)); assertEquals(contactId2, db.addContact(txn, secret));
assertTrue(db.containsContact(txn, contactId2)); assertTrue(db.containsContact(txn, contactId2));
// Delete the contact with the highest ID // Delete the contact with the highest ID
db.removeContact(txn, contactId2); db.removeContact(txn, contactId2);
assertFalse(db.containsContact(txn, contactId2)); assertFalse(db.containsContact(txn, contactId2));
// Add another contact - a new ID should be created // Add another contact - a new ID should be created
assertFalse(db.containsContact(txn, contactId3)); assertFalse(db.containsContact(txn, contactId3));
assertEquals(contactId3, db.addContact(txn, transports, secret)); assertEquals(contactId3, db.addContact(txn, secret));
assertTrue(db.containsContact(txn, contactId3)); assertTrue(db.containsContact(txn, contactId3));
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -242,7 +251,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and store a private message // 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.addPrivateMessage(txn, privateMessage, contactId);
// Removing the contact should remove the message // Removing the contact should remove the message
@@ -261,7 +270,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and store a private message // 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.addPrivateMessage(txn, privateMessage, contactId);
// The message has no status yet, so it should not be sendable // 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(); Connection txn = db.startTransaction();
// Add a contact and store a private message // 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.addPrivateMessage(txn, privateMessage, contactId);
db.setStatus(txn, contactId, privateMessageId, Status.NEW); db.setStatus(txn, contactId, privateMessageId, Status.NEW);
@@ -328,7 +337,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -366,7 +375,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -408,7 +417,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -447,7 +456,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -482,7 +491,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -513,7 +522,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -546,7 +555,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and some batches to ack // 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, batchId);
db.addBatchToAck(txn, contactId, batchId1); db.addBatchToAck(txn, contactId, batchId1);
@@ -573,7 +582,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and receive the same batch twice // 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);
db.addBatchToAck(txn, contactId, batchId); db.addBatchToAck(txn, contactId, batchId);
@@ -599,7 +608,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -624,8 +633,8 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add two contacts, subscribe to a group and store a message // Add two contacts, subscribe to a group and store a message
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
ContactId contactId1 = db.addContact(txn, transports, secret); ContactId contactId1 = db.addContact(txn, secret);
db.addSubscription(txn, group); db.addSubscription(txn, group);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -647,7 +656,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -686,7 +695,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -731,12 +740,12 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Add some outstanding batches, a few ms apart // Add some outstanding batches, a few ms apart
for(int i = 0; i < ids.length; i++) { for(int i = 0; i < ids.length; i++) {
db.addOutstandingBatch(txn, contactId, ids[i], db.addOutstandingBatch(txn, contactId, ids[i],
Collections.<MessageId>emptySet()); Collections.<MessageId>emptyList());
Thread.sleep(5); Thread.sleep(5);
} }
@@ -771,12 +780,12 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Add some outstanding batches, a few ms apart // Add some outstanding batches, a few ms apart
for(int i = 0; i < ids.length; i++) { for(int i = 0; i < ids.length; i++) {
db.addOutstandingBatch(txn, contactId, ids[i], db.addOutstandingBatch(txn, contactId, ids[i],
Collections.<MessageId>emptySet()); Collections.<MessageId>emptyList());
Thread.sleep(5); Thread.sleep(5);
} }
@@ -991,43 +1000,55 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact with some transport properties // 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, assertEquals(remoteProperties,
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(txn, transportId));
// Replace the transport properties // Replace the transport properties
TransportProperties properties1 = TransportProperties properties1 =
new TransportProperties(Collections.singletonMap("baz", "bam")); new TransportProperties(Collections.singletonMap("baz", "bam"));
Map<TransportId, TransportProperties> transports1 = Transport remoteTransport1 =
Collections.singletonMap(transportId, properties1); new Transport(transportId, remoteIndex, properties1);
Collection<Transport> remoteTransports1 =
Collections.singletonList(remoteTransport1);
Map<ContactId, TransportProperties> remoteProperties1 = Map<ContactId, TransportProperties> remoteProperties1 =
Collections.singletonMap(contactId, properties1); Collections.singletonMap(contactId, properties1);
db.setTransports(txn, contactId, transports1, 1); db.setTransports(txn, contactId, remoteTransports1, 2);
assertEquals(remoteProperties1, assertEquals(remoteProperties1,
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(txn, transportId));
// Remove the transport properties // Remove the transport properties
db.setTransports(txn, contactId, db.setTransports(txn, contactId, Collections.<Transport>emptyList(), 3);
Collections.<TransportId, TransportProperties>emptyMap(), 2);
assertEquals(Collections.emptyMap(), assertEquals(Collections.emptyMap(),
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(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));
// Remove the local transport properties
for(TransportId t : transports.keySet()) {
db.setLocalProperties(txn, t, new TransportProperties());
}
assertEquals(Collections.emptyMap(), db.getLocalTransports(txn));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); 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
db.setLocalProperties(txn, transportId, properties);
assertEquals(Collections.singletonList(properties),
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 @Test
public void testUpdateTransportConfig() throws Exception { public void testUpdateTransportConfig() throws Exception {
@@ -1039,6 +1060,9 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Allocate a transport index
assertEquals(localIndex, db.addTransport(txn, transportId));
// Set the transport config // Set the transport config
db.setConfig(txn, transportId, config); db.setConfig(txn, transportId, config);
assertEquals(config, db.getConfig(txn, transportId)); assertEquals(config, db.getConfig(txn, transportId));
@@ -1049,8 +1073,7 @@ public class H2DatabaseTest extends TestCase {
// Remove the transport config // Remove the transport config
db.setConfig(txn, transportId, new TransportConfig()); db.setConfig(txn, transportId, new TransportConfig());
assertEquals(Collections.emptyMap(), assertEquals(Collections.emptyMap(), db.getConfig(txn, transportId));
db.getConfig(txn, transportId));
db.commitTransaction(txn); db.commitTransaction(txn);
db.close(); db.close();
@@ -1062,27 +1085,32 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact with some transport properties // 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, assertEquals(remoteProperties,
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(txn, transportId));
// Replace the transport properties using a timestamp of 2 // Replace the transport properties using a timestamp of 2
TransportProperties properties1 = TransportProperties properties1 =
new TransportProperties(Collections.singletonMap("baz", "bam")); new TransportProperties(Collections.singletonMap("baz", "bam"));
Map<TransportId, TransportProperties> transports1 = Transport remoteTransport1 =
Collections.singletonMap(transportId, properties1); new Transport(transportId, remoteIndex, properties1);
Collection<Transport> remoteTransports1 =
Collections.singletonList(remoteTransport1);
Map<ContactId, TransportProperties> remoteProperties1 = Map<ContactId, TransportProperties> remoteProperties1 =
Collections.singletonMap(contactId, properties1); Collections.singletonMap(contactId, properties1);
db.setTransports(txn, contactId, transports1, 2); db.setTransports(txn, contactId, remoteTransports1, 2);
assertEquals(remoteProperties1, assertEquals(remoteProperties1,
db.getRemoteProperties(txn, transportId)); db.getRemoteProperties(txn, transportId));
// Try to replace the transport properties using a timestamp of 1 // Try to replace the transport properties using a timestamp of 1
TransportProperties properties2 = TransportProperties properties2 =
new TransportProperties(Collections.singletonMap("quux", "etc")); new TransportProperties(Collections.singletonMap("quux", "etc"));
Map<TransportId, TransportProperties> transports2 = Transport remoteTransport2 =
Collections.singletonMap(transportId, properties2); new Transport(transportId, remoteIndex, properties2);
db.setTransports(txn, contactId, transports2, 1); Collection<Transport> remoteTransports2 =
Collections.singletonList(remoteTransport2);
db.setTransports(txn, contactId, remoteTransports2, 1);
// The old properties should still be there // The old properties should still be there
assertEquals(remoteProperties1, assertEquals(remoteProperties1,
@@ -1100,10 +1128,8 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact with some subscriptions
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Add some subscriptions
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
assertEquals(Collections.singletonList(group), assertEquals(Collections.singletonList(group),
db.getSubscriptions(txn, contactId)); db.getSubscriptions(txn, contactId));
@@ -1127,10 +1153,8 @@ public class H2DatabaseTest extends TestCase {
Database<Connection> db = open(false); Database<Connection> db = open(false);
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact with some subscriptions
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Add some subscriptions
db.setSubscriptions(txn, contactId, subscriptions, 2); db.setSubscriptions(txn, contactId, subscriptions, 2);
assertEquals(Collections.singletonList(group), assertEquals(Collections.singletonList(group),
db.getSubscriptions(txn, contactId)); db.getSubscriptions(txn, contactId));
@@ -1154,7 +1178,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and subscribe to a group // 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.addSubscription(txn, group);
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1172,7 +1196,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -1195,7 +1219,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
@@ -1218,7 +1242,7 @@ public class H2DatabaseTest extends TestCase {
// Add a contact, subscribe to a group and store a message - // Add a contact, subscribe to a group and store a message -
// the message is older than the contact's subscription // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
Map<Group, Long> subs = Collections.singletonMap(group, timestamp + 1); Map<Group, Long> subs = Collections.singletonMap(group, timestamp + 1);
@@ -1242,7 +1266,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1267,7 +1291,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and subscribe to a group // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1286,7 +1310,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact with a subscription // 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); db.setSubscriptions(txn, contactId, subscriptions, 1);
// There's no local subscription for the group // There's no local subscription for the group
@@ -1303,7 +1327,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
db.setStatus(txn, contactId, messageId, Status.NEW); db.setStatus(txn, contactId, messageId, Status.NEW);
@@ -1322,7 +1346,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.addGroupMessage(txn, message); db.addGroupMessage(txn, message);
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1342,7 +1366,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1364,7 +1388,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message // 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.addSubscription(txn, group);
db.setVisibility(txn, groupId, Collections.singleton(contactId)); db.setVisibility(txn, groupId, Collections.singleton(contactId));
db.setSubscriptions(txn, contactId, subscriptions, 1); db.setSubscriptions(txn, contactId, subscriptions, 1);
@@ -1385,7 +1409,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and subscribe to a group // 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.addSubscription(txn, group);
// The group should not be visible to the contact // The group should not be visible to the contact
assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId));
@@ -1394,7 +1418,7 @@ public class H2DatabaseTest extends TestCase {
assertEquals(Collections.singletonList(contactId), assertEquals(Collections.singletonList(contactId),
db.getVisibility(txn, groupId)); db.getVisibility(txn, groupId));
// Make the group invisible again // 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)); assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId));
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -1408,10 +1432,10 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Get the connection window for a new transport // Get the connection window for a new index
ConnectionWindow w = db.getConnectionWindow(txn, contactId, ConnectionWindow w = db.getConnectionWindow(txn, contactId,
transportId); remoteIndex);
// The connection window should exist and be in the initial state // The connection window should exist and be in the initial state
assertNotNull(w); assertNotNull(w);
assertEquals(0L, w.getCentre()); assertEquals(0L, w.getCentre());
@@ -1427,19 +1451,19 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // Add a contact
assertEquals(contactId, db.addContact(txn, transports, secret)); assertEquals(contactId, db.addContact(txn, secret));
// Get the connection window for a new transport // Get the connection window for a new index
ConnectionWindow w = db.getConnectionWindow(txn, contactId, ConnectionWindow w = db.getConnectionWindow(txn, contactId,
transportId); remoteIndex);
// The connection window should exist and be in the initial state // The connection window should exist and be in the initial state
assertNotNull(w); assertNotNull(w);
assertEquals(0L, w.getCentre()); assertEquals(0L, w.getCentre());
assertEquals(0, w.getBitmap()); assertEquals(0, w.getBitmap());
// Update the connection window and store it // Update the connection window and store it
w.setSeen(5L); w.setSeen(5L);
db.setConnectionWindow(txn, contactId, transportId, w); db.setConnectionWindow(txn, contactId, remoteIndex, w);
// Check that the connection window was stored // Check that the connection window was stored
w = db.getConnectionWindow(txn, contactId, transportId); w = db.getConnectionWindow(txn, contactId, remoteIndex);
assertNotNull(w); assertNotNull(w);
assertEquals(6L, w.getCentre()); assertEquals(6L, w.getCentre());
assertTrue(w.isSeen(5L)); assertTrue(w.isSeen(5L));
@@ -1527,7 +1551,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and subscribe to a group // 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.addSubscription(txn, group);
// A message with a private parent should return null // A message with a private parent should return null
@@ -1576,7 +1600,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact // 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 // The subscription and transport timestamps should be initialised to 0
assertEquals(0L, db.getSubscriptionsModified(txn, contactId)); assertEquals(0L, db.getSubscriptionsModified(txn, contactId));
@@ -1606,7 +1630,7 @@ public class H2DatabaseTest extends TestCase {
Connection txn = db.startTransaction(); Connection txn = db.startTransaction();
// Add a contact and subscribe to a group // 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.addSubscription(txn, group);
// Store a couple of messages // Store a couple of messages

View File

@@ -1,10 +1,7 @@
package net.sf.briar.db; package net.sf.briar.db;
import java.io.IOException;
import net.sf.briar.api.protocol.Group; import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId; import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.serial.Writer;
public class TestGroup implements Group { public class TestGroup implements Group {
@@ -29,8 +26,4 @@ public class TestGroup implements Group {
public byte[] getPublicKey() { public byte[] getPublicKey() {
return publicKey; return publicKey;
} }
public void writeTo(Writer w) throws IOException {
throw new UnsupportedOperationException();
}
} }

Some files were not shown because too many files have changed in this diff Show More