Removed peer moderation (may be restored after beta testing).

This commit is contained in:
akwizgran
2013-09-27 18:04:27 +01:00
parent b94954544d
commit 0a153acd02
16 changed files with 80 additions and 1020 deletions

View File

@@ -13,7 +13,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH; import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP; import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
import static net.sf.briar.api.messaging.Rating.GOOD;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -168,7 +167,6 @@ public class HomeScreenActivity extends RoboActivity {
lifecycleManager.waitForDatabase(); lifecycleManager.waitForDatabase();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
db.addLocalAuthor(a); db.addLocalAuthor(a);
db.setRating(a.getId(), GOOD);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Storing author took " + duration + " ms"); LOG.info("Storing author took " + duration + " ms");

View File

@@ -12,7 +12,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH; import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP; import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
import static net.sf.briar.api.messaging.Rating.GOOD;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -143,7 +142,6 @@ implements OnEditorActionListener, OnClickListener {
lifecycleManager.waitForDatabase(); lifecycleManager.waitForDatabase();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
db.addLocalAuthor(a); db.addLocalAuthor(a);
db.setRating(a.getId(), GOOD);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Storing author took " + duration + " ms"); LOG.info("Storing author took " + duration + " ms");

View File

@@ -20,7 +20,6 @@ import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Offer; import net.sf.briar.api.messaging.Offer;
import net.sf.briar.api.messaging.Rating;
import net.sf.briar.api.messaging.Request; import net.sf.briar.api.messaging.Request;
import net.sf.briar.api.messaging.RetentionAck; import net.sf.briar.api.messaging.RetentionAck;
import net.sf.briar.api.messaging.RetentionUpdate; import net.sf.briar.api.messaging.RetentionUpdate;
@@ -204,9 +203,6 @@ public interface DatabaseComponent {
Collection<PrivateMessageHeader> getPrivateMessageHeaders(ContactId c) Collection<PrivateMessageHeader> getPrivateMessageHeaders(ContactId c)
throws DbException; throws DbException;
/** Returns the user's rating for the given author. */
Rating getRating(AuthorId a) throws DbException;
/** Returns true if the given message has been read. */ /** Returns true if the given message has been read. */
boolean getReadFlag(MessageId m) throws DbException; boolean getReadFlag(MessageId m) throws DbException;
@@ -313,9 +309,6 @@ public interface DatabaseComponent {
void setConnectionWindow(ContactId c, TransportId t, long period, void setConnectionWindow(ContactId c, TransportId t, long period,
long centre, byte[] bitmap) throws DbException; long centre, byte[] bitmap) throws DbException;
/** Records the user's rating for the given author. */
void setRating(AuthorId a, Rating r) throws DbException;
/** /**
* Marks the given message read or unread and returns true if it was * Marks the given message read or unread and returns true if it was
* previously read. * previously read.

View File

@@ -3,7 +3,6 @@ package net.sf.briar.api.db;
import net.sf.briar.api.Author; import net.sf.briar.api.Author;
import net.sf.briar.api.messaging.GroupId; import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
public class GroupMessageHeader extends MessageHeader { public class GroupMessageHeader extends MessageHeader {
@@ -11,9 +10,9 @@ public class GroupMessageHeader extends MessageHeader {
public GroupMessageHeader(MessageId id, MessageId parent, Author author, public GroupMessageHeader(MessageId id, MessageId parent, Author author,
String contentType, String subject, long timestamp, boolean read, String contentType, String subject, long timestamp, boolean read,
boolean starred, Rating rating, GroupId groupId) { boolean starred, GroupId groupId) {
super(id, parent, author, contentType, subject, timestamp, read, super(id, parent, author, contentType, subject, timestamp, read,
starred, rating); starred);
this.groupId = groupId; this.groupId = groupId;
} }

View File

@@ -2,7 +2,6 @@ package net.sf.briar.api.db;
import net.sf.briar.api.Author; import net.sf.briar.api.Author;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
public abstract class MessageHeader { public abstract class MessageHeader {
@@ -11,11 +10,10 @@ public abstract class MessageHeader {
private final String contentType, subject; private final String contentType, subject;
private final long timestamp; private final long timestamp;
private final boolean read, starred; private final boolean read, starred;
private final Rating rating;
protected MessageHeader(MessageId id, MessageId parent, Author author, protected MessageHeader(MessageId id, MessageId parent, Author author,
String contentType, String subject, long timestamp, boolean read, String contentType, String subject, long timestamp, boolean read,
boolean starred, Rating rating) { boolean starred) {
this.id = id; this.id = id;
this.parent = parent; this.parent = parent;
this.author = author; this.author = author;
@@ -24,7 +22,6 @@ public abstract class MessageHeader {
this.timestamp = timestamp; this.timestamp = timestamp;
this.read = read; this.read = read;
this.starred = starred; this.starred = starred;
this.rating = rating;
} }
/** Returns the message's unique identifier. */ /** Returns the message's unique identifier. */
@@ -71,12 +68,4 @@ public abstract class MessageHeader {
public boolean isStarred() { public boolean isStarred() {
return starred; return starred;
} }
/**
* Returns the rating for the message's author, or Rating.UNRATED if this
* is an anonymous message.
*/
public Rating getRating() {
return rating;
}
} }

View File

@@ -3,7 +3,6 @@ package net.sf.briar.api.db;
import net.sf.briar.api.Author; import net.sf.briar.api.Author;
import net.sf.briar.api.ContactId; import net.sf.briar.api.ContactId;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
public class PrivateMessageHeader extends MessageHeader { public class PrivateMessageHeader extends MessageHeader {
@@ -12,10 +11,9 @@ public class PrivateMessageHeader extends MessageHeader {
public PrivateMessageHeader(MessageId id, MessageId parent, Author author, public PrivateMessageHeader(MessageId id, MessageId parent, Author author,
String contentType, String subject, long timestamp, boolean read, String contentType, String subject, long timestamp, boolean read,
boolean starred, Rating rating, ContactId contactId, boolean starred, ContactId contactId, boolean incoming) {
boolean incoming) {
super(id, parent, author, contentType, subject, timestamp, read, super(id, parent, author, contentType, subject, timestamp, read,
starred, rating); starred);
this.contactId = contactId; this.contactId = contactId;
this.incoming = incoming; this.incoming = incoming;
} }

View File

@@ -1,23 +0,0 @@
package net.sf.briar.api.db.event;
import net.sf.briar.api.AuthorId;
import net.sf.briar.api.messaging.Rating;
public class RatingChangedEvent extends DatabaseEvent {
private final AuthorId author;
private final Rating rating;
public RatingChangedEvent(AuthorId author, Rating rating) {
this.author = author;
this.rating = rating;
}
public AuthorId getAuthorId() {
return author;
}
public Rating getRating() {
return rating;
}
}

View File

@@ -1,6 +0,0 @@
package net.sf.briar.api.messaging;
/** The ratings that may be applied to an author in peer moderation. */
public enum Rating {
UNRATED, BAD, GOOD
}

View File

@@ -20,7 +20,6 @@ import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus; import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
import net.sf.briar.api.messaging.RetentionAck; import net.sf.briar.api.messaging.RetentionAck;
import net.sf.briar.api.messaging.RetentionUpdate; import net.sf.briar.api.messaging.RetentionUpdate;
import net.sf.briar.api.messaging.SubscriptionAck; import net.sf.briar.api.messaging.SubscriptionAck;
@@ -45,7 +44,6 @@ import net.sf.briar.api.transport.TemporarySecret;
* <li> contact * <li> contact
* <li> identity * <li> identity
* <li> message * <li> message
* <li> rating
* <li> retention * <li> retention
* <li> subscription * <li> subscription
* <li> transport * <li> transport
@@ -270,7 +268,7 @@ interface Database<T> {
/** /**
* Returns the headers of all messages in the given group. * Returns the headers of all messages in the given group.
* <p> * <p>
* Locking: message read, rating read. * Locking: message read.
*/ */
Collection<GroupMessageHeader> getGroupMessageHeaders(T txn, GroupId g) Collection<GroupMessageHeader> getGroupMessageHeaders(T txn, GroupId g)
throws DbException; throws DbException;
@@ -341,7 +339,7 @@ interface Database<T> {
* Returns the headers of all private messages to or from the given * Returns the headers of all private messages to or from the given
* contact. * contact.
* <p> * <p>
* Locking: contact read, identity read, message read, rating read. * Locking: contact read, identity read, message read.
*/ */
Collection<PrivateMessageHeader> getPrivateMessageHeaders(T txn, Collection<PrivateMessageHeader> getPrivateMessageHeaders(T txn,
ContactId c) throws DbException; ContactId c) throws DbException;
@@ -364,15 +362,6 @@ interface Database<T> {
Collection<MessageId> getMessagesToOffer(T txn, ContactId c, Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
int maxMessages) throws DbException; int maxMessages) throws DbException;
/**
* Returns the number of children of the message identified by the given
* ID that are present in the database and have sendability scores greater
* than zero.
* <p>
* Locking: message read.
*/
int getNumberOfSendableChildren(T txn, MessageId m) throws DbException;
/** /**
* Returns the message identified by the given ID, in serialised form. * Returns the message identified by the given ID, in serialised form.
* <p> * <p>
@@ -398,13 +387,6 @@ interface Database<T> {
*/ */
Collection<MessageId> getOldMessages(T txn, int size) throws DbException; Collection<MessageId> getOldMessages(T txn, int size) throws DbException;
/**
* Returns the user's rating for the given author.
* <p>
* Locking: rating read.
*/
Rating getRating(T txn, AuthorId a) throws DbException;
/** /**
* Returns true if the given message has been read. * Returns true if the given message has been read.
* <p> * <p>
@@ -443,13 +425,6 @@ interface Database<T> {
*/ */
Collection<TemporarySecret> getSecrets(T txn) throws DbException; Collection<TemporarySecret> getSecrets(T txn) throws DbException;
/**
* Returns the sendability score of the given group message.
* <p>
* Locking: message read.
*/
int getSendability(T txn, MessageId m) throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact, with a total length less than or equal to the given * given contact, with a total length less than or equal to the given
@@ -669,13 +644,6 @@ interface Database<T> {
*/ */
void setLastConnected(T txn, ContactId c, long now) throws DbException; void setLastConnected(T txn, ContactId c, long now) throws DbException;
/**
* Sets the user's rating for the given author.
* <p>
* Locking: rating write.
*/
Rating setRating(T txn, AuthorId a, Rating r) throws DbException;
/** /**
* Marks the given message read or unread and returns true if it was * Marks the given message read or unread and returns true if it was
* previously read. * previously read.
@@ -714,13 +682,6 @@ interface Database<T> {
boolean setRetentionTime(T txn, ContactId c, long retention, long version) boolean setRetentionTime(T txn, ContactId c, long retention, long version)
throws DbException; throws DbException;
/**
* Sets the sendability score of the given message.
* <p>
* Locking: message write.
*/
void setSendability(T txn, MessageId m, int sendability) throws DbException;
/** /**
* Marks the given message starred or unstarred and returns true if it was * Marks the given message starred or unstarred and returns true if it was
* previously starred. * previously starred.

View File

@@ -2,7 +2,6 @@ package net.sf.briar.db;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static net.sf.briar.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP; import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP;
import static net.sf.briar.db.DatabaseConstants.CRITICAL_FREE_SPACE; import static net.sf.briar.db.DatabaseConstants.CRITICAL_FREE_SPACE;
@@ -52,7 +51,6 @@ import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent; import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent; import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent; import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
@@ -68,7 +66,6 @@ import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Offer; import net.sf.briar.api.messaging.Offer;
import net.sf.briar.api.messaging.Rating;
import net.sf.briar.api.messaging.Request; import net.sf.briar.api.messaging.Request;
import net.sf.briar.api.messaging.RetentionAck; import net.sf.briar.api.messaging.RetentionAck;
import net.sf.briar.api.messaging.RetentionUpdate; import net.sf.briar.api.messaging.RetentionUpdate;
@@ -105,8 +102,6 @@ DatabaseCleaner.Callback {
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock messageLock = private final ReentrantReadWriteLock messageLock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock ratingLock =
new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock retentionLock = private final ReentrantReadWriteLock retentionLock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final ReentrantReadWriteLock subscriptionLock = private final ReentrantReadWriteLock subscriptionLock =
@@ -290,27 +285,22 @@ DatabaseCleaner.Callback {
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
ratingLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
subscriptionLock.readLock().lock(); T txn = db.startTransaction();
try { try {
T txn = db.startTransaction(); // Don't store the message if the user has
try { // unsubscribed from the group
// Don't store the message if the user has GroupId g = m.getGroup().getId();
// unsubscribed from the group if(db.containsSubscription(txn, g))
GroupId g = m.getGroup().getId(); added = storeGroupMessage(txn, m, null);
if(db.containsSubscription(txn, g)) db.commitTransaction(txn);
added = storeGroupMessage(txn, m, null); } catch(DbException e) {
db.commitTransaction(txn); db.abortTransaction(txn);
} catch(DbException e) { throw e;
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
} }
} finally { } finally {
ratingLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
@@ -324,11 +314,10 @@ DatabaseCleaner.Callback {
/** /**
* If the given message is already in the database, marks it as seen by the * If the given message is already in the database, marks it as seen by the
* sender and returns false. Otherwise stores the message, updates the * sender and returns false. Otherwise stores the message, marks it as seen
* sendability of its ancestors if necessary, marks the message as seen by * by the sender and unseen by all other contacts, and returns true.
* the sender and unseen by all other contacts, and returns true.
* <p> * <p>
* Locking: contact read, message write, rating read. * Locking: contact read, message write.
* @param sender is null for a locally generated message. * @param sender is null for a locally generated message.
*/ */
private boolean storeGroupMessage(T txn, Message m, ContactId sender) private boolean storeGroupMessage(T txn, Message m, ContactId sender)
@@ -341,13 +330,8 @@ DatabaseCleaner.Callback {
if(sender != null) db.addStatus(txn, sender, id, true); if(sender != null) db.addStatus(txn, sender, id, true);
if(stored) { if(stored) {
// Mark the message as unseen by other contacts // Mark the message as unseen by other contacts
for(ContactId c : db.getContactIds(txn)) { for(ContactId c : db.getContactIds(txn))
if(!c.equals(sender)) db.addStatus(txn, c, id, false); if(!c.equals(sender)) db.addStatus(txn, c, id, false);
}
// Calculate and store the message's sendability
int sendability = calculateSendability(txn, m);
db.setSendability(txn, id, sendability);
if(sendability > 0) updateAncestorSendability(txn, id, true);
// Count the bytes stored // Count the bytes stored
synchronized(spaceLock) { synchronized(spaceLock) {
bytesStoredSinceLastCheck += m.getSerialised().length; bytesStoredSinceLastCheck += m.getSerialised().length;
@@ -359,59 +343,6 @@ DatabaseCleaner.Callback {
return stored; return stored;
} }
/**
* Calculates and returns the sendability score of a message.
* <p>
* Locking: message read, rating read.
*/
private int calculateSendability(T txn, Message m) throws DbException {
int sendability = 0;
// One point for a good rating
Author a = m.getAuthor();
if(a != null && db.getRating(txn, a.getId()) == GOOD) sendability++;
// One point per sendable child (backward inclusion)
sendability += db.getNumberOfSendableChildren(txn, m.getId());
return sendability;
}
/**
* Iteratively updates the sendability of a message's ancestors to reflect
* a change in the message's sendability. Returns the number of ancestors
* that have changed from sendable to not sendable, or vice versa.
* <p>
* Locking: message write.
* @param increment true if the message's sendability has changed from 0 to
* greater than 0, or false if it has changed from greater than 0 to 0.
*/
private int updateAncestorSendability(T txn, MessageId m, boolean increment)
throws DbException {
int affected = 0;
boolean changed = true;
while(changed) {
// Stop if the message has no parent, or the parent isn't in the
// database, or the parent belongs to a different group
MessageId parent = db.getGroupMessageParent(txn, m);
if(parent == null) break;
// Increment or decrement the parent's sendability
int parentSendability = db.getSendability(txn, parent);
if(increment) {
parentSendability++;
changed = parentSendability == 1;
if(changed) affected++;
} else {
assert parentSendability > 0;
parentSendability--;
changed = parentSendability == 0;
if(changed) affected++;
}
db.setSendability(txn, parent, parentSendability);
// Move on to the parent's parent
m = parent;
}
return affected;
}
public void addLocalPrivateMessage(Message m, ContactId c) public void addLocalPrivateMessage(Message m, ContactId c)
throws DbException { throws DbException {
boolean added; boolean added;
@@ -967,27 +898,22 @@ DatabaseCleaner.Callback {
throws DbException { throws DbException {
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
ratingLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
subscriptionLock.readLock().lock(); T txn = db.startTransaction();
try { try {
T txn = db.startTransaction(); if(!db.containsSubscription(txn, g))
try { throw new NoSuchSubscriptionException();
if(!db.containsSubscription(txn, g)) Collection<GroupMessageHeader> headers =
throw new NoSuchSubscriptionException(); db.getGroupMessageHeaders(txn, g);
Collection<GroupMessageHeader> headers = db.commitTransaction(txn);
db.getGroupMessageHeaders(txn, g); return headers;
db.commitTransaction(txn); } catch(DbException e) {
return headers; db.abortTransaction(txn);
} catch(DbException e) { throw e;
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
} }
} finally { } finally {
ratingLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
@@ -1111,20 +1037,15 @@ DatabaseCleaner.Callback {
try { try {
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
ratingLock.readLock().lock(); T txn = db.startTransaction();
try { try {
T txn = db.startTransaction(); Collection<PrivateMessageHeader> headers =
try { db.getPrivateMessageHeaders(txn, c);
Collection<PrivateMessageHeader> headers = db.commitTransaction(txn);
db.getPrivateMessageHeaders(txn, c); return headers;
db.commitTransaction(txn); } catch(DbException e) {
return headers; db.abortTransaction(txn);
} catch(DbException e) { throw e;
db.abortTransaction(txn);
throw e;
}
} finally {
ratingLock.readLock().unlock();
} }
} finally { } finally {
messageLock.readLock().unlock(); messageLock.readLock().unlock();
@@ -1137,23 +1058,6 @@ DatabaseCleaner.Callback {
} }
} }
public Rating getRating(AuthorId a) throws DbException {
ratingLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
Rating r = db.getRating(txn, a);
db.commitTransaction(txn);
return r;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
ratingLock.readLock().unlock();
}
}
public boolean getReadFlag(MessageId m) throws DbException { public boolean getReadFlag(MessageId m) throws DbException {
messageLock.readLock().lock(); messageLock.readLock().lock();
try { try {
@@ -1463,26 +1367,21 @@ DatabaseCleaner.Callback {
try { try {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
ratingLock.readLock().lock(); subscriptionLock.readLock().lock();
try { try {
subscriptionLock.readLock().lock(); T txn = db.startTransaction();
try { try {
T txn = db.startTransaction(); if(!db.containsContact(txn, c))
try { throw new NoSuchContactException();
if(!db.containsContact(txn, c)) added = storeMessage(txn, c, m);
throw new NoSuchContactException(); db.addMessageToAck(txn, c, m.getId());
added = storeMessage(txn, c, m); db.commitTransaction(txn);
db.addMessageToAck(txn, c, m.getId()); } catch(DbException e) {
db.commitTransaction(txn); db.abortTransaction(txn);
} catch(DbException e) { throw e;
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
} }
} finally { } finally {
ratingLock.readLock().unlock(); subscriptionLock.readLock().unlock();
} }
} finally { } finally {
messageLock.writeLock().unlock(); messageLock.writeLock().unlock();
@@ -1502,7 +1401,7 @@ DatabaseCleaner.Callback {
* Attempts to store a message received from the given contact, and returns * Attempts to store a message received from the given contact, and returns
* true if it was stored. * true if it was stored.
* <p> * <p>
* Locking: contact read, message write, rating read, subscription read. * Locking: contact read, message write, subscription read.
*/ */
private boolean storeMessage(T txn, ContactId c, Message m) private boolean storeMessage(T txn, ContactId c, Message m)
throws DbException { throws DbException {
@@ -1823,35 +1722,6 @@ DatabaseCleaner.Callback {
} }
} }
public void setRating(AuthorId a, Rating r) throws DbException {
boolean changed;
messageLock.writeLock().lock();
try {
ratingLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
Rating old = db.setRating(txn, a, r);
changed = (old != r);
// Update the sendability of the author's messages
if(r == GOOD && old != GOOD)
updateAuthorSendability(txn, a, true);
else if(r != GOOD && old == GOOD)
updateAuthorSendability(txn, a, false);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
ratingLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
if(changed) callListeners(new RatingChangedEvent(a, r));
}
public boolean setReadFlag(MessageId m, boolean read) throws DbException { public boolean setReadFlag(MessageId m, boolean read) throws DbException {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
try { try {
@@ -1925,31 +1795,6 @@ DatabaseCleaner.Callback {
} }
} }
/**
* 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
* from not good to good, or false if it has changed from good to not good.
*/
private void updateAuthorSendability(T txn, AuthorId a, boolean increment)
throws DbException {
for(MessageId id : db.getGroupMessages(txn, a)) {
int sendability = db.getSendability(txn, id);
if(increment) {
db.setSendability(txn, id, sendability + 1);
if(sendability == 0)
updateAncestorSendability(txn, id, true);
} else {
assert sendability > 0;
db.setSendability(txn, id, sendability - 1);
if(sendability == 1)
updateAncestorSendability(txn, id, false);
}
}
}
public boolean setStarredFlag(MessageId m, boolean starred) public boolean setStarredFlag(MessageId m, boolean starred)
throws DbException { throws DbException {
messageLock.writeLock().lock(); messageLock.writeLock().lock();
@@ -2134,7 +1979,7 @@ DatabaseCleaner.Callback {
try { try {
expired = db.getOldMessages(txn, size); expired = db.getOldMessages(txn, size);
if(!expired.isEmpty()) { if(!expired.isEmpty()) {
for(MessageId m : expired) removeMessage(txn, m); for(MessageId m : expired) db.removeMessage(txn, m);
db.incrementRetentionVersions(txn); db.incrementRetentionVersions(txn);
if(LOG.isLoggable(INFO)) if(LOG.isLoggable(INFO))
LOG.info("Expired " + expired.size() + " messages"); LOG.info("Expired " + expired.size() + " messages");
@@ -2155,19 +2000,6 @@ DatabaseCleaner.Callback {
return true; return true;
} }
/**
* Removes the given message (and all associated state) from the database.
* <p>
* Locking: message write.
*/
private void removeMessage(T txn, MessageId m) throws DbException {
int sendability = db.getSendability(txn, m);
// If the message is sendable, deleting it may affect its ancestors'
// sendability (backward inclusion)
if(sendability > 0) updateAncestorSendability(txn, m, false);
db.removeMessage(txn, m);
}
public boolean shouldCheckFreeSpace() { public boolean shouldCheckFreeSpace() {
synchronized(spaceLock) { synchronized(spaceLock) {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();

View File

@@ -6,7 +6,6 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS; import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS;
import static net.sf.briar.api.messaging.MessagingConstants.RETENTION_MODULUS; import static net.sf.briar.api.messaging.MessagingConstants.RETENTION_MODULUS;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import static net.sf.briar.db.ExponentialBackoff.calculateExpiry; import static net.sf.briar.db.ExponentialBackoff.calculateExpiry;
import java.io.IOException; import java.io.IOException;
@@ -45,7 +44,6 @@ import net.sf.briar.api.messaging.GroupId;
import net.sf.briar.api.messaging.GroupStatus; import net.sf.briar.api.messaging.GroupStatus;
import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.Message;
import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageId;
import net.sf.briar.api.messaging.Rating;
import net.sf.briar.api.messaging.RetentionAck; import net.sf.briar.api.messaging.RetentionAck;
import net.sf.briar.api.messaging.RetentionUpdate; import net.sf.briar.api.messaging.RetentionUpdate;
import net.sf.briar.api.messaging.SubscriptionAck; import net.sf.briar.api.messaging.SubscriptionAck;
@@ -154,7 +152,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " bodyLength INT NOT NULL," + " bodyLength INT NOT NULL,"
+ " raw BLOB NOT NULL," + " raw BLOB NOT NULL,"
+ " incoming BOOLEAN NOT NULL," + " incoming BOOLEAN NOT NULL,"
+ " sendability INT UNSIGNED," // Null for private messages
+ " contactId INT UNSIGNED," // Null for group messages + " contactId INT UNSIGNED," // Null for group messages
+ " read BOOLEAN NOT NULL," + " read BOOLEAN NOT NULL,"
+ " starred BOOLEAN NOT NULL," + " starred BOOLEAN NOT NULL,"
@@ -166,18 +163,12 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES contacts (contactId)" + " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String INDEX_MESSAGES_BY_PARENT =
"CREATE INDEX messagesByParent ON messages (parentId)";
private static final String INDEX_MESSAGES_BY_AUTHOR = private static final String INDEX_MESSAGES_BY_AUTHOR =
"CREATE INDEX messagesByAuthor ON messages (authorId)"; "CREATE INDEX messagesByAuthor ON messages (authorId)";
private static final String INDEX_MESSAGES_BY_TIMESTAMP = private static final String INDEX_MESSAGES_BY_TIMESTAMP =
"CREATE INDEX messagesByTimestamp ON messages (timestamp)"; "CREATE INDEX messagesByTimestamp ON messages (timestamp)";
private static final String INDEX_MESSAGES_BY_SENDABILITY =
"CREATE INDEX messagesBySendability ON messages (sendability)";
// Locking: message // Locking: message
private static final String CREATE_MESSAGES_TO_ACK = private static final String CREATE_MESSAGES_TO_ACK =
"CREATE TABLE messagesToAck" "CREATE TABLE messagesToAck"
@@ -210,13 +201,6 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String INDEX_STATUSES_BY_CONTACT = private static final String INDEX_STATUSES_BY_CONTACT =
"CREATE INDEX statusesByContact ON statuses (contactId)"; "CREATE INDEX statusesByContact ON statuses (contactId)";
// Locking: rating
private static final String CREATE_RATINGS =
"CREATE TABLE ratings"
+ " (authorId HASH NOT NULL,"
+ " rating SMALLINT NOT NULL,"
+ " PRIMARY KEY (authorId))";
// Locking: retention // Locking: retention
private static final String CREATE_RETENTION_VERSIONS = private static final String CREATE_RETENTION_VERSIONS =
"CREATE TABLE retentionVersions" "CREATE TABLE retentionVersions"
@@ -403,15 +387,12 @@ abstract class JdbcDatabase implements Database<Connection> {
s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS)); s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS));
s.executeUpdate(insertTypeNames(CREATE_MESSAGES)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
s.executeUpdate(INDEX_MESSAGES_BY_PARENT);
s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR); s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR);
s.executeUpdate(INDEX_MESSAGES_BY_TIMESTAMP); s.executeUpdate(INDEX_MESSAGES_BY_TIMESTAMP);
s.executeUpdate(INDEX_MESSAGES_BY_SENDABILITY);
s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK)); s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK));
s.executeUpdate(insertTypeNames(CREATE_STATUSES)); s.executeUpdate(insertTypeNames(CREATE_STATUSES));
s.executeUpdate(INDEX_STATUSES_BY_MESSAGE); s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
s.executeUpdate(INDEX_STATUSES_BY_CONTACT); s.executeUpdate(INDEX_STATUSES_BY_CONTACT);
s.executeUpdate(insertTypeNames(CREATE_RATINGS));
s.executeUpdate(insertTypeNames(CREATE_RETENTION_VERSIONS)); s.executeUpdate(insertTypeNames(CREATE_RETENTION_VERSIONS));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS)); s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS));
@@ -681,8 +662,8 @@ abstract class JdbcDatabase implements Database<Connection> {
String sql = "INSERT INTO messages (messageId, parentId, groupId," String sql = "INSERT INTO messages (messageId, parentId, groupId,"
+ " authorId, authorName, authorKey, contentType, subject," + " authorId, authorName, authorKey, contentType, subject,"
+ " timestamp, length, bodyStart, bodyLength, raw," + " timestamp, length, bodyStart, bodyLength, raw,"
+ " incoming, sendability, read, starred)" + " incoming, read, starred)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0," + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
+ " FALSE, FALSE)"; + " FALSE, FALSE)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getId().getBytes()); ps.setBytes(1, m.getId().getBytes());
@@ -1307,12 +1288,10 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT messageId, parentId, m.authorId, authorName," String sql = "SELECT messageId, parentId, authorId, authorName,"
+ " authorKey, rating, contentType, subject, timestamp," + " authorKey, contentType, subject, timestamp, read,"
+ " read, starred" + " starred"
+ " FROM messages AS m" + " FROM messages"
+ " LEFT OUTER JOIN ratings AS r"
+ " ON m.authorId = r.authorId"
+ " WHERE groupId = ?"; + " WHERE groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
@@ -1324,27 +1303,22 @@ abstract class JdbcDatabase implements Database<Connection> {
byte[] b = rs.getBytes(2); byte[] b = rs.getBytes(2);
MessageId parent = b == null ? null : new MessageId(b); MessageId parent = b == null ? null : new MessageId(b);
Author author; Author author;
Rating rating;
b = rs.getBytes(3); b = rs.getBytes(3);
if(b == null) { if(b == null) {
author = null; author = null;
rating = UNRATED;
} else { } else {
AuthorId authorId = new AuthorId(b); AuthorId authorId = new AuthorId(b);
String authorName = rs.getString(4); String authorName = rs.getString(4);
byte[] authorKey = rs.getBytes(5); byte[] authorKey = rs.getBytes(5);
author = new Author(authorId, authorName, authorKey); author = new Author(authorId, authorName, authorKey);
// NULL == 0 == UNRATED
rating = Rating.values()[rs.getByte(6)];
} }
String contentType = rs.getString(7); String contentType = rs.getString(6);
String subject = rs.getString(8); String subject = rs.getString(7);
long timestamp = rs.getLong(9); long timestamp = rs.getLong(8);
boolean read = rs.getBoolean(10); boolean read = rs.getBoolean(9);
boolean starred = rs.getBoolean(11); boolean starred = rs.getBoolean(10);
headers.add(new GroupMessageHeader(id, parent, author, headers.add(new GroupMessageHeader(id, parent, author,
contentType, subject, timestamp, read, starred, rating, contentType, subject, timestamp, read, starred, g));
g));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1626,7 +1600,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " WHERE cg.contactId = ?" + " WHERE cg.contactId = ?"
+ " AND timestamp >= retention" + " AND timestamp >= retention"
+ " AND seen = FALSE AND s.expiry < ?" + " AND seen = FALSE AND s.expiry < ?"
+ " AND sendability > 0"
+ " ORDER BY timestamp DESC LIMIT ?"; + " ORDER BY timestamp DESC LIMIT ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -1644,41 +1617,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public int getNumberOfSendableChildren(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
// Children in other groups should not be counted
String sql = "SELECT groupId FROM messages WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
byte[] groupId = rs.getBytes(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
sql = "SELECT COUNT (messageId) FROM messages"
+ " WHERE parentId = ? AND groupId = ?"
+ " AND sendability > 0";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
ps.setBytes(2, groupId);
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
int count = rs.getInt(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return count;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getOldMessages(Connection txn, int capacity) public Collection<MessageId> getOldMessages(Connection txn, int capacity)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1713,13 +1651,10 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
// Get the incoming message headers // Get the incoming message headers
String sql = "SELECT m.messageId, parentId, contentType, subject," String sql = "SELECT m.messageId, parentId, contentType, subject,"
+ " timestamp, read, starred, c.authorId, name, publicKey," + " timestamp, read, starred, c.authorId, name, publicKey"
+ " rating"
+ " FROM messages AS m" + " FROM messages AS m"
+ " JOIN contacts AS c" + " JOIN contacts AS c"
+ " ON m.contactId = c.contactId" + " ON m.contactId = c.contactId"
+ " LEFT OUTER JOIN ratings AS r"
+ " ON c.authorId = r.authorId"
+ " WHERE m.contactId = ?" + " WHERE m.contactId = ?"
+ " AND groupId IS NULL" + " AND groupId IS NULL"
+ " AND incoming = TRUE"; + " AND incoming = TRUE";
@@ -1741,25 +1676,21 @@ abstract class JdbcDatabase implements Database<Connection> {
String authorName = rs.getString(9); String authorName = rs.getString(9);
byte[] authorKey = rs.getBytes(10); byte[] authorKey = rs.getBytes(10);
Author author = new Author(authorId, authorName, authorKey); Author author = new Author(authorId, authorName, authorKey);
// NULL == 0 == UNRATED
Rating rating = Rating.values()[rs.getByte(11)];
headers.add(new PrivateMessageHeader(id, parent, author, headers.add(new PrivateMessageHeader(id, parent, author,
contentType, subject, timestamp, read, starred, rating, contentType, subject, timestamp, read, starred, c,
c, true)); true));
} }
rs.close(); rs.close();
ps.close(); ps.close();
// Get the outgoing message headers // Get the outgoing message headers
sql = "SELECT m.messageId, parentId, contentType, subject," sql = "SELECT m.messageId, parentId, contentType, subject,"
+ " timestamp, read, starred, a.authorId, a.name," + " timestamp, read, starred, a.authorId, a.name,"
+ " a.publicKey, rating" + " a.publicKey"
+ " FROM messages AS m" + " FROM messages AS m"
+ " JOIN contacts AS c" + " JOIN contacts AS c"
+ " ON m.contactId = c.contactId" + " ON m.contactId = c.contactId"
+ " JOIN localAuthors AS a" + " JOIN localAuthors AS a"
+ " ON c.localAuthorId = a.authorId" + " ON c.localAuthorId = a.authorId"
+ " LEFT OUTER JOIN ratings AS r"
+ " ON c.localAuthorId = r.authorId"
+ " WHERE m.contactId = ?" + " WHERE m.contactId = ?"
+ " AND groupId IS NULL" + " AND groupId IS NULL"
+ " AND incoming = FALSE"; + " AND incoming = FALSE";
@@ -1779,11 +1710,9 @@ abstract class JdbcDatabase implements Database<Connection> {
String authorName = rs.getString(9); String authorName = rs.getString(9);
byte[] authorKey = rs.getBytes(10); byte[] authorKey = rs.getBytes(10);
Author author = new Author(authorId, authorName, authorKey); Author author = new Author(authorId, authorName, authorKey);
// NULL == 0 == UNRATED
Rating rating = Rating.values()[rs.getByte(11)];
headers.add(new PrivateMessageHeader(id, parent, author, headers.add(new PrivateMessageHeader(id, parent, author,
contentType, subject, timestamp, read, starred, rating, contentType, subject, timestamp, read, starred, c,
c, false)); false));
} }
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1795,28 +1724,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Rating getRating(Connection txn, AuthorId a) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT rating FROM ratings WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
Rating r;
if(rs.next()) r = Rating.values()[rs.getByte(1)];
else r = UNRATED;
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return r;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public byte[] getRawMessage(Connection txn, MessageId m) public byte[] getRawMessage(Connection txn, MessageId m)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -1883,8 +1790,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " WHERE m.messageId = ?" + " WHERE m.messageId = ?"
+ " AND cg.contactId = ?" + " AND cg.contactId = ?"
+ " AND timestamp >= retention" + " AND timestamp >= retention"
+ " AND seen = FALSE AND s.expiry < ?" + " AND seen = FALSE AND s.expiry < ?";
+ " AND sendability > 0";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
@@ -2080,27 +1986,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public int getSendability(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT sendability FROM messages WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes());
rs = ps.executeQuery();
if(!rs.next()) throw new DbStateException();
int sendability = rs.getInt(1);
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return sendability;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<MessageId> getSendableMessages(Connection txn, public Collection<MessageId> getSendableMessages(Connection txn,
ContactId c, int maxLength) throws DbException { ContactId c, int maxLength) throws DbException {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
@@ -2143,7 +2028,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " WHERE cg.contactId = ?" + " WHERE cg.contactId = ?"
+ " AND timestamp >= retention" + " AND timestamp >= retention"
+ " AND seen = FALSE AND s.expiry < ?" + " AND seen = FALSE AND s.expiry < ?"
+ " AND sendability > 0"
+ " ORDER BY timestamp DESC"; + " ORDER BY timestamp DESC";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -2587,7 +2471,6 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " WHERE cg.contactId = ?" + " WHERE cg.contactId = ?"
+ " AND timestamp >= retention" + " AND timestamp >= retention"
+ " AND seen = FALSE AND s.expiry < ?" + " AND seen = FALSE AND s.expiry < ?"
+ " AND sendability > 0"
+ " LIMIT 1"; + " LIMIT 1";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -2940,53 +2823,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public Rating setRating(Connection txn, AuthorId a, Rating r)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT rating FROM ratings WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
Rating old = UNRATED;
boolean exists = false;
if(rs.next()) {
old = Rating.values()[rs.getByte(1)];
exists = true;
}
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
if(old == r) return old;
if(exists) {
// A rating row exists - update it
sql = "UPDATE ratings SET rating = ? WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setByte(1, (byte) r.ordinal());
ps.setBytes(2, a.getBytes());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} else {
// No rating row exists - create one
sql = "INSERT INTO ratings (authorId, rating)"
+ " VALUES (?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
ps.setByte(2, (byte) r.ordinal());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
}
return old;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public boolean setReadFlag(Connection txn, MessageId m, boolean read) public boolean setReadFlag(Connection txn, MessageId m, boolean read)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;
@@ -3181,24 +3017,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
public void setSendability(Connection txn, MessageId m, int sendability)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE messages SET sendability = ?"
+ " WHERE messageId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, sendability);
ps.setBytes(2, m.getBytes());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public boolean setStarredFlag(Connection txn, MessageId m, boolean starred) public boolean setStarredFlag(Connection txn, MessageId m, boolean starred)
throws DbException { throws DbException {
PreparedStatement ps = null; PreparedStatement ps = null;

View File

@@ -9,7 +9,6 @@ import static net.sf.briar.api.TransportPropertyConstants.MAX_PROPERTIES_PER_TRA
import static net.sf.briar.api.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static net.sf.briar.api.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
import static net.sf.briar.api.invitation.InvitationConstants.CONNECTION_TIMEOUT; import static net.sf.briar.api.invitation.InvitationConstants.CONNECTION_TIMEOUT;
import static net.sf.briar.api.invitation.InvitationConstants.HASH_LENGTH; import static net.sf.briar.api.invitation.InvitationConstants.HASH_LENGTH;
import static net.sf.briar.api.messaging.Rating.GOOD;
import java.io.IOException; import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@@ -263,8 +262,6 @@ abstract class Connector extends Thread {
long epoch, boolean alice) throws DbException { long epoch, boolean alice) throws DbException {
// Add the contact to the database // Add the contact to the database
contactId = db.addContact(remoteAuthor, localAuthor.getId()); contactId = db.addContact(remoteAuthor, localAuthor.getId());
// Add a positive rating for the contact's pseudonym
db.setRating(remoteAuthor.getId(), GOOD);
// Store the remote transport properties // Store the remote transport properties
db.setRemoteProperties(contactId, remoteProps); db.setRemoteProperties(contactId, remoteProps);
// Create an endpoint for each transport shared with the contact // Create an endpoint for each transport shared with the contact

View File

@@ -3,7 +3,6 @@ package net.sf.briar.messaging.duplex;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH; import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
import static net.sf.briar.api.messaging.Rating.GOOD;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
@@ -35,7 +34,6 @@ import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent; import net.sf.briar.api.db.event.MessageExpiredEvent;
import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.MessageReceivedEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent; import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent; import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent;
import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.RemoteSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent; import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
@@ -157,10 +155,6 @@ abstract class DuplexConnection implements DatabaseListener {
if(canSendOffer.getAndSet(false)) if(canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer()); dbExecutor.execute(new GenerateOffer());
} }
} else if(e instanceof RatingChangedEvent) {
RatingChangedEvent r = (RatingChangedEvent) e;
if(r.getRating() == GOOD && canSendOffer.getAndSet(false))
dbExecutor.execute(new GenerateOffer());
} else if(e instanceof RemoteRetentionTimeUpdatedEvent) { } else if(e instanceof RemoteRetentionTimeUpdatedEvent) {
dbExecutor.execute(new GenerateRetentionAck()); dbExecutor.execute(new GenerateRetentionAck());
} else if(e instanceof RemoteSubscriptionsUpdatedEvent) { } else if(e instanceof RemoteSubscriptionsUpdatedEvent) {

View File

@@ -3,7 +3,6 @@ package net.sf.briar.db;
import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP; import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP;
import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE; import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.clock.SystemClock;
@@ -66,68 +65,6 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test
public void testExpiringUnsendableMessageDoesNotTriggerBackwardInclusion()
throws DbException {
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() {{
oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE - 1));
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
oneOf(database).removeMessage(txn, messageId);
oneOf(database).incrementRetentionVersions(txn);
oneOf(database).commitTransaction(txn);
oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE));
}});
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean();
context.assertIsSatisfied();
}
@Test
public void testExpiringSendableMessageTriggersBackwardInclusion()
throws DbException {
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() {{
oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE - 1));
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(1));
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(null));
oneOf(database).removeMessage(txn, messageId);
oneOf(database).incrementRetentionVersions(txn);
oneOf(database).commitTransaction(txn);
oneOf(database).getFreeSpace();
will(returnValue(MIN_FREE_SPACE));
}});
Callback db = createDatabaseComponentImpl(database, cleaner, shutdown);
db.checkFreeSpaceAndClean();
context.assertIsSatisfied();
}
@Override @Override
protected <T> DatabaseComponent createDatabaseComponent( protected <T> DatabaseComponent createDatabaseComponent(
Database<T> database, DatabaseCleaner cleaner, Database<T> database, DatabaseCleaner cleaner,

View File

@@ -2,8 +2,6 @@ package net.sf.briar.db;
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; 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.MessagingConstants.GROUP_SALT_LENGTH;
import static net.sf.briar.api.messaging.Rating.GOOD;
import static net.sf.briar.api.messaging.Rating.UNRATED;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -34,7 +32,6 @@ import net.sf.briar.api.db.event.DatabaseListener;
import net.sf.briar.api.db.event.GroupMessageAddedEvent; import net.sf.briar.api.db.event.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.PrivateMessageAddedEvent; import net.sf.briar.api.db.event.PrivateMessageAddedEvent;
import net.sf.briar.api.db.event.RatingChangedEvent;
import net.sf.briar.api.db.event.SubscriptionAddedEvent; import net.sf.briar.api.db.event.SubscriptionAddedEvent;
import net.sf.briar.api.db.event.SubscriptionRemovedEvent; import net.sf.briar.api.db.event.SubscriptionRemovedEvent;
import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.lifecycle.ShutdownManager;
@@ -124,9 +121,9 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
final ShutdownManager shutdown = context.mock(ShutdownManager.class); final ShutdownManager shutdown = context.mock(ShutdownManager.class);
final DatabaseListener listener = context.mock(DatabaseListener.class); final DatabaseListener listener = context.mock(DatabaseListener.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
exactly(13).of(database).startTransaction(); exactly(10).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(13).of(database).commitTransaction(txn); exactly(10).of(database).commitTransaction(txn);
// open() // open()
oneOf(database).open(); oneOf(database).open();
will(returnValue(false)); will(returnValue(false));
@@ -135,18 +132,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
with(any(long.class))); with(any(long.class)));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle)); will(returnValue(shutdownHandle));
// getRating(authorId)
oneOf(database).getRating(txn, authorId);
will(returnValue(UNRATED));
// setRating(authorId, GOOD)
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Collections.emptyList()));
oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class)));
// setRating(authorId, GOOD) again
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(GOOD));
// addLocalAuthor(localAuthor) // addLocalAuthor(localAuthor)
oneOf(database).addLocalAuthor(txn, localAuthor); oneOf(database).addLocalAuthor(txn, localAuthor);
// addContact(author, localAuthorId) // addContact(author, localAuthorId)
@@ -204,9 +189,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
assertFalse(db.open()); assertFalse(db.open());
db.addListener(listener); db.addListener(listener);
assertEquals(UNRATED, db.getRating(authorId));
db.setRating(authorId, GOOD); // First time - listeners called
db.setRating(authorId, GOOD); // Second time - not called
db.addLocalAuthor(localAuthor); db.addLocalAuthor(localAuthor);
assertEquals(contactId, db.addContact(author, localAuthorId)); assertEquals(contactId, db.addContact(author, localAuthorId));
assertEquals(Arrays.asList(contact), db.getContacts()); assertEquals(Arrays.asList(contact), db.getContacts());
@@ -225,114 +207,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test
public void testNullParentStopsBackwardInclusion() throws Exception {
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() {{
// setRating(authorId, GOOD)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 1);
// Backward inclusion stops when the message has no parent
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(null));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, GOOD);
context.assertIsSatisfied();
}
@Test
public void testUnaffectedParentStopsBackwardInclusion() throws Exception {
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() {{
// setRating(authorId, GOOD)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 1);
// The parent exists, is in the DB, and is in the same group
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(messageId1));
// The parent is already sendable
oneOf(database).getSendability(txn, messageId1);
will(returnValue(1));
oneOf(database).setSendability(txn, messageId1, 2);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, GOOD);
context.assertIsSatisfied();
}
@Test
public void testAffectedParentContinuesBackwardInclusion()
throws Exception {
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() {{
// setRating(authorId, GOOD)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).setRating(txn, authorId, GOOD);
will(returnValue(UNRATED));
// The sendability of the author's messages should be incremented
oneOf(database).getGroupMessages(txn, authorId);
will(returnValue(Arrays.asList(messageId)));
oneOf(database).getSendability(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 1);
// The parent exists, is in the DB, and is in the same group
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(messageId1));
// The parent is not already sendable
oneOf(database).getSendability(txn, messageId1);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId1, 1);
// The parent has no parent
oneOf(database).getGroupMessageParent(txn, messageId1);
will(returnValue(null));
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.setRating(authorId, GOOD);
context.assertIsSatisfied();
}
@Test @Test
public void testGroupMessagesAreNotStoredUnlessSubscribed() public void testGroupMessagesAreNotStoredUnlessSubscribed()
throws Exception { throws Exception {
@@ -401,51 +275,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).getContactIds(txn); oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId))); will(returnValue(Arrays.asList(contactId)));
oneOf(database).addStatus(txn, contactId, messageId, false); oneOf(database).addStatus(txn, contactId, messageId, false);
// The author is unrated and there are no sendable children
oneOf(database).getRating(txn, authorId);
will(returnValue(UNRATED));
oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 0);
oneOf(database).commitTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.addLocalGroupMessage(message);
context.assertIsSatisfied();
}
@Test
public void testAddingSendableMessageTriggersBackwardInclusion()
throws Exception {
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(message)
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).containsSubscription(txn, groupId);
will(returnValue(true));
oneOf(database).addGroupMessage(txn, message, 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);
// The author is rated GOOD and there are two sendable children
oneOf(database).getRating(txn, authorId);
will(returnValue(GOOD));
oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(2));
oneOf(database).setSendability(txn, messageId, 3);
// The sendability of the message's ancestors should be updated
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(null));
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, cleaner, DatabaseComponent db = createDatabaseComponent(database, cleaner,
@@ -1226,124 +1055,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
context.assertIsSatisfied(); context.assertIsSatisfied();
} }
@Test
public void testReceiveMessageDoesNotCalculateSendabilityForDuplicates()
throws Exception {
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() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).commitTransaction(txn);
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
// Only store messages belonging to visible, subscribed groups
oneOf(database).containsVisibleSubscription(txn, contactId,
groupId);
will(returnValue(true));
// The message is not stored, it's a duplicate
oneOf(database).addGroupMessage(txn, message, true);
will(returnValue(false));
oneOf(database).addStatus(txn, contactId, messageId, true);
// The message must be acked
oneOf(database).addMessageToAck(txn, contactId, messageId);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveMessage(contactId, message);
context.assertIsSatisfied();
}
@Test
public void testReceiveMessageCalculatesSendability() throws Exception {
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() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).commitTransaction(txn);
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
// Only store messages belonging to visible, subscribed groups
oneOf(database).containsVisibleSubscription(txn, contactId,
groupId);
will(returnValue(true));
// The message is stored, and it's not a duplicate
oneOf(database).addGroupMessage(txn, message, true);
will(returnValue(true));
oneOf(database).addStatus(txn, contactId, messageId, true);
// Set the status to seen = true for all other contacts (none)
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
// Calculate the sendability - zero, so ancestors aren't updated
oneOf(database).getRating(txn, authorId);
will(returnValue(UNRATED));
oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 0);
// The message must be acked
oneOf(database).addMessageToAck(txn, contactId, messageId);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveMessage(contactId, message);
context.assertIsSatisfied();
}
@Test
public void testReceiveMessageUpdatesAncestorSendability()
throws Exception {
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() {{
oneOf(database).startTransaction();
will(returnValue(txn));
oneOf(database).commitTransaction(txn);
oneOf(database).containsContact(txn, contactId);
will(returnValue(true));
// Only store messages belonging to visible, subscribed groups
oneOf(database).containsVisibleSubscription(txn, contactId,
groupId);
will(returnValue(true));
// The message is stored, and it's not a duplicate
oneOf(database).addGroupMessage(txn, message, true);
will(returnValue(true));
oneOf(database).addStatus(txn, contactId, messageId, true);
// Set the status to seen = true for all other contacts (none)
oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId)));
// Calculate the sendability - ancestors are updated
oneOf(database).getRating(txn, authorId);
will(returnValue(GOOD));
oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(1));
oneOf(database).setSendability(txn, messageId, 2);
oneOf(database).getGroupMessageParent(txn, messageId);
will(returnValue(null));
// The message must be acked
oneOf(database).addMessageToAck(txn, contactId, messageId);
}});
DatabaseComponent db = createDatabaseComponent(database, cleaner,
shutdown);
db.receiveMessage(contactId, message);
context.assertIsSatisfied();
}
@Test @Test
public void testReceiveOffer() throws Exception { public void testReceiveOffer() throws Exception {
final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -1527,11 +1238,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
oneOf(database).getContactIds(txn); oneOf(database).getContactIds(txn);
will(returnValue(Arrays.asList(contactId))); will(returnValue(Arrays.asList(contactId)));
oneOf(database).addStatus(txn, contactId, messageId, false); oneOf(database).addStatus(txn, contactId, messageId, false);
oneOf(database).getRating(txn, authorId);
will(returnValue(UNRATED));
oneOf(database).getNumberOfSendableChildren(txn, messageId);
will(returnValue(0));
oneOf(database).setSendability(txn, messageId, 0);
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
// The message was added, so the listener should be called // The message was added, so the listener should be called
oneOf(listener).eventOccurred(with(any( oneOf(listener).eventOccurred(with(any(

View File

@@ -3,8 +3,6 @@ package net.sf.briar.db;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static net.sf.briar.api.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; 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.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; import static org.junit.Assert.assertArrayEquals;
import java.io.File; import java.io.File;
@@ -153,22 +151,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testRatings() throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Unknown authors should be unrated
assertEquals(UNRATED, db.getRating(txn, authorId));
// Store a rating
db.setRating(txn, authorId, GOOD);
// Check that the rating was stored
assertEquals(GOOD, db.getRating(txn, authorId));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testUnsubscribingRemovesGroupMessage() throws Exception { public void testUnsubscribingRemovesGroupMessage() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -264,45 +246,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testSendableGroupMessagesMustHavePositiveSendability()
throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addSubscription(txn, group);
db.addVisibility(txn, contactId, groupId);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false);
db.addStatus(txn, contactId, messageId, false);
// The message should not be sendable
assertFalse(db.hasSendableMessages(txn, contactId));
Iterator<MessageId> it =
db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext());
// Changing the sendability to > 0 should make the message sendable
db.setSendability(txn, messageId, 1);
assertTrue(db.hasSendableMessages(txn, contactId));
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertTrue(it.hasNext());
assertEquals(messageId, it.next());
assertFalse(it.hasNext());
// Changing the sendability to 0 should make the message unsendable
db.setSendability(txn, messageId, 0);
assertFalse(db.hasSendableMessages(txn, contactId));
it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator();
assertFalse(it.hasNext());
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testSendableGroupMessagesMustHaveSeenFlagFalse() public void testSendableGroupMessagesMustHaveSeenFlagFalse()
throws Exception { throws Exception {
@@ -316,7 +259,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
db.setSendability(txn, messageId, 1);
// The message has no status yet, so it should not be sendable // The message has no status yet, so it should not be sendable
assertFalse(db.hasSendableMessages(txn, contactId)); assertFalse(db.hasSendableMessages(txn, contactId));
@@ -353,7 +295,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.addSubscription(txn, group); db.addSubscription(txn, group);
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// The contact is not subscribed, so the message should not be sendable // The contact is not subscribed, so the message should not be sendable
@@ -392,7 +333,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// The message is sendable, but too large to send // The message is sendable, but too large to send
@@ -423,7 +363,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.addSubscription(txn, group); db.addSubscription(txn, group);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// The subscription is not visible to the contact, so the message // The subscription is not visible to the contact, so the message
@@ -509,7 +448,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.addVisibility(txn, contactId, groupId); db.addVisibility(txn, contactId, groupId);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// Retrieve the message from the database and mark it as sent // Retrieve the message from the database and mark it as sent
@@ -568,47 +506,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testGetNumberOfSendableChildren() throws Exception {
MessageId childId1 = new MessageId(TestUtils.getRandomId());
MessageId childId2 = new MessageId(TestUtils.getRandomId());
MessageId childId3 = new MessageId(TestUtils.getRandomId());
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
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,
contentType, subject, timestamp, raw);
// The third child is in a different group
Message child3 = new TestMessage(childId3, messageId, group1, author,
contentType, subject, timestamp, raw);
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Subscribe to the groups and store the messages
db.addSubscription(txn, group);
db.addSubscription(txn, group1);
db.addGroupMessage(txn, message, false);
db.addGroupMessage(txn, child1, false);
db.addGroupMessage(txn, child2, false);
db.addGroupMessage(txn, child3, false);
// Make all the children sendable
db.setSendability(txn, childId1, 1);
db.setSendability(txn, childId2, 5);
db.setSendability(txn, childId3, 3);
// There should be two sendable children
assertEquals(2, db.getNumberOfSendableChildren(txn, messageId));
// Make one of the children unsendable
db.setSendability(txn, childId1, 0);
// Now there should be one sendable child
assertEquals(1, db.getNumberOfSendableChildren(txn, messageId));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testGetOldMessages() throws Exception { public void testGetOldMessages() throws Exception {
MessageId messageId1 = new MessageId(TestUtils.getRandomId()); MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -902,8 +799,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
// Set the sendability to > 0 and the status to seen = true // Set the status to seen = true
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, true); db.addStatus(txn, contactId, messageId, true);
// The message is not sendable because its status is seen = true // The message is not sendable because its status is seen = true
@@ -913,31 +809,6 @@ public class H2DatabaseTest extends BriarTestCase {
db.close(); db.close();
} }
@Test
public void testGetMessageIfSendableReturnsNullIfNotSendable()
throws Exception {
Database<Connection> db = open(false);
Connection txn = db.startTransaction();
// Add a contact, subscribe to a group and store a message
db.addLocalAuthor(txn, localAuthor);
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
db.addSubscription(txn, group);
db.addVisibility(txn, contactId, groupId);
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false);
// Set the sendability to 0 and the status to seen = false
db.setSendability(txn, messageId, 0);
db.addStatus(txn, contactId, messageId, false);
// The message is not sendable because its sendability is 0
assertNull(db.getRawMessageIfSendable(txn, contactId, messageId));
db.commitTransaction(txn);
db.close();
}
@Test @Test
public void testGetMessageIfSendableReturnsNullIfOld() throws Exception { public void testGetMessageIfSendableReturnsNullIfOld() throws Exception {
Database<Connection> db = open(false); Database<Connection> db = open(false);
@@ -953,8 +824,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.setRetentionTime(txn, contactId, timestamp + 1, 1); db.setRetentionTime(txn, contactId, timestamp + 1, 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
// Set the sendability to > 0 and the status to seen = false // Set the status to seen = false
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// The message is not sendable because it's too old // The message is not sendable because it's too old
@@ -977,8 +847,7 @@ public class H2DatabaseTest extends BriarTestCase {
db.setSubscriptions(txn, contactId, Arrays.asList(group), 1); db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
db.addGroupMessage(txn, message, false); db.addGroupMessage(txn, message, false);
// Set the sendability to > 0 and the status to seen = false // Set the status to seen = false
db.setSendability(txn, messageId, 1);
db.addStatus(txn, contactId, messageId, false); db.addStatus(txn, contactId, messageId, false);
// The message is sendable so it should be returned // The message is sendable so it should be returned