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;
/** Type-safe wrapper for an integer that uniquely identifies a contact. */
/**
* Type-safe wrapper for an integer that uniquely identifies a contact within
* the scope of a single node.
*/
public class ContactId {
private final int id;
@@ -23,9 +26,4 @@ public class ContactId {
public int hashCode() {
return id;
}
@Override
public String toString() {
return String.valueOf(id);
}
}

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

View File

@@ -3,9 +3,15 @@ package net.sf.briar.api.db.event;
import net.sf.briar.api.ContactId;
/** An event that is broadcast when a contact is removed. */
public class ContactRemovedEvent extends ContactAddedEvent {
public class ContactRemovedEvent extends DatabaseEvent {
private final ContactId contactId;
public ContactRemovedEvent(ContactId contactId) {
super(contactId);
this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
}
}

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 {
BatchPlugin createPlugin(Executor executor,
BatchPluginCallback callback);
BatchPlugin createPlugin(Executor executor, BatchPluginCallback callback);
}

View File

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

View File

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

View File

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

View File

@@ -1,15 +1,7 @@
package net.sf.briar.api.protocol;
import net.sf.briar.api.serial.Writable;
/** A pseudonymous author of messages. */
public interface Author extends Writable {
/** The maximum length of an author's name in UTF-8 bytes. */
static final int MAX_NAME_LENGTH = 50;
/** The maximum length of an author's public key in bytes. */
static final int MAX_PUBLIC_KEY_LENGTH = 100;
public interface Author {
/** Returns the author's unique identifier. */
AuthorId getId();

View File

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

View File

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

View File

@@ -1,15 +1,7 @@
package net.sf.briar.api.protocol;
import net.sf.briar.api.serial.Writable;
/** A group to which users may subscribe. */
public interface Group extends Writable {
/** The maximum length of a group's name in UTF-8 bytes. */
static final int MAX_NAME_LENGTH = 50;
/** The maximum length of a group's public key in bytes. */
static final int MAX_PUBLIC_KEY_LENGTH = 100;
public interface Group {
/** Returns the group's unique identifier. */
GroupId getId();

View File

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

View File

@@ -2,23 +2,6 @@ package net.sf.briar.api.protocol;
public interface Message {
/**
* The maximum length of a message body in bytes. To allow for future
* changes in the protocol, this is smaller than the maximum packet length
* even when all the message's other fields have their maximum lengths.
*/
static final int MAX_BODY_LENGTH =
ProtocolConstants.MAX_PACKET_LENGTH - 1024;
/** The maximum length of a subject line in UTF-8 bytes. */
static final int MAX_SUBJECT_LENGTH = 100;
/** The maximum length of a signature in bytes. */
static final int MAX_SIGNATURE_LENGTH = 100;
/** The length of the random salt in bytes. */
static final int SALT_LENGTH = 8;
/** Returns the message's unique identifier. */
MessageId getId();

View File

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

View File

@@ -11,4 +11,41 @@ public interface ProtocolConstants {
*/
static final int MAX_PACKET_LENGTH =
TransportConstants.MIN_CONNECTION_LENGTH - 1024;
/** The maximum number of transport plugins a node may support. */
static final int MAX_TRANSPORTS = 50;
/** The maximum number of properties per transport plugin. */
static final int MAX_PROPERTIES_PER_TRANSPORT = 100;
/** The maximum length of a property's key or value in UTF-8 bytes. */
static final int MAX_PROPERTY_LENGTH = 100;
/** The maximum number of groups a node may subscribe to. */
static final int MAX_GROUPS = 6000;
/** The maximum length of a group's name in UTF-8 bytes. */
static final int MAX_GROUP_NAME_LENGTH = 50;
/** The maximum length of a serialised public key in bytes. */
static final int MAX_PUBLIC_KEY_LENGTH = 100;
/** The maximum length of an author's name in UTF-8 bytes. */
static final int MAX_AUTHOR_NAME_LENGTH = 50;
/**
* The maximum length of a message body in bytes. To allow for future
* changes in the protocol, this is smaller than the maximum packet length
* even when all the message's other fields have their maximum lengths.
*/
static final int MAX_BODY_LENGTH = MAX_PACKET_LENGTH - 1024;
/** The maximum length of a message's subject line in UTF-8 bytes. */
static final int MAX_SUBJECT_LENGTH = 100;
/** The maximum length of a signature in bytes. */
static final int MAX_SIGNATURE_LENGTH = 100;
/** The length of a message's random salt in bytes. */
static final int SALT_LENGTH = 8;
}

View File

@@ -5,9 +5,6 @@ import java.util.Map;
/** A packet updating the sender's subscriptions. */
public interface SubscriptionUpdate {
/** The maximum number of subscriptions per update. */
static final int MAX_SUBS_PER_UPDATE = 6000;
/** Returns the subscriptions contained in the update. */
Map<Group, Long> getSubscriptions();

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

View File

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

View File

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

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.security.GeneralSecurityException;
import java.security.PrivateKey;
import net.sf.briar.api.protocol.Author;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.Message;
import net.sf.briar.api.protocol.MessageId;
public interface MessageEncoder {
/** Encodes a private message. */

View File

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

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;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.protocol.TransportIndex;
public interface BatchConnectionFactory {
void createIncomingConnection(TransportId t, ContactId c,
void createIncomingConnection(TransportIndex i, ContactId c,
BatchTransportReader r, byte[] encryptedIv);
void createOutgoingConnection(TransportId t, ContactId c,
void createOutgoingConnection(TransportIndex i, ContactId c,
BatchTransportWriter w);
}

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -73,8 +73,6 @@ class PollerImpl implements Poller, Runnable {
public int compareTo(PollTime p) {
if(time < p.time) return -1;
if(time > p.time) return 1;
if(plugin.getId().getInt() < p.plugin.getId().getInt()) return -1;
if(plugin.getId().getInt() > p.plugin.getId().getInt()) return 1;
return 0;
}
}

View File

@@ -18,10 +18,10 @@ import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.plugins.StreamPlugin;
import net.sf.briar.api.plugins.StreamPluginCallback;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.transport.StreamTransportConnection;
import net.sf.briar.plugins.AbstractPlugin;
import net.sf.briar.util.OsUtils;
@@ -29,7 +29,9 @@ import net.sf.briar.util.StringUtils;
class BluetoothPlugin extends AbstractPlugin implements StreamPlugin {
public static final int TRANSPORT_ID = 2;
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("d99c9313c04417dcf22fc60d12a187ea"
+ "00a539fd260f08a13a0d8a900cde5e49");
private static final TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG =

View File

@@ -9,13 +9,16 @@ import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.plugins.BatchPluginCallback;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.util.StringUtils;
class RemovableDrivePlugin extends FilePlugin
implements RemovableDriveMonitor.Callback {
public static final int TRANSPORT_ID = 0;
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("7c81bf5c9b1cd557685548c85f976bbd"
+ "e633d2418ea2e230e5710fb43c6f8cc0");
private static final TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG =

View File

@@ -14,14 +14,17 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.plugins.StreamPluginCallback;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.transport.StreamTransportConnection;
import net.sf.briar.util.StringUtils;
class SimpleSocketPlugin extends SocketPlugin {
public static final int TRANSPORT_ID = 1;
public static final byte[] TRANSPORT_ID =
StringUtils.fromHexString("58c66d999e492b85065924acfd739d80"
+ "c65a62f87e5a4fc6c284f95908b9007d");
private static final TransportId id = new TransportId(TRANSPORT_ID);
private static final Logger LOG =

View File

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

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.AuthorFactory;
import net.sf.briar.api.protocol.AuthorId;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
@@ -28,7 +29,9 @@ class AuthorFactoryImpl implements AuthorFactory {
throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
new AuthorImpl(null, name, publicKey).writeTo(w);
w.writeUserDefinedId(Types.AUTHOR);
w.writeString(name);
w.writeBytes(publicKey);
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.reset();
messageDigest.update(out.toByteArray());

View File

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

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

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.GroupFactory;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.serial.Writer;
import net.sf.briar.api.serial.WriterFactory;
@@ -27,7 +28,10 @@ class GroupFactoryImpl implements GroupFactory {
public Group createGroup(String name, byte[] publicKey) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
Writer w = writerFactory.createWriter(out);
new GroupImpl(null, name, publicKey).writeTo(w);
w.writeUserDefinedId(Types.GROUP);
w.writeString(name);
if(publicKey == null) w.writeNull();
else w.writeBytes(publicKey);
MessageDigest messageDigest = crypto.getMessageDigest();
messageDigest.reset();
messageDigest.update(out.toByteArray());

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;
import java.io.IOException;
import net.sf.briar.api.protocol.Group;
import net.sf.briar.api.protocol.GroupId;
import net.sf.briar.api.protocol.Types;
import net.sf.briar.api.serial.Writer;
class GroupImpl implements Group {
@@ -31,13 +27,6 @@ class GroupImpl implements Group {
return publicKey;
}
public void writeTo(Writer w) throws IOException {
w.writeUserDefinedId(Types.GROUP);
w.writeString(name);
if(publicKey == null) w.writeNull();
else w.writeBytes(publicKey);
}
@Override
public boolean equals(Object o) {
return o instanceof Group && id.equals(((Group) o).getId());

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

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

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

View File

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

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

View File

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

View File

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

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.Reader;
class SubscriptionReader implements ObjectReader<SubscriptionUpdate> {
class SubscriptionUpdateReader implements ObjectReader<SubscriptionUpdate> {
private final ObjectReader<Group> groupReader;
private final SubscriptionFactory subscriptionFactory;
private final SubscriptionUpdateFactory subscriptionFactory;
SubscriptionReader(ObjectReader<Group> groupReader,
SubscriptionFactory subscriptionFactory) {
SubscriptionUpdateReader(ObjectReader<Group> groupReader,
SubscriptionUpdateFactory subscriptionFactory) {
this.groupReader = groupReader;
this.subscriptionFactory = subscriptionFactory;
}

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;
import java.util.Map;
import java.util.Collection;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.TransportProperties;
import net.sf.briar.api.protocol.Transport;
import net.sf.briar.api.protocol.TransportUpdate;
class TransportUpdateImpl implements TransportUpdate {
private final Map<TransportId, TransportProperties> transports;
private final Collection<Transport> transports;
private final long timestamp;
TransportUpdateImpl(Map<TransportId, TransportProperties> transports,
TransportUpdateImpl(Collection<Transport> transports,
long timestamp) {
this.transports = transports;
this.timestamp = timestamp;
}
public Map<TransportId, TransportProperties> getTransports() {
public Collection<Transport> getTransports() {
return transports;
}

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;
if(capacity < idLength + overhead) return false;
if(!started) start();
b.writeTo(w);
w.writeUserDefinedId(Types.BATCH_ID);
w.writeBytes(b.getBytes());
capacity -= idLength;
return true;
}

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;
if(capacity < idLength + overhead) return false;
if(!started) start();
m.writeTo(w);
w.writeUserDefinedId(Types.MESSAGE_ID);
w.writeBytes(m.getBytes());
capacity -= idLength;
return true;
}

View File

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

View File

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

View File

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

View File

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

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.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.protocol.TransportId;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.BatchConnectionFactory;
import net.sf.briar.api.transport.BatchTransportReader;
import net.sf.briar.api.transport.BatchTransportWriter;
import net.sf.briar.api.transport.ConnectionContext;
import net.sf.briar.api.transport.ConnectionDispatcher;
import net.sf.briar.api.transport.ConnectionRecogniser;
import net.sf.briar.api.transport.ConnectionRecogniserFactory;
import net.sf.briar.api.transport.StreamConnectionFactory;
import net.sf.briar.api.transport.StreamTransportConnection;
import net.sf.briar.api.transport.TransportConstants;
import com.google.inject.Inject;
public class ConnectionDispatcherImpl implements ConnectionDispatcher {
private static final Logger LOG =
Logger.getLogger(ConnectionDispatcherImpl.class.getName());
private final ConnectionRecogniserFactory recFactory;
private final ConnectionRecogniser recogniser;
private final BatchConnectionFactory batchConnFactory;
private final StreamConnectionFactory streamConnFactory;
private final Map<TransportId, ConnectionRecogniser> recognisers;
ConnectionDispatcherImpl(ConnectionRecogniserFactory recFactory,
@Inject
ConnectionDispatcherImpl(ConnectionRecogniser recogniser,
BatchConnectionFactory batchConnFactory,
StreamConnectionFactory streamConnFactory) {
this.recFactory = recFactory;
this.recogniser = recogniser;
this.batchConnFactory = batchConnFactory;
this.streamConnFactory = streamConnFactory;
recognisers = new HashMap<TransportId, ConnectionRecogniser>();
}
public void dispatchReader(TransportId t, BatchTransportReader r) {
@@ -49,21 +49,27 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
r.dispose(false);
return;
}
// Get the contact ID, or null if the IV wasn't expected
ContactId c;
// Get the connection context, or null if the IV wasn't expected
ConnectionContext ctx;
try {
ConnectionRecogniser rec = getRecogniser(t);
c = rec.acceptConnection(encryptedIv);
ctx = recogniser.acceptConnection(encryptedIv);
} catch(DbException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
r.dispose(false);
return;
}
if(c == null) {
if(ctx == null) {
r.dispose(false);
return;
}
batchConnFactory.createIncomingConnection(t, c, r, encryptedIv);
if(!t.equals(ctx.getTransportId())) {
if(LOG.isLoggable(Level.WARNING))
LOG.warning("Connection has unexpected transport ID");
r.dispose(false);
return;
}
batchConnFactory.createIncomingConnection(ctx.getTransportIndex(),
ctx.getContactId(), r, encryptedIv);
}
private byte[] readIv(InputStream in) throws IOException {
@@ -77,20 +83,9 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
return b;
}
private ConnectionRecogniser getRecogniser(TransportId t) {
synchronized(recognisers) {
ConnectionRecogniser rec = recognisers.get(t);
if(rec == null) {
rec = recFactory.createConnectionRecogniser(t);
recognisers.put(t, rec);
}
return rec;
}
}
public void dispatchWriter(TransportId t, ContactId c,
public void dispatchWriter(TransportIndex i, ContactId c,
BatchTransportWriter w) {
batchConnFactory.createOutgoingConnection(t, c, w);
batchConnFactory.createOutgoingConnection(i, c, w);
}
public void dispatchIncomingConnection(TransportId t,
@@ -104,25 +99,31 @@ public class ConnectionDispatcherImpl implements ConnectionDispatcher {
s.dispose(false);
return;
}
// Get the contact ID, or null if the IV wasn't expected
ContactId c;
// Get the connection context, or null if the IV wasn't expected
ConnectionContext ctx;
try {
ConnectionRecogniser rec = getRecogniser(t);
c = rec.acceptConnection(encryptedIv);
ctx = recogniser.acceptConnection(encryptedIv);
} catch(DbException e) {
if(LOG.isLoggable(Level.WARNING)) LOG.warning(e.getMessage());
s.dispose(false);
return;
}
if(c == null) {
if(ctx == null) {
s.dispose(false);
return;
}
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) {
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.SecretKey;
import net.sf.briar.api.TransportId;
import net.sf.briar.api.crypto.CryptoComponent;
import net.sf.briar.api.protocol.TransportIndex;
import net.sf.briar.api.transport.ConnectionReader;
import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -26,7 +26,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
}
public ConnectionReader createConnectionReader(InputStream in,
TransportId t, byte[] encryptedIv, byte[] secret) {
TransportIndex i, byte[] encryptedIv, byte[] secret) {
// Decrypt the IV
Cipher ivCipher = crypto.getIvCipher();
SecretKey ivKey = crypto.deriveIncomingIvKey(secret);
@@ -42,21 +42,22 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
throw new IllegalArgumentException(badKey);
}
// Validate the IV
if(!IvEncoder.validateIv(iv, true, t))
if(!IvEncoder.validateIv(iv, true, i))
throw new IllegalArgumentException();
// Copy the connection number
long connection = IvEncoder.getConnectionNumber(iv);
return createConnectionReader(in, true, t, connection, secret);
return createConnectionReader(in, true, i, connection, secret);
}
public ConnectionReader createConnectionReader(InputStream in,
TransportId t, long connection, byte[] secret) {
return createConnectionReader(in, false, t, connection, secret);
TransportIndex i, long connection, byte[] secret) {
return createConnectionReader(in, false, i, connection, secret);
}
private ConnectionReader createConnectionReader(InputStream in,
boolean initiator, TransportId t, long connection, byte[] secret) {
byte[] iv = IvEncoder.encodeIv(initiator, t, connection);
boolean initiator, TransportIndex i, long connection,
byte[] secret) {
byte[] iv = IvEncoder.encodeIv(initiator, i, connection);
// Create the decrypter
Cipher frameCipher = crypto.getFrameCipher();
SecretKey frameKey = crypto.deriveIncomingFrameKey(secret);

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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