From 0a153acd02529282e64cc4795173cb56fbbed805 Mon Sep 17 00:00:00 2001
From: akwizgran <akwizgran@users.sourceforge.net>
Date: Fri, 27 Sep 2013 18:04:27 +0100
Subject: [PATCH] Removed peer moderation (may be restored after beta testing).

---
 .../sf/briar/android/HomeScreenActivity.java  |   2 -
 .../identity/CreateIdentityActivity.java      |   2 -
 .../sf/briar/api/db/DatabaseComponent.java    |   7 -
 .../sf/briar/api/db/GroupMessageHeader.java   |   5 +-
 .../net/sf/briar/api/db/MessageHeader.java    |  13 +-
 .../sf/briar/api/db/PrivateMessageHeader.java |   6 +-
 .../api/db/event/RatingChangedEvent.java      |  23 --
 .../net/sf/briar/api/messaging/Rating.java    |   6 -
 briar-core/src/net/sf/briar/db/Database.java  |  43 +--
 .../sf/briar/db/DatabaseComponentImpl.java    | 266 +++-------------
 .../src/net/sf/briar/db/JdbcDatabase.java     | 220 ++-----------
 .../net/sf/briar/invitation/Connector.java    |   3 -
 .../messaging/duplex/DuplexConnection.java    |   6 -
 .../briar/db/DatabaseComponentImplTest.java   |  63 ----
 .../sf/briar/db/DatabaseComponentTest.java    | 298 +-----------------
 .../src/net/sf/briar/db/H2DatabaseTest.java   | 137 +-------
 16 files changed, 80 insertions(+), 1020 deletions(-)
 delete mode 100644 briar-api/src/net/sf/briar/api/db/event/RatingChangedEvent.java
 delete mode 100644 briar-api/src/net/sf/briar/api/messaging/Rating.java

diff --git a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
index 915f4b3d45..175511fc21 100644
--- a/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
+++ b/briar-android/src/net/sf/briar/android/HomeScreenActivity.java
@@ -13,7 +13,6 @@ import static java.util.logging.Level.INFO;
 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.WRAP_WRAP;
