diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java
index e269178b9ef739aa90e219d7ce65952fbd2dfaad..aa46a20bf58abc3310c28ae83949b086e59fbd6d 100644
--- a/briar-core/src/org/briarproject/db/Database.java
+++ b/briar-core/src/org/briarproject/db/Database.java
@@ -703,7 +703,7 @@ interface Database<T> {
 	 * true, unless an update with an equal or higher version number has
 	 * already been received from the contact.
 	 * <p>
-	 * Locking: subscription write.
+	 * Locking: message write, subscription write.
 	 */
 	boolean setGroups(T txn, ContactId c, Collection<Group> groups,
 			long version) throws DbException;
diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
index c7f375fe6b7fe81285774c7bf0aa77eadbc4cc52..a963367c957006523e9df03173865e37e4b841c7 100644
--- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
+++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java
@@ -1500,22 +1500,27 @@ DatabaseCleaner.Callback {
 		boolean updated;
 		contactLock.readLock().lock();
 		try {
-			subscriptionLock.writeLock().lock();
+			messageLock.writeLock().lock();
 			try {
-				T txn = db.startTransaction();
+				subscriptionLock.writeLock().lock();
 				try {
-					if(!db.containsContact(txn, c))
-						throw new NoSuchContactException();
-					Collection<Group> groups = u.getGroups();
-					long version = u.getVersion();
-					updated = db.setGroups(txn, c, groups, version);
-					db.commitTransaction(txn);
-				} catch(DbException e) {
-					db.abortTransaction(txn);
-					throw e;
+					T txn = db.startTransaction();
+					try {
+						if(!db.containsContact(txn, c))
+							throw new NoSuchContactException();
+						Collection<Group> groups = u.getGroups();
+						long version = u.getVersion();
+						updated = db.setGroups(txn, c, groups, version);
+						db.commitTransaction(txn);
+					} catch(DbException e) {
+						db.abortTransaction(txn);
+						throw e;
+					}
+				} finally {
+					subscriptionLock.writeLock().unlock();
 				}
 			} finally {
-				subscriptionLock.writeLock().unlock();
+				messageLock.writeLock().unlock();
 			}
 		} finally {
 			contactLock.readLock().unlock();
diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java
index 2d6a267498015b2c2d0b0d011872f41857f58134..d8624dc56c4a3499274afb828a49045a97da881e 100644
--- a/briar-core/src/org/briarproject/db/JdbcDatabase.java
+++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java
@@ -21,10 +21,12 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Logger;
 
@@ -2896,8 +2898,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 			throws DbException {
 		PreparedStatement ps = null;
 		try {
-			String sql = "UPDATE statuses"
-					+ " SET expiry = 0, txCount = 0"
+			String sql = "UPDATE statuses SET expiry = 0, txCount = 0"
 					+ " WHERE messageId = ? AND contactId = ?";
 			ps = txn.prepareStatement(sql);
 			ps.setBytes(1, m.getBytes());
@@ -2949,6 +2950,43 @@ abstract class JdbcDatabase implements Database<Connection> {
 			ps.close();
 			// Return false if the update is obsolete
 			if(affected == 0) return false;
+			// Find any messages in groups that are being removed
+			Set<GroupId> newIds = new HashSet<GroupId>();
+			for(Group g : groups) newIds.add(g.getId());
+			sql = "SELECT messageId, m.groupId"
+					+ " FROM messages AS m"
+					+ " JOIN contactGroups AS cg"
+					+ " ON m.groupId = cg.groupId"
+					+ " WHERE contactId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setInt(1, c.getInt());
+			rs = ps.executeQuery();
+			List<MessageId> removed = new ArrayList<MessageId>();
+			while(rs.next()) {
+				if(!newIds.contains(new GroupId(rs.getBytes(2))))
+					removed.add(new MessageId(rs.getBytes(1)));
+			}
+			rs.close();
+			ps.close();
+			// Reset any statuses for messages in groups that are being removed
+			if(!removed.isEmpty()) {
+				sql = "UPDATE statuses SET ack = FALSE, seen = FALSE,"
+						+ " requested = FALSE, expiry = 0, txCount = 0"
+						+ " WHERE contactId = ? AND messageId = ?";
+				ps = txn.prepareStatement(sql);
+				ps.setInt(1, c.getInt());
+				for(MessageId m : removed) {
+					ps.setBytes(2, m.getBytes());
+					ps.addBatch();
+				}
+				int[] batchAffected = ps.executeBatch();
+				if(batchAffected.length != removed.size())
+					throw new DbStateException();
+				for(int i = 0; i < batchAffected.length; i++) {
+					if(batchAffected[i] < 0) throw new DbStateException();
+				}
+				ps.close();
+			}
 			// Delete the existing subscriptions, if any
 			sql = "DELETE FROM contactGroups WHERE contactId = ?";
 			ps = txn.prepareStatement(sql);
diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
index 3b4e344cb2f38f9bfb9b33746c926e037e216806..361e9ff3ef87de13e643e82767bc2ea99ac33372 100644
--- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
+++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java
@@ -1577,6 +1577,46 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.close();
 	}
 
+	@Test
+	public void testContactUnsubscribingResetsMessageStatus() throws Exception {
+		Database<Connection> db = open(false);
+		Connection txn = db.startTransaction();
+
+		// Add a contact who subscribes to a group
+		db.addLocalAuthor(txn, localAuthor);
+		assertEquals(contactId, db.addContact(txn, author, localAuthorId));
+		db.setGroups(txn, contactId, Arrays.asList(group), 1);
+
+		// Subscribe to the group and make it visible to the contact
+		db.addGroup(txn, group);
+		db.addVisibility(txn, contactId, groupId);
+
+		// Add a message - it should be sendable to the contact
+		db.addMessage(txn, message, true);
+		db.addStatus(txn, contactId, messageId, false, false);
+		Collection<MessageId> sendable = db.getMessagesToSend(txn, contactId,
+				ONE_MEGABYTE);
+		assertEquals(Arrays.asList(messageId), sendable);
+
+		// Mark the message as seen - it should no longer be sendable
+		db.raiseSeenFlag(txn, contactId, messageId);
+		sendable = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
+		assertEquals(Collections.emptyList(), sendable);
+
+		// The contact unsubscribes - the message should not be sendable
+		db.setGroups(txn, contactId, Collections.<Group>emptyList(), 2);
+		sendable = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
+		assertEquals(Collections.emptyList(), sendable);
+
+		// The contact resubscribes - the message should be sendable again
+		db.setGroups(txn, contactId, Arrays.asList(group), 3);
+		sendable = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE);
+		assertEquals(Arrays.asList(messageId), sendable);
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
 	@Test
 	public void testExceptionHandling() throws Exception {
 		Database<Connection> db = open(false);