mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Removed restricted groups (may be restored after beta testing).
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 948 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 558 B |
Binary file not shown.
|
Before Width: | Height: | Size: 752 B |
Binary file not shown.
|
Before Width: | Height: | Size: 918 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
@@ -140,7 +140,6 @@ OnItemClickListener {
|
||||
long now = System.currentTimeMillis();
|
||||
for(GroupStatus s : db.getAvailableGroups()) {
|
||||
Group g = s.getGroup();
|
||||
if(g.isRestricted()) continue;
|
||||
if(s.isSubscribed()) {
|
||||
try {
|
||||
Collection<GroupMessageHeader> headers =
|
||||
@@ -242,11 +241,8 @@ OnItemClickListener {
|
||||
|
||||
public void eventOccurred(DatabaseEvent e) {
|
||||
if(e instanceof GroupMessageAddedEvent) {
|
||||
Group g = ((GroupMessageAddedEvent) e).getGroup();
|
||||
if(!g.isRestricted()) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders(g);
|
||||
}
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message added, reloading");
|
||||
loadHeaders(((GroupMessageAddedEvent) e).getGroup());
|
||||
} else if(e instanceof MessageExpiredEvent) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Message expired, reloading");
|
||||
loadHeaders();
|
||||
@@ -255,18 +251,12 @@ OnItemClickListener {
|
||||
LOG.info("Remote subscriptions changed, reloading");
|
||||
loadAvailable();
|
||||
} else if(e instanceof SubscriptionAddedEvent) {
|
||||
Group g = ((SubscriptionAddedEvent) e).getGroup();
|
||||
if(!g.isRestricted()) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadHeaders();
|
||||
}
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadHeaders();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
Group g = ((SubscriptionRemovedEvent) e).getGroup();
|
||||
if(!g.isRestricted()) {
|
||||
// Reload the group, expecting NoSuchSubscriptionException
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadHeaders(g);
|
||||
}
|
||||
// Reload the group, expecting NoSuchSubscriptionException
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadHeaders(((SubscriptionRemovedEvent) e).getGroup());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +307,8 @@ OnItemClickListener {
|
||||
lifecycleManager.waitForDatabase();
|
||||
int available = 0;
|
||||
long now = System.currentTimeMillis();
|
||||
for(GroupStatus s : db.getAvailableGroups()) {
|
||||
if(!s.getGroup().isRestricted() && !s.isSubscribed())
|
||||
available++;
|
||||
}
|
||||
for(GroupStatus s : db.getAvailableGroups())
|
||||
if(!s.isSubscribed()) available++;
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading available took " + duration + " ms");
|
||||
|
||||
@@ -5,11 +5,8 @@ import static java.util.logging.Level.WARNING;
|
||||
import static net.sf.briar.android.groups.ManageGroupsItem.NONE;
|
||||
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -78,13 +75,10 @@ implements DatabaseListener, OnItemClickListener {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
long now = System.currentTimeMillis();
|
||||
List<GroupStatus> available = new ArrayList<GroupStatus>();
|
||||
for(GroupStatus s : db.getAvailableGroups())
|
||||
if(!s.getGroup().isRestricted()) available.add(s);
|
||||
Collection<GroupStatus> available = db.getAvailableGroups();
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Load took " + duration + " ms");
|
||||
available = Collections.unmodifiableList(available);
|
||||
displayAvailableGroups(available);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
@@ -124,17 +118,11 @@ implements DatabaseListener, OnItemClickListener {
|
||||
LOG.info("Remote subscriptions changed, reloading");
|
||||
loadAvailableGroups();
|
||||
} else if(e instanceof SubscriptionAddedEvent) {
|
||||
Group g = ((SubscriptionAddedEvent) e).getGroup();
|
||||
if(g.isRestricted()) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadAvailableGroups();
|
||||
}
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group added, reloading");
|
||||
loadAvailableGroups();
|
||||
} else if(e instanceof SubscriptionRemovedEvent) {
|
||||
Group g = ((SubscriptionRemovedEvent) e).getGroup();
|
||||
if(g.isRestricted()) {
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadAvailableGroups();
|
||||
}
|
||||
if(LOG.isLoggable(INFO)) LOG.info("Group removed, reloading");
|
||||
loadAvailableGroups();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,7 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -213,14 +210,12 @@ implements OnItemSelectedListener, OnClickListener {
|
||||
public void run() {
|
||||
try {
|
||||
lifecycleManager.waitForDatabase();
|
||||
List<Group> groups = new ArrayList<Group>();
|
||||
long now = System.currentTimeMillis();
|
||||
for(Group g : db.getSubscriptions())
|
||||
if(!g.isRestricted()) groups.add(g);
|
||||
Collection<Group> groups = db.getSubscriptions();
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if(LOG.isLoggable(INFO))
|
||||
LOG.info("Loading groups took " + duration + " ms");
|
||||
displayGroups(Collections.unmodifiableList(groups));
|
||||
displayGroups(groups);
|
||||
} catch(DbException e) {
|
||||
if(LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
|
||||
@@ -17,7 +17,6 @@ import net.sf.briar.api.messaging.Ack;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.GroupStatus;
|
||||
import net.sf.briar.api.messaging.LocalGroup;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.Offer;
|
||||
@@ -62,12 +61,6 @@ public interface DatabaseComponent {
|
||||
/** Stores a pseudonym that the user can use to sign messages. */
|
||||
void addLocalAuthor(LocalAuthor a) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a restricted group to which the user can post messages. Storing
|
||||
* a group does not create a subscription to it.
|
||||
*/
|
||||
void addLocalGroup(LocalGroup g) throws DbException;
|
||||
|
||||
/** Stores a locally generated group message. */
|
||||
void addLocalGroupMessage(Message m) throws DbException;
|
||||
|
||||
@@ -194,9 +187,6 @@ public interface DatabaseComponent {
|
||||
/** Returns all pseudonyms that the user can use to sign messages. */
|
||||
Collection<LocalAuthor> getLocalAuthors() throws DbException;
|
||||
|
||||
/** Returns all restricted groups to which the user can post messages. */
|
||||
Collection<LocalGroup> getLocalGroups() throws DbException;
|
||||
|
||||
/** Returns the local transport properties for all transports. */
|
||||
Map<TransportId, TransportProperties> getLocalProperties()
|
||||
throws DbException;
|
||||
|
||||
@@ -5,12 +5,12 @@ public class Group {
|
||||
|
||||
private final GroupId id;
|
||||
private final String name;
|
||||
private final byte[] publicKey;
|
||||
private final byte[] salt;
|
||||
|
||||
public Group(GroupId id, String name, byte[] publicKey) {
|
||||
public Group(GroupId id, String name, byte[] salt) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.publicKey = publicKey;
|
||||
this.salt = salt;
|
||||
}
|
||||
|
||||
/** Returns the group's unique identifier. */
|
||||
@@ -23,18 +23,12 @@ public class Group {
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Returns true if the group is restricted. */
|
||||
public boolean isRestricted() {
|
||||
return publicKey != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the group is restricted, returns the public key used to verify the
|
||||
* signatures on all messages sent to the group. If the group is
|
||||
* unrestricted, returns null.
|
||||
* Returns the salt used to distinguish the group from other groups with
|
||||
* the same name.
|
||||
*/
|
||||
public byte[] getPublicKey() {
|
||||
return publicKey;
|
||||
public byte[] getSalt() {
|
||||
return salt;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,13 +4,9 @@ import java.io.IOException;
|
||||
|
||||
public interface GroupFactory {
|
||||
|
||||
/** Creates an unrestricted group. */
|
||||
/** Creates a group with the given name and a random salt. */
|
||||
Group createGroup(String name) throws IOException;
|
||||
|
||||
/** Creates a restricted group. */
|
||||
Group createGroup(String name, byte[] publicKey) throws IOException;
|
||||
|
||||
/** Creates a restricted group to which the local user can post messages. */
|
||||
LocalGroup createLocalGroup(String name, byte[] publicKey,
|
||||
byte[] privateKey) throws IOException;
|
||||
/** Creates a group with the given name and salt. */
|
||||
Group createGroup(String name, byte[] salt) throws IOException;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
package net.sf.briar.api.messaging;
|
||||
|
||||
/** A restricted group to which the local user can post messages. */
|
||||
public class LocalGroup extends Group {
|
||||
|
||||
private final byte[] privateKey;
|
||||
|
||||
public LocalGroup(GroupId id, String name, byte[] publicKey,
|
||||
byte[] privateKey) {
|
||||
super(id, name, publicKey);
|
||||
this.privateKey = privateKey;
|
||||
}
|
||||
|
||||
/** Returns the private key used to sign all messages sent to the group. */
|
||||
public byte[] getPrivateKey() {
|
||||
return privateKey;
|
||||
}
|
||||
}
|
||||
@@ -12,24 +12,13 @@ public interface MessageFactory {
|
||||
Message createPrivateMessage(MessageId parent, String contentType,
|
||||
byte[] body) throws IOException, GeneralSecurityException;
|
||||
|
||||
/** Creates an anonymous message to an unrestricted group. */
|
||||
/** Creates an anonymous group message. */
|
||||
Message createAnonymousMessage(MessageId parent, Group group,
|
||||
String contentType, byte[] body) throws IOException,
|
||||
GeneralSecurityException;
|
||||
|
||||
/** Creates an anonymous message to a restricted group. */
|
||||
Message createAnonymousMessage(MessageId parent, Group group,
|
||||
PrivateKey groupKey, String contentType, byte[] body)
|
||||
throws IOException, GeneralSecurityException;
|
||||
|
||||
/** Creates a pseudonymous message to an unrestricted group. */
|
||||
/** Creates a pseudonymous group message. */
|
||||
Message createPseudonymousMessage(MessageId parent, Group group,
|
||||
Author author, PrivateKey authorKey, String contentType,
|
||||
Author author, PrivateKey privateKey, String contentType,
|
||||
byte[] body) throws IOException, GeneralSecurityException;
|
||||
|
||||
/** Creates a pseudonymous message to a restricted group. */
|
||||
Message createPseudonymousMessage(MessageId parent, Group group,
|
||||
PrivateKey groupKey, Author author, PrivateKey authorKey,
|
||||
String contentType, byte[] body) throws IOException,
|
||||
GeneralSecurityException;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ public interface MessagingConstants {
|
||||
/** The maximum length of a group's name in UTF-8 bytes. */
|
||||
int MAX_GROUP_NAME_LENGTH = 50;
|
||||
|
||||
/** The length of a group's random salt in bytes. */
|
||||
int GROUP_SALT_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* 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
|
||||
@@ -31,7 +34,7 @@ public interface MessagingConstants {
|
||||
int MAX_SUBJECT_LENGTH = 100;
|
||||
|
||||
/** The length of a message's random salt in bytes. */
|
||||
int SALT_LENGTH = 8;
|
||||
int MESSAGE_SALT_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* The timestamp of the oldest message in the database is rounded using
|
||||
|
||||
@@ -10,13 +10,12 @@ public class UnverifiedMessage {
|
||||
private final Author author;
|
||||
private final String contentType, subject;
|
||||
private final long timestamp;
|
||||
private final byte[] raw, authorSig, groupSig;
|
||||
private final int bodyStart, bodyLength, signedByAuthor, signedByGroup;
|
||||
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[] authorSig, byte[] groupSig, int bodyStart, int bodyLength,
|
||||
int signedByAuthor, int signedByGroup) {
|
||||
byte[] signature, int bodyStart, int bodyLength, int signedLength) {
|
||||
this.parent = parent;
|
||||
this.group = group;
|
||||
this.author = author;
|
||||
@@ -24,12 +23,10 @@ public class UnverifiedMessage {
|
||||
this.subject = subject;
|
||||
this.timestamp = timestamp;
|
||||
this.raw = raw;
|
||||
this.authorSig = authorSig;
|
||||
this.groupSig = groupSig;
|
||||
this.signature = signature;
|
||||
this.bodyStart = bodyStart;
|
||||
this.bodyLength = bodyLength;
|
||||
this.signedByAuthor = signedByAuthor;
|
||||
this.signedByGroup = signedByGroup;
|
||||
this.signedLength = signedLength;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,16 +80,8 @@ public class UnverifiedMessage {
|
||||
/**
|
||||
* Returns the author's signature, or null if this is an anonymous message.
|
||||
*/
|
||||
public byte[] getAuthorSignature() {
|
||||
return authorSig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group's signature, or null if this is a private message or
|
||||
* a message belonging to an unrestricted group.
|
||||
*/
|
||||
public byte[] getGroupSignature() {
|
||||
return groupSig;
|
||||
public byte[] getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/** Returns the offset of the message body within the serialised message. */
|
||||
@@ -109,15 +98,7 @@ public class UnverifiedMessage {
|
||||
* Returns the length in bytes of the data covered by the author's
|
||||
* signature.
|
||||
*/
|
||||
public int getLengthSignedByAuthor() {
|
||||
return signedByAuthor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length in bytes of the data covered by the group's
|
||||
* signature.
|
||||
*/
|
||||
public int getLengthSignedByGroup() {
|
||||
return signedByGroup;
|
||||
public int getSignedLength() {
|
||||
return signedLength;
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.GroupStatus;
|
||||
import net.sf.briar.api.messaging.LocalGroup;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.Rating;
|
||||
@@ -112,14 +111,6 @@ interface Database<T> {
|
||||
*/
|
||||
void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a restricted group to which the user can post messages. Storing
|
||||
* a group does not create a subscription to it.
|
||||
* <p>
|
||||
* Locking: identity write.
|
||||
*/
|
||||
void addLocalGroup(T txn, LocalGroup g) throws DbException;
|
||||
|
||||
/**
|
||||
* Records a received message as needing to be acknowledged.
|
||||
* <p>
|
||||
@@ -192,14 +183,6 @@ interface Database<T> {
|
||||
*/
|
||||
boolean containsContact(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given restricted group to
|
||||
* which the user can post messages.
|
||||
* <p>
|
||||
* Locking: identity read.
|
||||
*/
|
||||
boolean containsLocalGroup(T txn, GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns true if the database contains the given message.
|
||||
* <p>
|
||||
@@ -301,6 +284,14 @@ interface Database<T> {
|
||||
*/
|
||||
MessageId getGroupMessageParent(T txn, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all group messages posted by the given author.
|
||||
* <p>
|
||||
* Locking: message read.
|
||||
*/
|
||||
Collection<MessageId> getGroupMessages(T txn, AuthorId a)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the time at which a connection to each contact was last opened
|
||||
* or closed.
|
||||
@@ -323,13 +314,6 @@ interface Database<T> {
|
||||
*/
|
||||
Collection<LocalAuthor> getLocalAuthors(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns all restricted groups to which the user can post messages.
|
||||
* <p>
|
||||
* Locking: identity read.
|
||||
*/
|
||||
Collection<LocalGroup> getLocalGroups(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the local transport properties for all transports.
|
||||
* <p>
|
||||
@@ -556,15 +540,6 @@ interface Database<T> {
|
||||
*/
|
||||
Map<GroupId, Integer> getUnreadMessageCounts(T txn) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the IDs of all messages posted by the given author to
|
||||
* unrestricted groups.
|
||||
* <p>
|
||||
* Locking: message read.
|
||||
*/
|
||||
Collection<MessageId> getUnrestrictedGroupMessages(T txn, AuthorId a)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Returns the contacts to which the given group is visible.
|
||||
* <p>
|
||||
@@ -631,13 +606,6 @@ interface Database<T> {
|
||||
*/
|
||||
void removeContact(T txn, ContactId c) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes the given restricted group to which the user can post messages.
|
||||
* <p>
|
||||
* Locking: identity write.
|
||||
*/
|
||||
void removeLocalGroup(T txn, GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes a message (and all associated state) from the database.
|
||||
* <p>
|
||||
|
||||
@@ -65,7 +65,6 @@ import net.sf.briar.api.messaging.Ack;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.GroupStatus;
|
||||
import net.sf.briar.api.messaging.LocalGroup;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.Offer;
|
||||
@@ -285,22 +284,6 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public void addLocalGroup(LocalGroup g) throws DbException {
|
||||
identityLock.writeLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
db.addLocalGroup(txn, g);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
identityLock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void addLocalGroupMessage(Message m) throws DbException {
|
||||
boolean added = false;
|
||||
contactLock.readLock().lock();
|
||||
@@ -362,13 +345,9 @@ DatabaseCleaner.Callback {
|
||||
if(!c.equals(sender)) db.addStatus(txn, c, id, false);
|
||||
}
|
||||
// Calculate and store the message's sendability
|
||||
if(m.getGroup().isRestricted()) {
|
||||
db.setSendability(txn, id, 1);
|
||||
} else {
|
||||
int sendability = calculateSendability(txn, m);
|
||||
db.setSendability(txn, id, sendability);
|
||||
if(sendability > 0) updateAncestorSendability(txn, id, true);
|
||||
}
|
||||
int sendability = calculateSendability(txn, m);
|
||||
db.setSendability(txn, id, sendability);
|
||||
if(sendability > 0) updateAncestorSendability(txn, id, true);
|
||||
// Count the bytes stored
|
||||
synchronized(spaceLock) {
|
||||
bytesStoredSinceLastCheck += m.getSerialised().length;
|
||||
@@ -1066,23 +1045,6 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<LocalGroup> getLocalGroups() throws DbException {
|
||||
identityLock.readLock().lock();
|
||||
try {
|
||||
T txn = db.startTransaction();
|
||||
try {
|
||||
Collection<LocalGroup> groups = db.getLocalGroups(txn);
|
||||
db.commitTransaction(txn);
|
||||
return groups;
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
throw e;
|
||||
}
|
||||
} finally {
|
||||
identityLock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<TransportId, TransportProperties> getLocalProperties()
|
||||
throws DbException {
|
||||
transportLock.readLock().lock();
|
||||
@@ -1964,8 +1926,8 @@ DatabaseCleaner.Callback {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the sendability of all messages posted by the given author to
|
||||
* unrestricted groups, and the ancestors of those messages if necessary.
|
||||
* Updates the sendability of all group messages posted by the given
|
||||
* author, and the ancestors of those messages if necessary.
|
||||
* <p>
|
||||
* Locking: message write.
|
||||
* @param increment true if the user's rating for the author has changed
|
||||
@@ -1973,7 +1935,7 @@ DatabaseCleaner.Callback {
|
||||
*/
|
||||
private void updateAuthorSendability(T txn, AuthorId a, boolean increment)
|
||||
throws DbException {
|
||||
for(MessageId id : db.getUnrestrictedGroupMessages(txn, a)) {
|
||||
for(MessageId id : db.getGroupMessages(txn, a)) {
|
||||
int sendability = db.getSendability(txn, id);
|
||||
if(increment) {
|
||||
db.setSendability(txn, id, sendability + 1);
|
||||
@@ -2125,8 +2087,6 @@ DatabaseCleaner.Callback {
|
||||
throw new NoSuchSubscriptionException();
|
||||
affected = db.getVisibility(txn, id);
|
||||
db.removeSubscription(txn, id);
|
||||
if(db.containsLocalGroup(txn, id))
|
||||
db.removeLocalGroup(txn, id);
|
||||
db.commitTransaction(txn);
|
||||
} catch(DbException e) {
|
||||
db.abortTransaction(txn);
|
||||
|
||||
@@ -43,7 +43,6 @@ import net.sf.briar.api.db.PrivateMessageHeader;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.GroupStatus;
|
||||
import net.sf.briar.api.messaging.LocalGroup;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.Rating;
|
||||
@@ -71,15 +70,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " privateKey BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (authorId))";
|
||||
|
||||
// Locking: identity
|
||||
private static final String CREATE_LOCAL_GROUPS =
|
||||
"CREATE TABLE localGroups"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY NOT NULL,"
|
||||
+ " privateKey BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId))";
|
||||
|
||||
// Locking: contact
|
||||
// Dependents: message, retention, subscription, transport, window
|
||||
private static final String CREATE_CONTACTS =
|
||||
@@ -104,7 +94,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
"CREATE TABLE groups"
|
||||
+ " (groupId HASH NOT NULL,"
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY," // Null for unrestricted groups
|
||||
+ " salt BINARY NOT NULL,"
|
||||
+ " visibleToAll BOOLEAN NOT NULL,"
|
||||
+ " PRIMARY KEY (groupId))";
|
||||
|
||||
@@ -126,7 +116,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
+ " (contactId INT NOT NULL,"
|
||||
+ " groupId HASH NOT NULL," // Not a foreign key
|
||||
+ " name VARCHAR NOT NULL,"
|
||||
+ " publicKey BINARY," // Null for unrestricted groups
|
||||
+ " salt BINARY NOT NULL,"
|
||||
+ " PRIMARY KEY (contactId, groupId),"
|
||||
+ " FOREIGN KEY (contactId)"
|
||||
+ " REFERENCES contacts (contactId)"
|
||||
@@ -406,7 +396,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
try {
|
||||
s = txn.createStatement();
|
||||
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_LOCAL_GROUPS));
|
||||
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
|
||||
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR);
|
||||
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
|
||||
@@ -750,26 +739,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public void addLocalGroup(Connection txn, LocalGroup g) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "INSERT INTO localGroups"
|
||||
+ " (groupId, name, publicKey, privateKey)"
|
||||
+ " VALUES (?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getId().getBytes());
|
||||
ps.setString(2, g.getName());
|
||||
ps.setBytes(3, g.getPublicKey());
|
||||
ps.setBytes(4, g.getPrivateKey());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch(SQLException e) {
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMessageToAck(Connection txn, ContactId c, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -916,13 +885,12 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ps.close();
|
||||
if(count > MAX_SUBSCRIPTIONS) throw new DbStateException();
|
||||
if(count == MAX_SUBSCRIPTIONS) return false;
|
||||
sql = "INSERT INTO groups (groupId, name, publicKey, visibleToAll)"
|
||||
sql = "INSERT INTO groups (groupId, name, salt, visibleToAll)"
|
||||
+ " VALUES (?, ?, ?, FALSE)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getId().getBytes());
|
||||
ps.setString(2, g.getName());
|
||||
if(g.isRestricted()) ps.setBytes(3, g.getPublicKey());
|
||||
else ps.setNull(3, BINARY);
|
||||
ps.setBytes(3, g.getSalt());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
@@ -1059,27 +1027,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsLocalGroup(Connection txn, GroupId g)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT NULL FROM localGroups WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
boolean found = rs.next();
|
||||
if(rs.next()) throw new DbStateException();
|
||||
rs.close();
|
||||
ps.close();
|
||||
return found;
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsMessage(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -1172,8 +1119,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
// Add all subscribed groups to the list
|
||||
String sql = "SELECT groupId, name, publicKey, visibleToAll"
|
||||
+ " FROM groups";
|
||||
String sql = "SELECT groupId, name, salt, visibleToAll FROM groups";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
List<GroupStatus> groups = new ArrayList<GroupStatus>();
|
||||
@@ -1182,24 +1128,23 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
GroupId id = new GroupId(rs.getBytes(1));
|
||||
subscribed.add(id);
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
Group group = new Group(id, name, publicKey);
|
||||
byte[] salt = rs.getBytes(3);
|
||||
Group group = new Group(id, name, salt);
|
||||
boolean visibleToAll = rs.getBoolean(4);
|
||||
groups.add(new GroupStatus(group, true, visibleToAll));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
// Add all contact groups to the list, unless already added
|
||||
sql = "SELECT DISTINCT groupId, name, publicKey"
|
||||
+ " FROM contactGroups";
|
||||
sql = "SELECT DISTINCT groupId, name, salt FROM contactGroups";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
GroupId id = new GroupId(rs.getBytes(1));
|
||||
if(subscribed.contains(id)) continue;
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
Group group = new Group(id, name, publicKey);
|
||||
byte[] salt = rs.getBytes(3);
|
||||
Group group = new Group(id, name, salt);
|
||||
groups.add(new GroupStatus(group, false, false));
|
||||
}
|
||||
rs.close();
|
||||
@@ -1340,16 +1285,16 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT name, publicKey FROM groups WHERE groupId = ?";
|
||||
String sql = "SELECT name, salt FROM groups WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
if(!rs.next()) throw new DbStateException();
|
||||
String name = rs.getString(1);
|
||||
byte[] publicKey = rs.getBytes(2);
|
||||
byte[] salt = rs.getBytes(2);
|
||||
rs.close();
|
||||
ps.close();
|
||||
return new Group(g, name, publicKey);
|
||||
return new Group(g, name, salt);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
@@ -1439,6 +1384,31 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MessageId> getGroupMessages(Connection txn,
|
||||
AuthorId a) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT messageId"
|
||||
+ " FROM messages AS m"
|
||||
+ " JOIN groups AS g"
|
||||
+ " ON m.groupId = g.groupId"
|
||||
+ " WHERE authorId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, a.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
List<MessageId> ids = new ArrayList<MessageId>();
|
||||
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return Collections.unmodifiableList(ids);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<ContactId, Long> getLastConnected(Connection txn)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -1512,34 +1482,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<LocalGroup> getLocalGroups(Connection txn)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT groupId, name, publicKey, privateKey"
|
||||
+ " FROM localGroups";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
List<LocalGroup> groups = new ArrayList<LocalGroup>();
|
||||
while(rs.next()) {
|
||||
GroupId groupId = new GroupId(rs.getBytes(1));
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
byte[] privateKey = rs.getBytes(4);
|
||||
groups.add(new LocalGroup(groupId, name, publicKey,
|
||||
privateKey));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return Collections.unmodifiableList(groups);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<TransportId, TransportProperties> getLocalProperties(
|
||||
Connection txn) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -2223,7 +2165,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getStarredFlag(Connection txn, MessageId m) throws DbException {
|
||||
public boolean getStarredFlag(Connection txn, MessageId m)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
@@ -2249,15 +2192,15 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT groupId, name, publicKey FROM groups";
|
||||
String sql = "SELECT groupId, name, salt FROM groups";
|
||||
ps = txn.prepareStatement(sql);
|
||||
rs = ps.executeQuery();
|
||||
List<Group> subs = new ArrayList<Group>();
|
||||
while(rs.next()) {
|
||||
GroupId groupId = new GroupId(rs.getBytes(1));
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, publicKey));
|
||||
byte[] salt = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, salt));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
@@ -2274,7 +2217,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT groupId, name, publicKey FROM contactGroups"
|
||||
String sql = "SELECT groupId, name, salt FROM contactGroups"
|
||||
+ " WHERE contactId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
@@ -2283,8 +2226,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
while(rs.next()) {
|
||||
GroupId groupId = new GroupId(rs.getBytes(1));
|
||||
String name = rs.getString(2);
|
||||
byte[] publicKey = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, publicKey));
|
||||
byte[] salt = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, salt));
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
@@ -2336,7 +2279,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT g.groupId, name, publicKey,"
|
||||
String sql = "SELECT g.groupId, name, salt,"
|
||||
+ " localVersion, txCount"
|
||||
+ " FROM groups AS g"
|
||||
+ " JOIN groupVisibilities AS vis"
|
||||
@@ -2356,8 +2299,8 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
while(rs.next()) {
|
||||
GroupId groupId = new GroupId(rs.getBytes(1));
|
||||
String name = rs.getString(2);
|
||||
byte[] key = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, key));
|
||||
byte[] salt = rs.getBytes(3);
|
||||
subs.add(new Group(groupId, name, salt));
|
||||
version = rs.getLong(4);
|
||||
txCount = rs.getInt(5);
|
||||
}
|
||||
@@ -2564,32 +2507,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MessageId> getUnrestrictedGroupMessages(Connection txn,
|
||||
AuthorId a) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
try {
|
||||
String sql = "SELECT messageId"
|
||||
+ " FROM messages AS m"
|
||||
+ " JOIN groups AS g"
|
||||
+ " ON m.groupId = g.groupId"
|
||||
+ " WHERE authorId = ?"
|
||||
+ " AND publicKey IS NULL";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, a.getBytes());
|
||||
rs = ps.executeQuery();
|
||||
List<MessageId> ids = new ArrayList<MessageId>();
|
||||
while(rs.next()) ids.add(new MessageId(rs.getBytes(1)));
|
||||
rs.close();
|
||||
ps.close();
|
||||
return Collections.unmodifiableList(ids);
|
||||
} catch(SQLException e) {
|
||||
tryToClose(rs);
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ContactId> getVisibility(Connection txn, GroupId g)
|
||||
throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
@@ -2808,21 +2725,6 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
}
|
||||
}
|
||||
|
||||
public void removeLocalGroup(Connection txn, GroupId g) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
String sql = "DELETE FROM localGroups WHERE groupId = ?";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setBytes(1, g.getBytes());
|
||||
int affected = ps.executeUpdate();
|
||||
if(affected != 1) throw new DbStateException();
|
||||
ps.close();
|
||||
} catch(SQLException e) {
|
||||
tryToClose(ps);
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeMessage(Connection txn, MessageId m) throws DbException {
|
||||
PreparedStatement ps = null;
|
||||
try {
|
||||
@@ -3395,15 +3297,14 @@ abstract class JdbcDatabase implements Database<Connection> {
|
||||
// Store the new subscriptions, if any
|
||||
if(subs.isEmpty()) return true;
|
||||
sql = "INSERT INTO contactGroups"
|
||||
+ " (contactId, groupId, name, publicKey)"
|
||||
+ " (contactId, groupId, name, salt)"
|
||||
+ " VALUES (?, ?, ?, ?)";
|
||||
ps = txn.prepareStatement(sql);
|
||||
ps.setInt(1, c.getInt());
|
||||
for(Group g : subs) {
|
||||
ps.setBytes(2, g.getId().getBytes());
|
||||
ps.setString(3, g.getName());
|
||||
if(g.isRestricted()) ps.setBytes(4, g.getPublicKey());
|
||||
else ps.setNull(4, BINARY);
|
||||
ps.setBytes(4, g.getSalt());
|
||||
ps.addBatch();
|
||||
}
|
||||
int[] batchAffected = ps.executeBatch();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.sf.briar.messaging;
|
||||
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.Types.GROUP;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -10,7 +11,6 @@ import net.sf.briar.api.crypto.MessageDigest;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.GroupFactory;
|
||||
import net.sf.briar.api.messaging.GroupId;
|
||||
import net.sf.briar.api.messaging.LocalGroup;
|
||||
import net.sf.briar.api.serial.Writer;
|
||||
import net.sf.briar.api.serial.WriterFactory;
|
||||
|
||||
@@ -28,29 +28,20 @@ class GroupFactoryImpl implements GroupFactory {
|
||||
}
|
||||
|
||||
public Group createGroup(String name) throws IOException {
|
||||
return createGroup(name, null);
|
||||
byte[] salt = new byte[GROUP_SALT_LENGTH];
|
||||
crypto.getSecureRandom().nextBytes(salt);
|
||||
return createGroup(name, salt);
|
||||
}
|
||||
|
||||
public Group createGroup(String name, byte[] publicKey) throws IOException {
|
||||
GroupId id = getId(name, publicKey);
|
||||
return new Group(id, name, publicKey);
|
||||
}
|
||||
|
||||
public LocalGroup createLocalGroup(String name, byte[] publicKey,
|
||||
byte[] privateKey) throws IOException {
|
||||
GroupId id = getId(name, publicKey);
|
||||
return new LocalGroup(id, name, publicKey, privateKey);
|
||||
}
|
||||
|
||||
private GroupId getId(String name, byte[] publicKey) throws IOException {
|
||||
public Group createGroup(String name, byte[] salt) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
Writer w = writerFactory.createWriter(out);
|
||||
w.writeStructId(GROUP);
|
||||
w.writeString(name);
|
||||
if(publicKey == null) w.writeNull();
|
||||
else w.writeBytes(publicKey);
|
||||
w.writeBytes(salt);
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
messageDigest.update(out.toByteArray());
|
||||
return new GroupId(messageDigest.digest());
|
||||
GroupId id = new GroupId(messageDigest.digest());
|
||||
return new Group(id, name, salt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MESSAGE_SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.Types.AUTHOR;
|
||||
import static net.sf.briar.api.messaging.Types.GROUP;
|
||||
import static net.sf.briar.api.messaging.Types.MESSAGE;
|
||||
@@ -39,7 +39,7 @@ import com.google.inject.Inject;
|
||||
|
||||
class MessageFactoryImpl implements MessageFactory {
|
||||
|
||||
private final Signature authorSignature, groupSignature;
|
||||
private final Signature signature;
|
||||
private final SecureRandom random;
|
||||
private final MessageDigest messageDigest;
|
||||
private final WriterFactory writerFactory;
|
||||
@@ -49,8 +49,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
@Inject
|
||||
MessageFactoryImpl(CryptoComponent crypto, WriterFactory writerFactory,
|
||||
Clock clock) {
|
||||
authorSignature = crypto.getSignature();
|
||||
groupSignature = crypto.getSignature();
|
||||
signature = crypto.getSignature();
|
||||
random = crypto.getSecureRandom();
|
||||
messageDigest = crypto.getMessageDigest();
|
||||
this.writerFactory = writerFactory;
|
||||
@@ -60,46 +59,27 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
|
||||
public Message createPrivateMessage(MessageId parent, String contentType,
|
||||
byte[] body) throws IOException, GeneralSecurityException {
|
||||
return createMessage(parent, null, null, null, null, contentType, body);
|
||||
return createMessage(parent, null, null, null, contentType, body);
|
||||
}
|
||||
|
||||
public Message createAnonymousMessage(MessageId parent, Group group,
|
||||
String contentType, byte[] body) throws IOException,
|
||||
GeneralSecurityException {
|
||||
return createMessage(parent, group, null, null, null, contentType,
|
||||
body);
|
||||
}
|
||||
|
||||
public Message createAnonymousMessage(MessageId parent, Group group,
|
||||
PrivateKey groupKey, String contentType, byte[] body)
|
||||
throws IOException, GeneralSecurityException {
|
||||
return createMessage(parent, group, groupKey, null, null, contentType,
|
||||
body);
|
||||
return createMessage(parent, group, null, null, contentType, body);
|
||||
}
|
||||
|
||||
public Message createPseudonymousMessage(MessageId parent, Group group,
|
||||
Author author, PrivateKey authorKey, String contentType,
|
||||
Author author, PrivateKey privateKey, String contentType,
|
||||
byte[] body) throws IOException, GeneralSecurityException {
|
||||
return createMessage(parent, group, null, author, authorKey,
|
||||
contentType, body);
|
||||
return createMessage(parent, group, author, privateKey, contentType,
|
||||
body);
|
||||
}
|
||||
|
||||
public Message createPseudonymousMessage(MessageId parent, Group group,
|
||||
PrivateKey groupKey, Author author, PrivateKey authorKey,
|
||||
String contentType, byte[] body) throws IOException,
|
||||
GeneralSecurityException {
|
||||
return createMessage(parent, group, groupKey, author, authorKey,
|
||||
contentType, body);
|
||||
}
|
||||
|
||||
private Message createMessage(MessageId parent, Group group,
|
||||
PrivateKey groupKey, Author author, PrivateKey authorKey,
|
||||
String contentType, byte[] body) throws IOException,
|
||||
GeneralSecurityException {
|
||||
private Message createMessage(MessageId parent, Group group, Author author,
|
||||
PrivateKey privateKey, String contentType, byte[] body)
|
||||
throws IOException, GeneralSecurityException {
|
||||
// Validate the arguments
|
||||
if((author == null) != (authorKey == null))
|
||||
throw new IllegalArgumentException();
|
||||
if((group == null || !group.isRestricted()) != (groupKey == null))
|
||||
if((author == null) != (privateKey == null))
|
||||
throw new IllegalArgumentException();
|
||||
if(contentType.getBytes("UTF-8").length > MAX_CONTENT_TYPE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
@@ -113,17 +93,11 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
w.addConsumer(counting);
|
||||
Consumer digestingConsumer = new DigestingConsumer(messageDigest);
|
||||
w.addConsumer(digestingConsumer);
|
||||
Consumer authorConsumer = null;
|
||||
if(authorKey != null) {
|
||||
authorSignature.initSign(authorKey);
|
||||
authorConsumer = new SigningConsumer(authorSignature);
|
||||
w.addConsumer(authorConsumer);
|
||||
}
|
||||
Consumer groupConsumer = null;
|
||||
if(groupKey != null) {
|
||||
groupSignature.initSign(groupKey);
|
||||
groupConsumer = new SigningConsumer(groupSignature);
|
||||
w.addConsumer(groupConsumer);
|
||||
Consumer signingConsumer = null;
|
||||
if(privateKey != null) {
|
||||
signature.initSign(privateKey);
|
||||
signingConsumer = new SigningConsumer(signature);
|
||||
w.addConsumer(signingConsumer);
|
||||
}
|
||||
// Write the message
|
||||
w.writeStructId(MESSAGE);
|
||||
@@ -136,32 +110,22 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
w.writeString(contentType);
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
w.writeInt64(timestamp);
|
||||
byte[] salt = new byte[SALT_LENGTH];
|
||||
byte[] salt = new byte[MESSAGE_SALT_LENGTH];
|
||||
random.nextBytes(salt);
|
||||
w.writeBytes(salt);
|
||||
w.writeBytes(body);
|
||||
int bodyStart = (int) counting.getCount() - body.length;
|
||||
// Sign the message with the author's private key, if there is one
|
||||
if(authorKey == null) {
|
||||
if(privateKey == null) {
|
||||
w.writeNull();
|
||||
} else {
|
||||
w.removeConsumer(authorConsumer);
|
||||
byte[] sig = authorSignature.sign();
|
||||
w.removeConsumer(signingConsumer);
|
||||
byte[] sig = signature.sign();
|
||||
if(sig.length > MAX_SIGNATURE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
w.writeBytes(sig);
|
||||
}
|
||||
// Sign the message with the group's private key, if there is one
|
||||
if(groupKey == null) {
|
||||
w.writeNull();
|
||||
} else {
|
||||
w.removeConsumer(groupConsumer);
|
||||
byte[] sig = groupSignature.sign();
|
||||
if(sig.length > MAX_SIGNATURE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
w.writeBytes(sig);
|
||||
}
|
||||
// Hash the message, including the signatures, to get the message ID
|
||||
// Hash the message, including the signature, to get the message ID
|
||||
w.removeConsumer(digestingConsumer);
|
||||
MessageId id = new MessageId(messageDigest.digest());
|
||||
// If the content type is text/plain, extract a subject line
|
||||
@@ -181,8 +145,7 @@ class MessageFactoryImpl implements MessageFactory {
|
||||
private void writeGroup(Writer w, Group g) throws IOException {
|
||||
w.writeStructId(GROUP);
|
||||
w.writeString(g.getName());
|
||||
if(g.isRestricted()) w.writeBytes(g.getPublicKey());
|
||||
else w.writeNull();
|
||||
w.writeBytes(g.getSalt());
|
||||
}
|
||||
|
||||
private void writeAuthor(Writer w, Author a) throws IOException {
|
||||
|
||||
@@ -5,7 +5,7 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBJECT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.MESSAGE_SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.Types.MESSAGE;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -67,8 +67,8 @@ class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
long timestamp = r.readInt64();
|
||||
if(timestamp < 0) throw new FormatException();
|
||||
// Read the salt
|
||||
byte[] salt = r.readBytes(SALT_LENGTH);
|
||||
if(salt.length < SALT_LENGTH) throw new FormatException();
|
||||
byte[] salt = r.readBytes(MESSAGE_SALT_LENGTH);
|
||||
if(salt.length < MESSAGE_SALT_LENGTH) throw new FormatException();
|
||||
// Read the message body
|
||||
byte[] body = r.readBytes(MAX_BODY_LENGTH);
|
||||
// If the content type is text/plain, extract a subject line
|
||||
@@ -84,23 +84,17 @@ class MessageReader implements StructReader<UnverifiedMessage> {
|
||||
// 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
|
||||
int signedByAuthor = (int) counting.getCount();
|
||||
int signedLength = (int) counting.getCount();
|
||||
// Read the author's signature, if there is one
|
||||
byte[] authorSig = null;
|
||||
byte[] signature = null;
|
||||
if(author == null) r.readNull();
|
||||
else authorSig = r.readBytes(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.isRestricted()) r.readNull();
|
||||
else groupSig = r.readBytes(MAX_SIGNATURE_LENGTH);
|
||||
// That's all, folks
|
||||
else signature = r.readBytes(MAX_SIGNATURE_LENGTH);
|
||||
// The signature will be verified later
|
||||
r.removeConsumer(counting);
|
||||
r.removeConsumer(copying);
|
||||
byte[] raw = copying.getCopy();
|
||||
return new UnverifiedMessage(parent, group, author, contentType,
|
||||
subject, timestamp, raw, authorSig, groupSig, bodyStart,
|
||||
body.length, signedByAuthor, signedByGroup);
|
||||
subject, timestamp, raw, signature, bodyStart, body.length,
|
||||
signedLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import net.sf.briar.api.crypto.KeyParser;
|
||||
import net.sf.briar.api.crypto.MessageDigest;
|
||||
import net.sf.briar.api.crypto.PublicKey;
|
||||
import net.sf.briar.api.crypto.Signature;
|
||||
import net.sf.briar.api.messaging.Group;
|
||||
import net.sf.briar.api.messaging.Message;
|
||||
import net.sf.briar.api.messaging.MessageId;
|
||||
import net.sf.briar.api.messaging.MessageVerifier;
|
||||
@@ -31,7 +30,7 @@ class MessageVerifierImpl implements MessageVerifier {
|
||||
throws GeneralSecurityException {
|
||||
MessageDigest messageDigest = crypto.getMessageDigest();
|
||||
Signature signature = crypto.getSignature();
|
||||
// Hash the message, including the signatures, to get the message ID
|
||||
// Hash the message, including the signature, to get the message ID
|
||||
byte[] raw = m.getSerialised();
|
||||
messageDigest.update(raw);
|
||||
MessageId id = new MessageId(messageDigest.digest());
|
||||
@@ -40,20 +39,11 @@ class MessageVerifierImpl implements MessageVerifier {
|
||||
if(author != null) {
|
||||
PublicKey k = keyParser.parsePublicKey(author.getPublicKey());
|
||||
signature.initVerify(k);
|
||||
signature.update(raw, 0, m.getLengthSignedByAuthor());
|
||||
if(!signature.verify(m.getAuthorSignature()))
|
||||
signature.update(raw, 0, m.getSignedLength());
|
||||
if(!signature.verify(m.getSignature()))
|
||||
throw new GeneralSecurityException();
|
||||
}
|
||||
// Verify the group's signature, if there is one
|
||||
Group group = m.getGroup();
|
||||
if(group != null && group.isRestricted()) {
|
||||
PublicKey k = keyParser.parsePublicKey(group.getPublicKey());
|
||||
signature.initVerify(k);
|
||||
signature.update(raw, 0, m.getLengthSignedByGroup());
|
||||
if(!signature.verify(m.getGroupSignature()))
|
||||
throw new GeneralSecurityException();
|
||||
}
|
||||
return new MessageImpl(id, m.getParent(), group, author,
|
||||
return new MessageImpl(id, m.getParent(), m.getGroup(), author,
|
||||
m.getContentType(), m.getSubject(), m.getTimestamp(), raw,
|
||||
m.getBodyStart(), m.getBodyLength());
|
||||
}
|
||||
|
||||
@@ -133,8 +133,7 @@ class PacketWriterImpl implements PacketWriter {
|
||||
for(Group g : u.getGroups()) {
|
||||
w.writeStructId(GROUP);
|
||||
w.writeString(g.getName());
|
||||
if(g.isRestricted()) w.writeBytes(g.getPublicKey());
|
||||
else w.writeNull();
|
||||
w.writeBytes(g.getSalt());
|
||||
}
|
||||
w.writeListEnd();
|
||||
w.writeInt64(u.getVersion());
|
||||
|
||||
@@ -68,8 +68,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
private final ContactId contactId;
|
||||
private final byte[] secret;
|
||||
private final Author author;
|
||||
private final Group group, group1;
|
||||
private final Message message, message1, message2, message3;
|
||||
private final Group group;
|
||||
private final Message message, message1;
|
||||
private final String authorName = "Alice";
|
||||
private final String contentType = "text/plain";
|
||||
private final String messageBody = "Hello world";
|
||||
@@ -93,33 +93,23 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
// Create a shared secret
|
||||
secret = new byte[32];
|
||||
new Random().nextBytes(secret);
|
||||
// Create two groups: one restricted, one unrestricted
|
||||
// Create a group
|
||||
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
|
||||
group = groupFactory.createGroup("Unrestricted group");
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
KeyPair groupKeyPair = crypto.generateSignatureKeyPair();
|
||||
group1 = groupFactory.createGroup("Restricted group",
|
||||
groupKeyPair.getPublic().getEncoded());
|
||||
group = groupFactory.createGroup("Group");
|
||||
// Create an author
|
||||
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
|
||||
author = authorFactory.createAuthor(authorName,
|
||||
authorKeyPair.getPublic().getEncoded());
|
||||
// Create two messages to each group: one anonymous, one pseudonymous
|
||||
// Create two messages to the group: one anonymous, one pseudonymous
|
||||
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
||||
message = messageFactory.createAnonymousMessage(null, group,
|
||||
contentType, messageBody.getBytes("UTF-8"));
|
||||
message1 = messageFactory.createAnonymousMessage(null, group1,
|
||||
groupKeyPair.getPrivate(), contentType,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message2 = messageFactory.createPseudonymousMessage(null, group,
|
||||
message1 = messageFactory.createPseudonymousMessage(null, group,
|
||||
author, authorKeyPair.getPrivate(), contentType,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message3 = messageFactory.createPseudonymousMessage(null, group1,
|
||||
groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(),
|
||||
contentType, messageBody.getBytes("UTF-8"));
|
||||
messageIds = Arrays.asList(message.getId(), message1.getId(),
|
||||
message2.getId(), message3.getId());
|
||||
messageIds = Arrays.asList(message.getId(), message1.getId());
|
||||
// Create some transport properties
|
||||
transportId = new TransportId(TestUtils.getRandomId());
|
||||
transportProperties = new TransportProperties(Collections.singletonMap(
|
||||
@@ -145,18 +135,14 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
writer.writeMessage(message.getSerialised());
|
||||
writer.writeMessage(message1.getSerialised());
|
||||
writer.writeMessage(message2.getSerialised());
|
||||
writer.writeMessage(message3.getSerialised());
|
||||
|
||||
writer.writeOffer(new Offer(messageIds));
|
||||
|
||||
BitSet requested = new BitSet(4);
|
||||
BitSet requested = new BitSet(2);
|
||||
requested.set(1);
|
||||
requested.set(3);
|
||||
writer.writeRequest(new Request(requested, 4));
|
||||
writer.writeRequest(new Request(requested, 2));
|
||||
|
||||
SubscriptionUpdate su = new SubscriptionUpdate(
|
||||
Arrays.asList(group, group1), 1);
|
||||
SubscriptionUpdate su = new SubscriptionUpdate(Arrays.asList(group), 1);
|
||||
writer.writeSubscriptionUpdate(su);
|
||||
|
||||
TransportUpdate tu = new TransportUpdate(transportId,
|
||||
@@ -191,12 +177,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message2, messageVerifier.verifyMessage(m));
|
||||
assertTrue(reader.hasMessage());
|
||||
m = reader.readMessage();
|
||||
checkMessageEquality(message3, messageVerifier.verifyMessage(m));
|
||||
assertFalse(reader.hasMessage());
|
||||
|
||||
// Read the offer
|
||||
assertTrue(reader.hasOffer());
|
||||
@@ -209,15 +190,13 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
BitSet requested = req.getBitmap();
|
||||
assertFalse(requested.get(0));
|
||||
assertTrue(requested.get(1));
|
||||
assertFalse(requested.get(2));
|
||||
assertTrue(requested.get(3));
|
||||
// If there are any padding bits, they should all be zero
|
||||
assertEquals(2, requested.cardinality());
|
||||
assertEquals(1, requested.cardinality());
|
||||
|
||||
// Read the subscription update
|
||||
assertTrue(reader.hasSubscriptionUpdate());
|
||||
SubscriptionUpdate su = reader.readSubscriptionUpdate();
|
||||
assertEquals(Arrays.asList(group, group1), su.getGroups());
|
||||
assertEquals(Arrays.asList(group), su.getGroups());
|
||||
assertEquals(1, su.getVersion());
|
||||
|
||||
// Read the transport update
|
||||
|
||||
@@ -15,7 +15,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
|
||||
public void testEncryptionAndDecryption() {
|
||||
CryptoComponent crypto = new CryptoComponentImpl();
|
||||
Random random = new Random();
|
||||
byte[] input = new byte[123];
|
||||
byte[] input = new byte[1234];
|
||||
random.nextBytes(input);
|
||||
char[] password = "password".toCharArray();
|
||||
byte[] ciphertext = crypto.encryptWithPassword(input, password);
|
||||
@@ -27,7 +27,7 @@ public class PasswordBasedKdfTest extends BriarTestCase {
|
||||
public void testInvalidCiphertextReturnsNull() {
|
||||
CryptoComponent crypto = new CryptoComponentImpl();
|
||||
Random random = new Random();
|
||||
byte[] input = new byte[123];
|
||||
byte[] input = new byte[1234];
|
||||
random.nextBytes(input);
|
||||
char[] password = "password".toCharArray();
|
||||
byte[] ciphertext = crypto.encryptWithPassword(input, password);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.Rating.GOOD;
|
||||
import static net.sf.briar.api.messaging.Rating.UNRATED;
|
||||
|
||||
@@ -59,8 +61,8 @@ import org.junit.Test;
|
||||
public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
|
||||
protected final Object txn = new Object();
|
||||
protected final GroupId groupId, restrictedGroupId;
|
||||
protected final Group group, restrictedGroup;
|
||||
protected final GroupId groupId;
|
||||
protected final Group group;
|
||||
protected final AuthorId authorId;
|
||||
protected final Author author;
|
||||
protected final AuthorId localAuthorId;
|
||||
@@ -80,15 +82,12 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
|
||||
public DatabaseComponentTest() {
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
restrictedGroupId = new GroupId(TestUtils.getRandomId());
|
||||
group = new Group(groupId, "Group name", null);
|
||||
restrictedGroup = new Group(restrictedGroupId, "Restricted group name",
|
||||
new byte[60]);
|
||||
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
|
||||
authorId = new AuthorId(TestUtils.getRandomId());
|
||||
author = new Author(authorId, "Alice", new byte[60]);
|
||||
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
localAuthorId = new AuthorId(TestUtils.getRandomId());
|
||||
localAuthor = new LocalAuthor(localAuthorId, "Bob", new byte[60],
|
||||
new byte[60]);
|
||||
localAuthor = new LocalAuthor(localAuthorId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
privateMessageId = new MessageId(TestUtils.getRandomId());
|
||||
@@ -142,7 +141,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
// setRating(authorId, GOOD)
|
||||
oneOf(database).setRating(txn, authorId, GOOD);
|
||||
will(returnValue(UNRATED));
|
||||
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
|
||||
oneOf(database).getGroupMessages(txn, authorId);
|
||||
will(returnValue(Collections.emptyList()));
|
||||
oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class)));
|
||||
// setRating(authorId, GOOD) again
|
||||
@@ -186,8 +185,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).getVisibility(txn, groupId);
|
||||
will(returnValue(Collections.emptyList()));
|
||||
oneOf(database).removeSubscription(txn, groupId);
|
||||
oneOf(database).containsLocalGroup(txn, groupId);
|
||||
will(returnValue(false));
|
||||
oneOf(listener).eventOccurred(with(any(
|
||||
SubscriptionRemovedEvent.class)));
|
||||
oneOf(listener).eventOccurred(with(any(
|
||||
@@ -228,59 +225,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestrictedGroupMessagesAreAlwaysSendable()
|
||||
throws Exception {
|
||||
final Message groupMessage = new TestMessage(messageId, null,
|
||||
restrictedGroup, author, contentType, subject, timestamp, raw);
|
||||
final Message groupMessage1 = new TestMessage(messageId1, null,
|
||||
restrictedGroup, null, contentType, subject, timestamp, raw);
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
context.checking(new Expectations() {{
|
||||
// addLocalGroupMessage(groupMessage)
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsSubscription(txn, restrictedGroupId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).addGroupMessage(txn, groupMessage, false);
|
||||
will(returnValue(true));
|
||||
oneOf(database).setReadFlag(txn, messageId, true);
|
||||
oneOf(database).getContactIds(txn);
|
||||
will(returnValue(Arrays.asList(contactId)));
|
||||
oneOf(database).addStatus(txn, contactId, messageId, false);
|
||||
oneOf(database).setSendability(txn, messageId, 1);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// receiveMessage(groupMessage1)
|
||||
oneOf(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).containsVisibleSubscription(txn, contactId,
|
||||
restrictedGroupId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).addGroupMessage(txn, groupMessage1, true);
|
||||
will(returnValue(true));
|
||||
oneOf(database).addStatus(txn, contactId, messageId1, true);
|
||||
oneOf(database).getContactIds(txn);
|
||||
will(returnValue(Arrays.asList(contactId)));
|
||||
oneOf(database).setSendability(txn, messageId1, 1);
|
||||
oneOf(database).addMessageToAck(txn, contactId, messageId1);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
}});
|
||||
|
||||
DatabaseComponent db = createDatabaseComponent(database, cleaner,
|
||||
shutdown);
|
||||
|
||||
db.addLocalGroupMessage(groupMessage);
|
||||
db.receiveMessage(contactId, groupMessage1);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullParentStopsBackwardInclusion() throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@@ -295,7 +239,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setRating(txn, authorId, GOOD);
|
||||
will(returnValue(UNRATED));
|
||||
// The sendability of the author's messages should be incremented
|
||||
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
|
||||
oneOf(database).getGroupMessages(txn, authorId);
|
||||
will(returnValue(Arrays.asList(messageId)));
|
||||
oneOf(database).getSendability(txn, messageId);
|
||||
will(returnValue(0));
|
||||
@@ -327,7 +271,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setRating(txn, authorId, GOOD);
|
||||
will(returnValue(UNRATED));
|
||||
// The sendability of the author's messages should be incremented
|
||||
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
|
||||
oneOf(database).getGroupMessages(txn, authorId);
|
||||
will(returnValue(Arrays.asList(messageId)));
|
||||
oneOf(database).getSendability(txn, messageId);
|
||||
will(returnValue(0));
|
||||
@@ -364,7 +308,7 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
|
||||
oneOf(database).setRating(txn, authorId, GOOD);
|
||||
will(returnValue(UNRATED));
|
||||
// The sendability of the author's messages should be incremented
|
||||
oneOf(database).getUnrestrictedGroupMessages(txn, authorId);
|
||||
oneOf(database).getGroupMessages(txn, authorId);
|
||||
will(returnValue(Arrays.asList(messageId)));
|
||||
oneOf(database).getSendability(txn, messageId);
|
||||
will(returnValue(0));
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.sf.briar.db;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static net.sf.briar.api.messaging.MessagingConstants.GROUP_SALT_LENGTH;
|
||||
import static net.sf.briar.api.messaging.Rating.GOOD;
|
||||
import static net.sf.briar.api.messaging.Rating.UNRATED;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
@@ -71,12 +73,12 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
public H2DatabaseTest() throws Exception {
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
group = new Group(groupId, "Group name", null);
|
||||
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
|
||||
authorId = new AuthorId(TestUtils.getRandomId());
|
||||
author = new Author(authorId, "Alice", new byte[60]);
|
||||
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
localAuthorId = new AuthorId(TestUtils.getRandomId());
|
||||
localAuthor = new LocalAuthor(localAuthorId, "Bob", new byte[60],
|
||||
new byte[60]);
|
||||
localAuthor = new LocalAuthor(localAuthorId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
contentType = "text/plain";
|
||||
@@ -535,38 +537,28 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnrestrictedGroupMessages() throws Exception {
|
||||
public void testGetGroupMessages() throws Exception {
|
||||
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||
Author author1 = new Author(authorId1, "Bob", new byte[60]);
|
||||
Author author1 = new Author(authorId1, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Message message1 = new TestMessage(messageId1, null, group, author1,
|
||||
contentType, subject, timestamp, raw);
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Restricted group name",
|
||||
new byte[60]);
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
Message message2 = new TestMessage(messageId2, null, group1, author,
|
||||
contentType, subject, timestamp, raw);
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to an unrestricted group and store two messages
|
||||
// Subscribe to a group and store two messages
|
||||
db.addSubscription(txn, group);
|
||||
db.addGroupMessage(txn, message, false);
|
||||
db.addGroupMessage(txn, message1, false);
|
||||
|
||||
// Subscribe to a restricted group and store a message
|
||||
db.addSubscription(txn, group1);
|
||||
db.addGroupMessage(txn, message2, false);
|
||||
|
||||
// Check that only the messages in the unrestricted group are retrieved
|
||||
Collection<MessageId> ids = db.getUnrestrictedGroupMessages(txn,
|
||||
authorId);
|
||||
// Check that both messages are retrievable by their authors
|
||||
Collection<MessageId> ids = db.getGroupMessages(txn, authorId);
|
||||
Iterator<MessageId> it = ids.iterator();
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(messageId, it.next());
|
||||
assertFalse(it.hasNext());
|
||||
ids = db.getUnrestrictedGroupMessages(txn, authorId1);
|
||||
ids = db.getGroupMessages(txn, authorId1);
|
||||
it = ids.iterator();
|
||||
assertTrue(it.hasNext());
|
||||
assertEquals(messageId1, it.next());
|
||||
@@ -582,7 +574,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
MessageId childId2 = new MessageId(TestUtils.getRandomId());
|
||||
MessageId childId3 = new MessageId(TestUtils.getRandomId());
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Group name", null);
|
||||
Group group1 = new Group(groupId1, "Another group",
|
||||
new byte[GROUP_SALT_LENGTH]);
|
||||
Message child1 = new TestMessage(childId1, messageId, group, author,
|
||||
contentType, subject, timestamp, raw);
|
||||
Message child2 = new TestMessage(childId2, messageId, group, author,
|
||||
@@ -1193,7 +1186,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
public void testGetGroupMessageParentWithParentInAnotherGroup()
|
||||
throws Exception {
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Group name", null);
|
||||
Group group1 = new Group(groupId1, "Another group",
|
||||
new byte[GROUP_SALT_LENGTH]);
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
@@ -1446,7 +1440,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
// Subscribe to a couple of groups
|
||||
db.addSubscription(txn, group);
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Group name", null);
|
||||
Group group1 = new Group(groupId1, "Another group",
|
||||
new byte[GROUP_SALT_LENGTH]);
|
||||
db.addSubscription(txn, group1);
|
||||
|
||||
// Store two messages in the first group
|
||||
@@ -1499,7 +1494,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
List<Group> groups = new ArrayList<Group>();
|
||||
for(int i = 0; i < 100; i++) {
|
||||
GroupId id = new GroupId(TestUtils.getRandomId());
|
||||
groups.add(new Group(id, "Group name", null));
|
||||
String name = "Group " + i;
|
||||
groups.add(new Group(id, name, new byte[GROUP_SALT_LENGTH]));
|
||||
}
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
@@ -1834,7 +1830,8 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
public void testGetAvailableGroups() throws Exception {
|
||||
ContactId contactId1 = new ContactId(2);
|
||||
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||
Author author1 = new Author(authorId1, "Carol", new byte[60]);
|
||||
Author author1 = new Author(authorId1, "Carol",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
@@ -96,7 +96,7 @@ public class ConstantsTest extends BriarTestCase {
|
||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||
assertTrue(publicKey.length <= MAX_PUBLIC_KEY_LENGTH);
|
||||
// Sign some random data and check the length of the signature
|
||||
byte[] toBeSigned = new byte[1000];
|
||||
byte[] toBeSigned = new byte[1234];
|
||||
random.nextBytes(toBeSigned);
|
||||
sig.initSign(keyPair.getPrivate());
|
||||
sig.update(toBeSigned);
|
||||
@@ -120,23 +120,19 @@ public class ConstantsTest extends BriarTestCase {
|
||||
MessageId parent = new MessageId(TestUtils.getRandomId());
|
||||
// Create a maximum-length group
|
||||
String groupName = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Group group = groupFactory.createGroup(groupName, groupPublic);
|
||||
Group group = groupFactory.createGroup(groupName);
|
||||
// Create a maximum-length author
|
||||
String authorName =
|
||||
TestUtils.createRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Author author = authorFactory.createAuthor(authorName, authorPublic);
|
||||
// Create a maximum-length message
|
||||
PrivateKey groupPrivate =
|
||||
crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey authorPrivate =
|
||||
crypto.generateSignatureKeyPair().getPrivate();
|
||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||
String contentType =
|
||||
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
|
||||
byte[] body = new byte[MAX_BODY_LENGTH];
|
||||
Message message = messageFactory.createPseudonymousMessage(parent,
|
||||
group, groupPrivate, author, authorPrivate, contentType, body);
|
||||
group, author, privateKey, contentType, body);
|
||||
// Check the size of the serialised message
|
||||
int length = message.getSerialised().length;
|
||||
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
||||
@@ -181,10 +177,8 @@ public class ConstantsTest extends BriarTestCase {
|
||||
// Create the maximum number of maximum-length groups
|
||||
Collection<Group> subs = new ArrayList<Group>();
|
||||
for(int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
|
||||
String groupName =
|
||||
TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
subs.add(groupFactory.createGroup(groupName, groupPublic));
|
||||
String name = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
subs.add(groupFactory.createGroup(name));
|
||||
}
|
||||
// Create a maximum-length subscription update
|
||||
SubscriptionUpdate u = new SubscriptionUpdate(subs, Long.MAX_VALUE);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package net.sf.briar.messaging.simplex;
|
||||
|
||||
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -108,11 +109,12 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
// Add a local pseudonym for Alice
|
||||
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
|
||||
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
|
||||
new byte[60], new byte[60]);
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
|
||||
db.addLocalAuthor(aliceAuthor);
|
||||
// Add Bob as a contact
|
||||
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
|
||||
Author bobAuthor = new Author(bobId, "Bob", new byte[60]);
|
||||
Author bobAuthor = new Author(bobId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactId contactId = db.addContact(bobAuthor, aliceId);
|
||||
// Add the transport and the endpoint
|
||||
db.addTransport(transportId, LATENCY);
|
||||
@@ -161,12 +163,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
km.start();
|
||||
// Add a local pseudonym for Bob
|
||||
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
|
||||
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob", new byte[60],
|
||||
new byte[60]);
|
||||
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100]);
|
||||
db.addLocalAuthor(bobAuthor);
|
||||
// Add Alice as a contact
|
||||
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
|
||||
Author aliceAuthor = new Author(aliceId, "Alice", new byte[60]);
|
||||
Author aliceAuthor = new Author(aliceId, "Alice",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactId contactId = db.addContact(aliceAuthor, bobId);
|
||||
// Add the transport and the endpoint
|
||||
db.addTransport(transportId, LATENCY);
|
||||
|
||||
@@ -263,13 +263,13 @@ public class RemovableDrivePluginTest extends BriarTestCase {
|
||||
assertEquals(0, files[0].length());
|
||||
// Writing to the output stream should increase the size of the file
|
||||
OutputStream out = writer.getOutputStream();
|
||||
out.write(new byte[123]);
|
||||
out.write(new byte[1234]);
|
||||
out.flush();
|
||||
out.close();
|
||||
// Disposing of the writer should not delete the file
|
||||
writer.dispose(false);
|
||||
assertTrue(files[0].exists());
|
||||
assertEquals(123, files[0].length());
|
||||
assertEquals(1234, files[0].length());
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user