Replaced private messages with private groups.

Private messages are now the same as group messages, but groups can be
private or public. When a contact is added, a private group is created
and designated as the inbox for exchanging private messages with the
contact.
This commit is contained in:
akwizgran
2013-12-19 21:53:26 +00:00
parent 1d4213e9c6
commit 0dc869228b
61 changed files with 1717 additions and 2329 deletions

View File

@@ -1,11 +1,9 @@
package net.sf.briar.api;
import java.io.IOException;
public interface AuthorFactory {
Author createAuthor(String name, byte[] publicKey) throws IOException;
Author createAuthor(String name, byte[] publicKey);
LocalAuthor createLocalAuthor(String name, byte[] publicKey,
byte[] privateKey) throws IOException;
byte[] privateKey);
}

View File

@@ -47,6 +47,9 @@ public interface CryptoComponent {
byte[] deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/** Derives a group salt from the given master secret. */
byte[] deriveGroupSalt(byte[] secret);
/**
* Derives an initial secret for the given transport from the given master
* secret.

View File

@@ -48,22 +48,25 @@ public interface DatabaseComponent {
void removeListener(DatabaseListener d);
/**
* Stores a contact with the given pseudonym, associated with the given
* local pseudonym, and returns an ID for the contact.
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
*/
ContactId addContact(Author remote, AuthorId local) throws DbException;
/** Stores an endpoint. */
void addEndpoint(Endpoint ep) throws DbException;
/**
* Subscribes to a group, or returns false if the user already has the
* maximum number of public subscriptions.
*/
boolean addGroup(Group g) throws DbException;
/** Stores a local pseudonym. */
void addLocalAuthor(LocalAuthor a) throws DbException;
/** Stores a locally generated group message. */
void addLocalGroupMessage(Message m) throws DbException;
/** Stores a locally generated private message. */
void addLocalPrivateMessage(Message m, ContactId c) throws DbException;
/** Stores a local message. */
void addLocalMessage(Message m) throws DbException;
/**
* Stores the given temporary secrets and deletes any secrets that have
@@ -77,14 +80,17 @@ public interface DatabaseComponent {
*/
boolean addTransport(TransportId t, long maxLatency) throws DbException;
/** Returns true if any messages are sendable to the given contact. */
boolean containsSendableMessages(ContactId c) throws DbException;
/**
* Generates an acknowledgement for the given contact, or returns null if
* there are no messages to acknowledge.
* Returns an acknowledgement for the given contact, or null if there are
* no messages to acknowledge.
*/
Ack generateAck(ContactId c, int maxMessages) throws DbException;
/**
* Generates a batch of raw messages for the given contact, with a total
* Returns a batch of raw messages for the given contact, with a total
* length less than or equal to the given length, for transmission over a
* transport with the given maximum latency. Returns null if there are no
* sendable messages that fit in the given length.
@@ -93,7 +99,7 @@ public interface DatabaseComponent {
long maxLatency) throws DbException;
/**
* Generates a batch of raw messages for the given contact from the given
* Returns a batch of raw messages for the given contact from the given
* collection of requested messages, with a total length less than or equal
* to the given length, for transmission over a transport with the given
* maximum latency. Any messages that were either added to the batch, or
@@ -106,19 +112,19 @@ public interface DatabaseComponent {
throws DbException;
/**
* Generates an offer for the given contact, or returns null if there are
* no messages to offer.
* Returns an offer for the given contact, or null if there are no messages
* to offer.
*/
Offer generateOffer(ContactId c, int maxMessages) throws DbException;
/**
* Generates a retention ack for the given contact, or returns null if no
* Returns a retention ack for the given contact, or null if no retention
* ack is due.
*/
RetentionAck generateRetentionAck(ContactId c) throws DbException;
/**
* Generates a retention update for the given contact, for transmission
* Returns a retention update for the given contact, for transmission
* over a transport with the given latency. Returns null if no update is
* due.
*/
@@ -126,13 +132,13 @@ public interface DatabaseComponent {
throws DbException;
/**
* Generates a subscription ack for the given contact, or returns null if
* no ack is due.
* Returns a subscription ack for the given contact, or null if no
* subscription ack is due.
*/
SubscriptionAck generateSubscriptionAck(ContactId c) throws DbException;
/**
* Generates a subscription update for the given contact, for transmission
* Returns a subscription update for the given contact, for transmission
* over a transport with the given latency. Returns null if no update is
* due.
*/
@@ -140,14 +146,14 @@ public interface DatabaseComponent {
throws DbException;
/**
* Generates a batch of transport acks for the given contact, or returns
* null if no acks are due.
* Returns a batch of transport acks for the given contact, or null if no
* transport acks are due.
*/
Collection<TransportAck> generateTransportAcks(ContactId c)
throws DbException;
/**
* Generates a batch of transport updates for the given contact, for
* Returns a batch of transport updates for the given contact, for
* transmission over a transport with the given latency. Returns null if no
* updates are due.
*/
@@ -169,8 +175,20 @@ public interface DatabaseComponent {
/** Returns the group with the given ID, if the user subscribes to it. */
Group getGroup(GroupId g) throws DbException;
/** Returns the headers of all messages in the given group. */
Collection<GroupMessageHeader> getGroupMessageHeaders(GroupId g)
/** Returns all groups to which the user subscribes. */
Collection<Group> getGroups() throws DbException;
/**
* Returns the ID of the inbox group for the given contact, or null if no
* inbox group has been set.
*/
GroupId getInboxGroup(ContactId c) throws DbException;
/**
* Returns the headers of all messages in the inbox group for the given
* contact, or null if no inbox group has been set.
*/
Collection<MessageHeader> getInboxMessageHeaders(ContactId c)
throws DbException;
/**
@@ -195,11 +213,8 @@ public interface DatabaseComponent {
/** Returns the body of the message with the given ID. */
byte[] getMessageBody(MessageId m) throws DbException;
/**
* Returns the headers of all private messages to or from the given
* contact.
*/
Collection<PrivateMessageHeader> getPrivateMessageHeaders(ContactId c)
/** Returns the headers of all messages in the given group. */
Collection<MessageHeader> getMessageHeaders(GroupId g)
throws DbException;
/** Returns true if the given message has been read. */
@@ -212,24 +227,15 @@ public interface DatabaseComponent {
/** Returns all temporary secrets. */
Collection<TemporarySecret> getSecrets() throws DbException;
/** Returns the set of groups to which the user subscribes. */
Collection<Group> getSubscriptions() throws DbException;
/** Returns the maximum latencies of all local transports. */
Map<TransportId, Long> getTransportLatencies() throws DbException;
/** Returns the number of unread messages in each subscribed group. */
Map<GroupId, Integer> getUnreadMessageCounts() throws DbException;
/** Returns the contacts to which the given group is visible. */
/** Returns the IDs of all contacts to which the given group is visible. */
Collection<ContactId> getVisibility(GroupId g) throws DbException;
/** Returns the subscriptions that are visible to the given contact. */
Collection<GroupId> getVisibleSubscriptions(ContactId c) throws DbException;
/** Returns true if any messages are sendable to the given contact. */
boolean hasSendableMessages(ContactId c) throws DbException;
/**
* Increments the outgoing connection counter for the given endpoint
* in the given rotation period and returns the old value, or -1 if the
@@ -295,6 +301,12 @@ public interface DatabaseComponent {
/** Removes a contact (and all associated state) from the database. */
void removeContact(ContactId c) throws DbException;
/**
* Unsubscribes from a group. Any messages belonging to the group
* are deleted from the database.
*/
void removeGroup(Group g) throws DbException;
/**
* Removes a local pseudonym (and all associated state) from the database.
*/
@@ -314,8 +326,14 @@ public interface DatabaseComponent {
long centre, byte[] bitmap) throws DbException;
/**
* Marks the given message read or unread and returns true if it was
* previously read.
* Makes a private group visible to the given contact, adds it to the
* contact's subscriptions, and sets it as the inbox group for the contact.
*/
public void setInboxGroup(ContactId c, Group g) throws DbException;
/**
* Marks a message read or unread and returns true if it was previously
* read.
*/
boolean setReadFlag(MessageId m, boolean read) throws DbException;
@@ -342,16 +360,4 @@ public interface DatabaseComponent {
* current contacts.
*/
void setVisibleToAll(GroupId g, boolean all) throws DbException;
/**
* Subscribes to the given group, or returns false if the user already has
* the maximum number of subscriptions.
*/
boolean subscribe(Group g) throws DbException;
/**
* Unsubscribes from the given group. Any messages belonging to the group
* are deleted from the database.
*/
void unsubscribe(Group g) throws DbException;
}

View File

@@ -1,22 +0,0 @@
package net.sf.briar.api.db;
import net.sf.briar.api.Author;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.MessageId;
public class GroupMessageHeader extends MessageHeader {
private final GroupId groupId;
public GroupMessageHeader(MessageId id, MessageId parent, Author author,
String contentType, long timestamp, boolean read,
GroupId groupId) {
super(id, parent, author, contentType, timestamp, read);
this.groupId = groupId;
}
/** Returns the ID of the group to which the message belongs. */
public GroupId getGroupId() {
return groupId;
}
}

View File

@@ -1,20 +1,23 @@
package net.sf.briar.api.db;
import net.sf.briar.api.Author;
import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.MessageId;
public abstract class MessageHeader {
public class MessageHeader {
private final MessageId id, parent;
private final GroupId groupId;
private final Author author;
private final String contentType;
private final long timestamp;
private final boolean read;
protected MessageHeader(MessageId id, MessageId parent, Author author,
String contentType, long timestamp, boolean read) {
public MessageHeader(MessageId id, MessageId parent, GroupId groupId,
Author author, String contentType, long timestamp, boolean read) {
this.id = id;
this.parent = parent;
this.groupId = groupId;
this.author = author;
this.contentType = contentType;
this.timestamp = timestamp;
@@ -34,6 +37,13 @@ public abstract class MessageHeader {
return parent;
}
/**
* Returns the unique identifier of the group to which the message belongs.
*/
public GroupId getGroupId() {
return groupId;
}
/**
* Returns the message's author, or null if this is an anonymous message.
*/

View File

@@ -1,32 +0,0 @@
package net.sf.briar.api.db;
import net.sf.briar.api.Author;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.messaging.MessageId;
public class PrivateMessageHeader extends MessageHeader {
private final ContactId contactId;
private final boolean incoming;
public PrivateMessageHeader(MessageId id, MessageId parent, Author author,
String contentType, long timestamp, boolean read,
ContactId contactId, boolean incoming) {
super(id, parent, author, contentType, timestamp, read);
this.contactId = contactId;
this.incoming = incoming;
}
/**
* Returns the ID of the contact who is the sender (if incoming) or
* recipient (if outgoing) of this message.
*/
public ContactId getContactId() {
return contactId;
}
/** Returns true if this is an incoming message. */
public boolean isIncoming() {
return incoming;
}
}

View File

@@ -1,23 +0,0 @@
package net.sf.briar.api.db.event;
import net.sf.briar.api.messaging.Group;
/** An event that is broadcast when a group message is added to the database. */
public class GroupMessageAddedEvent extends DatabaseEvent {
private final Group group;
private final boolean incoming;
public GroupMessageAddedEvent(Group group, boolean incoming) {
this.group = group;
this.incoming = incoming;
}
public Group getGroup() {
return group;
}
public boolean isIncoming() {
return incoming;
}
}

View File

@@ -2,7 +2,7 @@ package net.sf.briar.api.db.event;
import net.sf.briar.api.AuthorId;
/** An event that is broadcast when a pseudonym for the user is added. */
/** An event that is broadcast when a local pseudonym is added. */
public class LocalAuthorAddedEvent extends DatabaseEvent {
private final AuthorId authorId;

View File

@@ -2,7 +2,7 @@ package net.sf.briar.api.db.event;
import net.sf.briar.api.AuthorId;
/** An event that is broadcast when a pseudonym for the user is removed. */
/** An event that is broadcast when a local pseudonym is removed. */
public class LocalAuthorRemovedEvent extends DatabaseEvent {
private final AuthorId authorId;

View File

@@ -0,0 +1,29 @@
package net.sf.briar.api.db.event;
import net.sf.briar.api.ContactId;
import net.sf.briar.api.messaging.Group;
/** An event that is broadcast when a message is added to the database. */
public class MessageAddedEvent extends DatabaseEvent {
private final Group group;
private final ContactId contactId;
public MessageAddedEvent(Group group, ContactId contactId) {
this.group = group;
this.contactId = contactId;
}
/** Returns the group to which the message belongs. */
public Group getGroup() {
return group;
}
/**
* Returns the ID of the contact from which the message was received, or
* null if the message was locally generated.
*/
public ContactId getContactId() {
return contactId;
}
}

View File

@@ -1,25 +0,0 @@
package net.sf.briar.api.db.event;
import net.sf.briar.api.ContactId;
/**
* An event that is broadcast when a private message is added to the database.
*/
public class PrivateMessageAddedEvent extends DatabaseEvent {
private final ContactId contactId;
private final boolean incoming;
public PrivateMessageAddedEvent(ContactId contactId, boolean incoming) {
this.contactId = contactId;
this.incoming = incoming;
}
public ContactId getContactId() {
return contactId;
}
public boolean isIncoming() {
return incoming;
}
}

View File

@@ -6,11 +6,13 @@ public class Group {
private final GroupId id;
private final String name;
private final byte[] salt;
private final boolean isPrivate;
public Group(GroupId id, String name, byte[] salt) {
public Group(GroupId id, String name, byte[] salt, boolean isPrivate) {
this.id = id;
this.name = name;
this.salt = salt;
this.isPrivate = isPrivate;
}
/** Returns the group's unique identifier. */
@@ -31,6 +33,11 @@ public class Group {
return salt;
}
/** Returns true if the group is private. */
public boolean isPrivate() {
return isPrivate;
}
@Override
public int hashCode() {
return id.hashCode();

View File

@@ -1,12 +1,10 @@
package net.sf.briar.api.messaging;
import java.io.IOException;
public interface GroupFactory {
/** Creates a group with the given name and a random salt. */
Group createGroup(String name) throws IOException;
Group createGroup(String name, boolean isPrivate);
/** Creates a group with the given name and salt. */
Group createGroup(String name, byte[] salt) throws IOException;
Group createGroup(String name, byte[] salt, boolean isPrivate);
}

View File

@@ -8,17 +8,10 @@ import net.sf.briar.api.crypto.PrivateKey;
public interface MessageFactory {
/** Creates a private message. */
Message createPrivateMessage(MessageId parent, String contentType,
long timestamp, byte[] body) throws IOException,
GeneralSecurityException;
/** Creates an anonymous group message. */
Message createAnonymousMessage(MessageId parent, Group group,
String contentType, long timestamp, byte[] body) throws IOException,
GeneralSecurityException;
/** Creates a pseudonymous group message. */
Message createPseudonymousMessage(MessageId parent, Group group,
Author author, PrivateKey privateKey, String contentType,
long timestamp, byte[] body) throws IOException,

View File

@@ -11,7 +11,7 @@ public interface MessagingConstants {
*/
int MAX_PACKET_LENGTH = MIN_CONNECTION_LENGTH / 2;
/** The maximum number of groups a user may subscribe to. */
/** The maximum number of public groups a user may subscribe to. */
int MAX_SUBSCRIPTIONS = 3000;
/** The maximum length of a group's name in UTF-8 bytes. */

View File

@@ -5,11 +5,11 @@ import java.util.Collection;
/** A packet updating the recipient's view of the sender's subscriptions. */
public class SubscriptionUpdate {
private final Collection<Group> subs;
private final Collection<Group> groups;
private final long version;
public SubscriptionUpdate(Collection<Group> subs, long version) {
this.subs = subs;
public SubscriptionUpdate(Collection<Group> groups, long version) {
this.groups = groups;
this.version = version;
}
@@ -18,7 +18,7 @@ public class SubscriptionUpdate {
* has made visible to the recipient.
*/
public Collection<Group> getGroups() {
return subs;
return groups;
}
/** Returns the update's version number. */

View File

@@ -8,19 +8,18 @@ public class UnverifiedMessage {
private final MessageId parent;
private final Group group;
private final Author author;
private final String contentType, subject;
private final String contentType;
private final long timestamp;
private final byte[] raw, signature;
private final int bodyStart, bodyLength, signedLength;
public UnverifiedMessage(MessageId parent, Group group, Author author,
String contentType, String subject, long timestamp, byte[] raw,
byte[] signature, int bodyStart, int bodyLength, int signedLength) {
String contentType, long timestamp, byte[] raw, byte[] signature,
int bodyStart, int bodyLength, int signedLength) {
this.parent = parent;
this.group = group;
this.author = author;
this.contentType = contentType;
this.subject = subject;
this.timestamp = timestamp;
this.raw = raw;
this.signature = signature;
@@ -58,15 +57,6 @@ public class UnverifiedMessage {
return contentType;
}
/**
* Returns the message's subject line, which is created from the first 50
* bytes of the message body if the content type is text/plain, or is the
* empty string otherwise.
*/
public String getSubject() {
return subject;
}
/** Returns the message's timestamp. */
public long getTimestamp() {
return timestamp;