-import static net.sf.briar.api.messaging.Rating.GOOD;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -168,7 +167,6 @@ public class HomeScreenActivity extends RoboActivity {
 					lifecycleManager.waitForDatabase();
 					long now = System.currentTimeMillis();
 					db.addLocalAuthor(a);
-					db.setRating(a.getId(), GOOD);
 					long duration = System.currentTimeMillis() - now;
 					if(LOG.isLoggable(INFO))
 						LOG.info("Storing author took " + duration + " ms");
diff --git a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
index 1df435f8d6..dba0a3eef8 100644
--- a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
+++ b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
@@ -12,7 +12,6 @@ import static java.util.logging.Level.INFO;
 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.WRAP_WRAP;
-import static net.sf.briar.api.messaging.Rating.GOOD;
 
 import java.io.IOException;
 import java.util.concurrent.Executor;
@@ -143,7 +142,6 @@ implements OnEditorActionListener, OnClickListener {
 					lifecycleManager.waitForDatabase();
 					long now = System.currentTimeMillis();
 					db.addLocalAuthor(a);
-					db.setRating(a.getId(), GOOD);
 					long duration = System.currentTimeMillis() - now;
 					if(LOG.isLoggable(INFO))
 						LOG.info("Storing author took " + duration + " ms");
diff --git a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
index d0c7a9f1b3..7de22f1e86 100644
--- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
+++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
@@ -20,7 +20,6 @@ import net.sf.briar.api.messaging.GroupStatus;
 import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageId;
 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.RetentionAck;
 import net.sf.briar.api.messaging.RetentionUpdate;
@@ -204,9 +203,6 @@ public interface DatabaseComponent {
 	Collection<PrivateMessageHeader> getPrivateMessageHeaders(ContactId c)
 			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. */
 	boolean getReadFlag(MessageId m) throws DbException;
 
@@ -313,9 +309,6 @@ public interface DatabaseComponent {
 	void setConnectionWindow(ContactId c, TransportId t, long period,
 			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
 	 * previously read.
diff --git a/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java b/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java
index df6e4ac240..6df4434fb2 100644
--- a/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java
+++ b/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java
@@ -3,7 +3,6 @@ package net.sf.briar.api.db;
 import net.sf.briar.api.Author;
 import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.messaging.MessageId;
-import net.sf.briar.api.messaging.Rating;
 
 public class GroupMessageHeader extends MessageHeader {
 
@@ -11,9 +10,9 @@ public class GroupMessageHeader extends MessageHeader {
 
 	public GroupMessageHeader(MessageId id, MessageId parent, Author author,
 			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,
-				starred, rating);
+				starred);
 		this.groupId = groupId;
 	}
 
diff --git a/briar-api/src/net/sf/briar/api/db/MessageHeader.java b/briar-api/src/net/sf/briar/api/db/MessageHeader.java
index 2f48df5b6a..beeda54c4b 100644
--- a/briar-api/src/net/sf/briar/api/db/MessageHeader.java
+++ b/briar-api/src/net/sf/briar/api/db/MessageHeader.java
@@ -2,7 +2,6 @@ package net.sf.briar.api.db;
 
 import net.sf.briar.api.Author;
 import net.sf.briar.api.messaging.MessageId;
-import net.sf.briar.api.messaging.Rating;
 
 public abstract class MessageHeader {
 
@@ -11,11 +10,10 @@ public abstract class MessageHeader {
 	private final String contentType, subject;
 	private final long timestamp;
 	private final boolean read, starred;
-	private final Rating rating;
 
 	protected MessageHeader(MessageId id, MessageId parent, Author author,
 			String contentType, String subject, long timestamp, boolean read,
-			boolean starred, Rating rating) {
+			boolean starred) {
 		this.id = id;
 		this.parent = parent;
 		this.author = author;
@@ -24,7 +22,6 @@ public abstract class MessageHeader {
 		this.timestamp = timestamp;
 		this.read = read;
 		this.starred = starred;
-		this.rating = rating;
 	}
 
 	/** Returns the message's unique identifier. */
@@ -71,12 +68,4 @@ public abstract class MessageHeader {
 	public boolean isStarred() {
 		return starred;
 	}
-
-	/**
-	 * Returns the rating for the message's author, or Rating.UNRATED if this
-	 * is an anonymous message.
-	 */
-	public Rating getRating() {
-		return rating;
-	}
 }
diff --git a/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java b/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java
index 5a9b3c3472..1af301e990 100644
--- a/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java
+++ b/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java
@@ -3,7 +3,6 @@ package net.sf.briar.api.db;
 import net.sf.briar.api.Author;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.messaging.MessageId;
-import net.sf.briar.api.messaging.Rating;
 
 public class PrivateMessageHeader extends MessageHeader {
 
@@ -12,10 +11,9 @@ public class PrivateMessageHeader extends MessageHeader {
 
 	public PrivateMessageHeader(MessageId id, MessageId parent, Author author,
 			String contentType, String subject, long timestamp, boolean read,
-			boolean starred, Rating rating, ContactId contactId,
-			boolean incoming) {
+			boolean starred, ContactId contactId, boolean incoming) {
 		super(id, parent, author, contentType, subject, timestamp, read,
-				starred, rating);
+				starred);
 		this.contactId = contactId;
 		this.incoming = incoming;
 	}
diff --git a/briar-api/src/net/sf/briar/api/db/event/RatingChangedEvent.java b/briar-api/src/net/sf/briar/api/db/event/RatingChangedEvent.java
deleted file mode 100644
index bc47937221..0000000000
--- a/briar-api/src/net/sf/briar/api/db/event/RatingChangedEvent.java
+++ /dev/null
@@ -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;
-	}
-}
diff --git a/briar-api/src/net/sf/briar/api/messaging/Rating.java b/briar-api/src/net/sf/briar/api/messaging/Rating.java
deleted file mode 100644
index 86dd7d67cd..0000000000
--- a/briar-api/src/net/sf/briar/api/messaging/Rating.java
+++ /dev/null
@@ -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
-}
diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java
index f08ed03358..819804cfd6 100644
--- a/briar-core/src/net/sf/briar/db/Database.java
+++ b/briar-core/src/net/sf/briar/db/Database.java
@@ -20,7 +20,6 @@ import net.sf.briar.api.messaging.GroupId;
 import net.sf.briar.api.messaging.GroupStatus;
 import net.sf.briar.api.messaging.Message;
 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.RetentionUpdate;
 import net.sf.briar.api.messaging.SubscriptionAck;
@@ -45,7 +44,6 @@ import net.sf.briar.api.transport.TemporarySecret;
  * <li> contact
  * <li> identity
  * <li> message
- * <li> rating
  * <li> retention
  * <li> subscription
  * <li> transport
@@ -270,7 +268,7 @@ interface Database<T> {
 	/**
 	 * Returns the headers of all messages in the given group.
 	 * <p>
-	 * Locking: message read, rating read.
+	 * Locking: message read.
 	 */
 	Collection<GroupMessageHeader> getGroupMessageHeaders(T txn, GroupId g)
 			throws DbException;
@@ -341,7 +339,7 @@ interface Database<T> {
 	 * Returns the headers of all private messages to or from the given
 	 * contact.
 	 * <p>
-	 * Locking: contact read, identity read, message read, rating read.
+	 * Locking: contact read, identity read, message read.
 	 */
 	Collection<PrivateMessageHeader> getPrivateMessageHeaders(T txn,
 			ContactId c) throws DbException;
@@ -364,15 +362,6 @@ interface Database<T> {
 	Collection<MessageId> getMessagesToOffer(T txn, ContactId c,
 			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.
 	 * <p>
@@ -398,13 +387,6 @@ interface Database<T> {
 	 */
 	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.
 	 * <p>
@@ -443,13 +425,6 @@ interface Database<T> {
 	 */
 	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
 	 * 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;
 
-	/**
-	 * 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
 	 * previously read.
@@ -714,13 +682,6 @@ interface Database<T> {
 	boolean setRetentionTime(T txn, ContactId c, long retention, long version)
 			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
 	 * previously starred.
diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
index edc300ab6d..6171009506 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
@@ -2,7 +2,6 @@ package net.sf.briar.db;
 
 import static java.util.logging.Level.INFO;
 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.db.DatabaseConstants.BYTES_PER_SWEEP;
 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.MessageReceivedEvent;
 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.RemoteSubscriptionsUpdatedEvent;
 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.MessageId;
 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.RetentionAck;
 import net.sf.briar.api.messaging.RetentionUpdate;
@@ -105,8 +102,6 @@ DatabaseCleaner.Callback {
 			new ReentrantReadWriteLock(true);
 	private final ReentrantReadWriteLock messageLock =
 			new ReentrantReadWriteLock(true);
-	private final ReentrantReadWriteLock ratingLock =
-			new ReentrantReadWriteLock(true);
 	private final ReentrantReadWriteLock retentionLock =
 			new ReentrantReadWriteLock(true);
 	private final ReentrantReadWriteLock subscriptionLock =
@@ -290,27 +285,22 @@ DatabaseCleaner.Callback {
 		try {
 			messageLock.writeLock().lock();
 			try {
-				ratingLock.readLock().lock();
+				subscriptionLock.readLock().lock();
 				try {
-					subscriptionLock.readLock().lock();
+					T txn = db.startTransaction();
 					try {
-						T txn = db.startTransaction();
-						try {
-							// Don't store the message if the user has
-							// unsubscribed from the group
-							GroupId g = m.getGroup().getId();
-							if(db.containsSubscription(txn, g))
-								added = storeGroupMessage(txn, m, null);
-							db.commitTransaction(txn);
-						} catch(DbException e) {
-							db.abortTransaction(txn);
-							throw e;
-						}
-					} finally {
-						subscriptionLock.readLock().unlock();
+						// Don't store the message if the user has
+						// unsubscribed from the group
+						GroupId g = m.getGroup().getId();
+						if(db.containsSubscription(txn, g))
+							added = storeGroupMessage(txn, m, null);
+						db.commitTransaction(txn);
+					} catch(DbException e) {
+						db.abortTransaction(txn);
+						throw e;
 					}
 				} finally {
-					ratingLock.readLock().unlock();
+					subscriptionLock.readLock().unlock();
 				}
 			} finally {
 				messageLock.writeLock().unlock();
@@ -324,11 +314,10 @@ DatabaseCleaner.Callback {
 
 	/**
 	 * 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
-	 * sendability of its ancestors if necessary, marks the message as seen by
-	 * the sender and unseen by all other contacts, and returns true.
+	 * sender and returns false. Otherwise stores the message, marks it as seen
+	 * by the sender and unseen by all other contacts, and returns true.
 	 * <p>
-	 * Locking: contact read, message write, rating read.
+	 * Locking: contact read, message write.
 	 * @param sender is null for a locally generated message.
 	 */
 	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(stored) {
 			// 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);
-			}
-			// 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
 			synchronized(spaceLock) {
 				bytesStoredSinceLastCheck += m.getSerialised().length;
@@ -359,59 +343,6 @@ DatabaseCleaner.Callback {
 		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)
 			throws DbException {
 		boolean added;
@@ -967,27 +898,22 @@ DatabaseCleaner.Callback {
 			throws DbException {
 		messageLock.readLock().lock();
 		try {
-			ratingLock.readLock().lock();
+			subscriptionLock.readLock().lock();
 			try {
-				subscriptionLock.readLock().lock();
+				T txn = db.startTransaction();
 				try {
-					T txn = db.startTransaction();
-					try {
-						if(!db.containsSubscription(txn, g))
-							throw new NoSuchSubscriptionException();
-						Collection<GroupMessageHeader> headers =
-								db.getGroupMessageHeaders(txn, g);
-						db.commitTransaction(txn);
-						return headers;
-					} catch(DbException e) {
-						db.abortTransaction(txn);
-						throw e;
-					}
-				} finally {
-					subscriptionLock.readLock().unlock();
+					if(!db.containsSubscription(txn, g))
+						throw new NoSuchSubscriptionException();
+					Collection<GroupMessageHeader> headers =
+							db.getGroupMessageHeaders(txn, g);
+					db.commitTransaction(txn);
+					return headers;
+				} catch(DbException e) {
+					db.abortTransaction(txn);
+					throw e;
 				}
 			} finally {
-				ratingLock.readLock().unlock();
+				subscriptionLock.readLock().unlock();
 			}
 		} finally {
 			messageLock.readLock().unlock();
@@ -1111,20 +1037,15 @@ DatabaseCleaner.Callback {
 			try {
 				messageLock.readLock().lock();
 				try {
-					ratingLock.readLock().lock();
+					T txn = db.startTransaction();
 					try {
-						T txn = db.startTransaction();
-						try {
-							Collection<PrivateMessageHeader> headers =
-									db.getPrivateMessageHeaders(txn, c);
-							db.commitTransaction(txn);
-							return headers;
-						} catch(DbException e) {
-							db.abortTransaction(txn);
-							throw e;
-						}
-					} finally {
-						ratingLock.readLock().unlock();
+						Collection<PrivateMessageHeader> headers =
+								db.getPrivateMessageHeaders(txn, c);
+						db.commitTransaction(txn);
+						return headers;
+					} catch(DbException e) {
+						db.abortTransaction(txn);
+						throw e;
 					}
 				} finally {
 					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 {
 		messageLock.readLock().lock();
 		try {
@@ -1463,26 +1367,21 @@ DatabaseCleaner.Callback {
 		try {
 			messageLock.writeLock().lock();
 			try {
-				ratingLock.readLock().lock();
+				subscriptionLock.readLock().lock();
 				try {
-					subscriptionLock.readLock().lock();
+					T txn = db.startTransaction();
 					try {
-						T txn = db.startTransaction();
-						try {
-							if(!db.containsContact(txn, c))
-								throw new NoSuchContactException();
-							added = storeMessage(txn, c, m);
-							db.addMessageToAck(txn, c, m.getId());
-							db.commitTransaction(txn);
-						} catch(DbException e) {
-							db.abortTransaction(txn);
-							throw e;
-						}
-					} finally {
-						subscriptionLock.readLock().unlock();
+						if(!db.containsContact(txn, c))
+							throw new NoSuchContactException();
+						added = storeMessage(txn, c, m);
+						db.addMessageToAck(txn, c, m.getId());
+						db.commitTransaction(txn);
+					} catch(DbException e) {
+						db.abortTransaction(txn);
+						throw e;
 					}
 				} finally {
-					ratingLock.readLock().unlock();
+					subscriptionLock.readLock().unlock();
 				}
 			} finally {
 				messageLock.writeLock().unlock();
@@ -1502,7 +1401,7 @@ DatabaseCleaner.Callback {
 	 * Attempts to store a message received from the given contact, and returns
 	 * true if it was stored.
 	 * <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)
 			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 {
 		messageLock.writeLock().lock();
 		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)
 			throws DbException {
 		messageLock.writeLock().lock();
@@ -2134,7 +1979,7 @@ DatabaseCleaner.Callback {
 				try {
 					expired = db.getOldMessages(txn, size);
 					if(!expired.isEmpty()) {
-						for(MessageId m : expired) removeMessage(txn, m);
+						for(MessageId m : expired) db.removeMessage(txn, m);
 						db.incrementRetentionVersions(txn);
 						if(LOG.isLoggable(INFO))
 							LOG.info("Expired " + expired.size() + " messages");
@@ -2155,19 +2000,6 @@ DatabaseCleaner.Callback {
 		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() {
 		synchronized(spaceLock) {
 			long now = clock.currentTimeMillis();
diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
index c42b223ef8..c5d8e9546c 100644
--- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java
+++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
@@ -6,7 +6,6 @@ import static java.util.logging.Level.INFO;
 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.RETENTION_MODULUS;
-import static net.sf.briar.api.messaging.Rating.UNRATED;
 import static net.sf.briar.db.ExponentialBackoff.calculateExpiry;
 
 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.Message;
 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.RetentionUpdate;
 import net.sf.briar.api.messaging.SubscriptionAck;
@@ -154,7 +152,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " bodyLength INT NOT NULL,"
 					+ " raw BLOB NOT NULL,"
 					+ " incoming BOOLEAN NOT NULL,"
-					+ " sendability INT UNSIGNED," // Null for private messages
 					+ " contactId INT UNSIGNED," // Null for group messages
 					+ " read BOOLEAN NOT NULL,"
 					+ " starred BOOLEAN NOT NULL,"
@@ -166,18 +163,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " REFERENCES contacts (contactId)"
 					+ " 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 =
 			"CREATE INDEX messagesByAuthor ON messages (authorId)";
 
 	private static final String INDEX_MESSAGES_BY_TIMESTAMP =
 			"CREATE INDEX messagesByTimestamp ON messages (timestamp)";
 
-	private static final String INDEX_MESSAGES_BY_SENDABILITY =
-			"CREATE INDEX messagesBySendability ON messages (sendability)";
-
 	// Locking: message
 	private static final String CREATE_MESSAGES_TO_ACK =
 			"CREATE TABLE messagesToAck"
@@ -210,13 +201,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 	private static final String INDEX_STATUSES_BY_CONTACT =
 			"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
 	private static final String CREATE_RETENTION_VERSIONS =
 			"CREATE TABLE retentionVersions"
@@ -403,15 +387,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 			s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS));
 			s.executeUpdate(insertTypeNames(CREATE_GROUP_VERSIONS));
 			s.executeUpdate(insertTypeNames(CREATE_MESSAGES));
-			s.executeUpdate(INDEX_MESSAGES_BY_PARENT);
 			s.executeUpdate(INDEX_MESSAGES_BY_AUTHOR);
 			s.executeUpdate(INDEX_MESSAGES_BY_TIMESTAMP);
-			s.executeUpdate(INDEX_MESSAGES_BY_SENDABILITY);
 			s.executeUpdate(insertTypeNames(CREATE_MESSAGES_TO_ACK));
 			s.executeUpdate(insertTypeNames(CREATE_STATUSES));
 			s.executeUpdate(INDEX_STATUSES_BY_MESSAGE);
 			s.executeUpdate(INDEX_STATUSES_BY_CONTACT);
-			s.executeUpdate(insertTypeNames(CREATE_RATINGS));
 			s.executeUpdate(insertTypeNames(CREATE_RETENTION_VERSIONS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS));
 			s.executeUpdate(insertTypeNames(CREATE_TRANSPORT_CONFIGS));
@@ -681,8 +662,8 @@ abstract class JdbcDatabase implements Database<Connection> {
 			String sql = "INSERT INTO messages (messageId, parentId, groupId,"
 					+ " authorId, authorName, authorKey, contentType, subject,"
 					+ " timestamp, length, bodyStart, bodyLength, raw,"
-					+ " incoming, sendability, read, starred)"
-					+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0,"
+					+ " incoming, read, starred)"
+					+ " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,"
 					+ " FALSE, FALSE)";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, m.getId().getBytes());
@@ -1307,12 +1288,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 		PreparedStatement ps = null;
 		ResultSet rs = null;
 		try {
-			String sql = "SELECT messageId, parentId, m.authorId, authorName,"
-					+ " authorKey, rating, contentType, subject, timestamp,"
-					+ " read, starred"
-					+ " FROM messages AS m"
-					+ " LEFT OUTER JOIN ratings AS r"
-					+ " ON m.authorId = r.authorId"
+			String sql = "SELECT messageId, parentId, authorId, authorName,"
+					+ " authorKey, contentType, subject, timestamp, read,"
+					+ " starred"
+					+ " FROM messages"
 					+ " WHERE groupId = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, g.getBytes());
@@ -1324,27 +1303,22 @@ abstract class JdbcDatabase implements Database<Connection> {
 				byte[] b = rs.getBytes(2);
 				MessageId parent = b == null ? null : new MessageId(b);
 				Author author;
-				Rating rating;
 				b = rs.getBytes(3);
 				if(b == null) {
 					author = null;
-					rating = UNRATED;
 				} else {
 					AuthorId authorId = new AuthorId(b);
 					String authorName = rs.getString(4);
 					byte[] authorKey = rs.getBytes(5);
 					author = new Author(authorId, authorName, authorKey);
-					// NULL == 0 == UNRATED
-					rating = Rating.values()[rs.getByte(6)];
 				}
-				String contentType = rs.getString(7);
-				String subject = rs.getString(8);
-				long timestamp = rs.getLong(9);
-				boolean read = rs.getBoolean(10);
-				boolean starred = rs.getBoolean(11);
+				String contentType = rs.getString(6);
+				String subject = rs.getString(7);
+				long timestamp = rs.getLong(8);
+				boolean read = rs.getBoolean(9);
+				boolean starred = rs.getBoolean(10);
 				headers.add(new GroupMessageHeader(id, parent, author,
-						contentType, subject, timestamp, read, starred, rating,
-						g));
+						contentType, subject, timestamp, read, starred, g));
 			}
 			rs.close();
 			ps.close();
@@ -1626,7 +1600,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " WHERE cg.contactId = ?"
 					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE AND s.expiry < ?"
-					+ " AND sendability > 0"
 					+ " ORDER BY timestamp DESC LIMIT ?";
 			ps = txn.prepareStatement(sql);
 			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)
 			throws DbException {
 		PreparedStatement ps = null;
@@ -1713,13 +1651,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 		try {
 			// Get the incoming message headers
 			String sql = "SELECT m.messageId, parentId, contentType, subject,"
-					+ " timestamp, read, starred, c.authorId, name, publicKey,"
-					+ " rating"
+					+ " timestamp, read, starred, c.authorId, name, publicKey"
 					+ " FROM messages AS m"
 					+ " JOIN contacts AS c"
 					+ " ON m.contactId = c.contactId"
-					+ " LEFT OUTER JOIN ratings AS r"
-					+ " ON c.authorId = r.authorId"
 					+ " WHERE m.contactId = ?"
 					+ " AND groupId IS NULL"
 					+ " AND incoming = TRUE";
@@ -1741,25 +1676,21 @@ abstract class JdbcDatabase implements Database<Connection> {
 				String authorName = rs.getString(9);
 				byte[] authorKey = rs.getBytes(10);
 				Author author = new Author(authorId, authorName, authorKey);
-				// NULL == 0 == UNRATED
-				Rating rating = Rating.values()[rs.getByte(11)];
 				headers.add(new PrivateMessageHeader(id, parent, author,
-						contentType, subject, timestamp, read, starred, rating,
-						c, true));
+						contentType, subject, timestamp, read, starred, c,
+						true));
 			}
 			rs.close();
 			ps.close();
 			// Get the outgoing message headers
 			sql = "SELECT m.messageId, parentId, contentType, subject,"
 					+ " timestamp, read, starred, a.authorId, a.name,"
-					+ " a.publicKey, rating"
+					+ " a.publicKey"
 					+ " FROM messages AS m"
 					+ " JOIN contacts AS c"
 					+ " ON m.contactId = c.contactId"
 					+ " JOIN localAuthors AS a"
 					+ " ON c.localAuthorId = a.authorId"
-					+ " LEFT OUTER JOIN ratings AS r"
-					+ " ON c.localAuthorId = r.authorId"
 					+ " WHERE m.contactId = ?"
 					+ " AND groupId IS NULL"
 					+ " AND incoming = FALSE";
@@ -1779,11 +1710,9 @@ abstract class JdbcDatabase implements Database<Connection> {
 				String authorName = rs.getString(9);
 				byte[] authorKey = rs.getBytes(10);
 				Author author = new Author(authorId, authorName, authorKey);
-				// NULL == 0 == UNRATED
-				Rating rating = Rating.values()[rs.getByte(11)];
 				headers.add(new PrivateMessageHeader(id, parent, author,
-						contentType, subject, timestamp, read, starred, rating,
-						c, false));
+						contentType, subject, timestamp, read, starred, c,
+						false));
 			}
 			rs.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)
 			throws DbException {
 		PreparedStatement ps = null;
@@ -1883,8 +1790,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " WHERE m.messageId = ?"
 					+ " AND cg.contactId = ?"
 					+ " AND timestamp >= retention"
-					+ " AND seen = FALSE AND s.expiry < ?"
-					+ " AND sendability > 0";
+					+ " AND seen = FALSE AND s.expiry < ?";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, m.getBytes());
 			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,
 			ContactId c, int maxLength) throws DbException {
 		long now = clock.currentTimeMillis();
@@ -2143,7 +2028,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " WHERE cg.contactId = ?"
 					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE AND s.expiry < ?"
-					+ " AND sendability > 0"
 					+ " ORDER BY timestamp DESC";
 			ps = txn.prepareStatement(sql);
 			ps.setInt(1, c.getInt());
@@ -2587,7 +2471,6 @@ abstract class JdbcDatabase implements Database<Connection> {
 					+ " WHERE cg.contactId = ?"
 					+ " AND timestamp >= retention"
 					+ " AND seen = FALSE AND s.expiry < ?"
-					+ " AND sendability > 0"
 					+ " LIMIT 1";
 			ps = txn.prepareStatement(sql);
 			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)
 			throws DbException {
 		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)
 			throws DbException {
 		PreparedStatement ps = null;
diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java
index 35283c7844..7ccd4952f7 100644
--- a/briar-core/src/net/sf/briar/invitation/Connector.java
+++ b/briar-core/src/net/sf/briar/invitation/Connector.java
@@ -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.invitation.InvitationConstants.CONNECTION_TIMEOUT;
 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.security.GeneralSecurityException;
@@ -263,8 +262,6 @@ abstract class Connector extends Thread {
 			long epoch, boolean alice) throws DbException {
 		// Add the contact to the database
 		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
 		db.setRemoteProperties(contactId, remoteProps);
 		// Create an endpoint for each transport shared with the contact
diff --git a/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java b/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
index d5fae8e24e..d671b09072 100644
--- a/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
+++ b/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java
@@ -3,7 +3,6 @@ package net.sf.briar.messaging.duplex;
 import static java.util.logging.Level.INFO;
 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.Rating.GOOD;
 
 import java.io.IOException;
 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.MessageReceivedEvent;
 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.RemoteSubscriptionsUpdatedEvent;
 import net.sf.briar.api.db.event.RemoteTransportsUpdatedEvent;
@@ -157,10 +155,6 @@ abstract class DuplexConnection implements DatabaseListener {
 				if(canSendOffer.getAndSet(false))
 					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) {
 			dbExecutor.execute(new GenerateRetentionAck());
 		} else if(e instanceof RemoteSubscriptionsUpdatedEvent) {
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
index 68d12d2868..d07bb403ba 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java
@@ -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.MIN_FREE_SPACE;
 
-import java.util.Arrays;
 import java.util.Collections;
 
 import net.sf.briar.api.clock.SystemClock;
@@ -66,68 +65,6 @@ public class DatabaseComponentImplTest extends DatabaseComponentTest {
 		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
 	protected <T> DatabaseComponent createDatabaseComponent(
 			Database<T> database, DatabaseCleaner cleaner,
diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
index 2248ef5b2d..34b9c6e6f1 100644
--- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
+++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java
@@ -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.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.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.LocalSubscriptionsUpdatedEvent;
 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.SubscriptionRemovedEvent;
 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 DatabaseListener listener = context.mock(DatabaseListener.class);
 		context.checking(new Expectations() {{
-			exactly(13).of(database).startTransaction();
+			exactly(10).of(database).startTransaction();
 			will(returnValue(txn));
-			exactly(13).of(database).commitTransaction(txn);
+			exactly(10).of(database).commitTransaction(txn);
 			// open()
 			oneOf(database).open();
 			will(returnValue(false));
@@ -135,18 +132,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 					with(any(long.class)));
 			oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
 			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)
 			oneOf(database).addLocalAuthor(txn, localAuthor);
 			// addContact(author, localAuthorId)
@@ -204,9 +189,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 
 		assertFalse(db.open());
 		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);
 		assertEquals(contactId, db.addContact(author, localAuthorId));
 		assertEquals(Arrays.asList(contact), db.getContacts());
@@ -225,114 +207,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		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
 	public void testGroupMessagesAreNotStoredUnlessSubscribed()
 			throws Exception {
@@ -401,51 +275,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).getContactIds(txn);
 			will(returnValue(Arrays.asList(contactId)));
 			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);
 		}});
 		DatabaseComponent db = createDatabaseComponent(database, cleaner,
@@ -1226,124 +1055,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 		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
 	public void testReceiveOffer() throws Exception {
 		final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -1527,11 +1238,6 @@ public abstract class DatabaseComponentTest extends BriarTestCase {
 			oneOf(database).getContactIds(txn);
 			will(returnValue(Arrays.asList(contactId)));
 			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);
 			// The message was added, so the listener should be called
 			oneOf(listener).eventOccurred(with(any(
diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
index 2edf5af408..943ed3e97e 100644
--- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
+++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
@@ -3,8 +3,6 @@ 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;
 
 import java.io.File;
@@ -153,22 +151,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		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
 	public void testUnsubscribingRemovesGroupMessage() throws Exception {
 		Database<Connection> db = open(false);
@@ -264,45 +246,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		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
 	public void testSendableGroupMessagesMustHaveSeenFlagFalse()
 			throws Exception {
@@ -316,7 +259,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addVisibility(txn, contactId, groupId);
 		db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
 		db.addGroupMessage(txn, message, false);
-		db.setSendability(txn, messageId, 1);
 
 		// The message has no status yet, so it should not be sendable
 		assertFalse(db.hasSendableMessages(txn, contactId));
@@ -353,7 +295,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addSubscription(txn, group);
 		db.addVisibility(txn, contactId, groupId);
 		db.addGroupMessage(txn, message, false);
-		db.setSendability(txn, messageId, 1);
 		db.addStatus(txn, contactId, messageId, false);
 
 		// 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.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
 		db.addGroupMessage(txn, message, false);
-		db.setSendability(txn, messageId, 1);
 		db.addStatus(txn, contactId, messageId, false);
 
 		// The message is sendable, but too large to send
@@ -423,7 +363,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.addSubscription(txn, group);
 		db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
 		db.addGroupMessage(txn, message, false);
-		db.setSendability(txn, messageId, 1);
 		db.addStatus(txn, contactId, messageId, false);
 
 		// 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.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
 		db.addGroupMessage(txn, message, false);
-		db.setSendability(txn, messageId, 1);
 		db.addStatus(txn, contactId, messageId, false);
 
 		// Retrieve the message from the database and mark it as sent
@@ -568,47 +506,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		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
 	public void testGetOldMessages() throws Exception {
 		MessageId messageId1 = new MessageId(TestUtils.getRandomId());
@@ -902,8 +799,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
 		db.addGroupMessage(txn, message, false);
 
-		// Set the sendability to > 0 and the status to seen = true
-		db.setSendability(txn, messageId, 1);
+		// Set the status to seen = true
 		db.addStatus(txn, contactId, messageId, true);
 
 		// The message is not sendable because its status is seen = true
@@ -913,31 +809,6 @@ public class H2DatabaseTest extends BriarTestCase {
 		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
 	public void testGetMessageIfSendableReturnsNullIfOld() throws Exception {
 		Database<Connection> db = open(false);
@@ -953,8 +824,7 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.setRetentionTime(txn, contactId, timestamp + 1, 1);
 		db.addGroupMessage(txn, message, false);
 
-		// Set the sendability to > 0 and the status to seen = false
-		db.setSendability(txn, messageId, 1);
+		// Set the status to seen = false
 		db.addStatus(txn, contactId, messageId, false);
 
 		// 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.addGroupMessage(txn, message, false);
 
-		// Set the sendability to > 0 and the status to seen = false
-		db.setSendability(txn, messageId, 1);
+		// Set the status to seen = false
 		db.addStatus(txn, contactId, messageId, false);
 
 		// The message is sendable so it should be returned
-- 
GitLab