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 67883406cc03e26aedf83ed1bf654008c4db3136..1b3e9bfd6e886539c6d127d6c2e216f9822002cb 100644
--- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
+++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java
@@ -162,6 +162,12 @@ public interface DatabaseComponent {
 	Collection<TransportUpdate> generateTransportUpdates(ContactId c,
 			long maxLatency) throws DbException;
 
+	/**
+	 * Returns any groups that contacts have made visible but to which the user
+	 * does not subscribe.
+	 */
+	Collection<Group> getAvailableGroups() throws DbException;
+
 	/** Returns the configuration for the given transport. */
 	TransportConfig getConfig(TransportId t) throws DbException;
 
diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java
index 05dce999c77cba3b27b7ff14910ca1ee02dec1ad..f1031d7a9214628516aa8e1952473a51c212fea8 100644
--- a/briar-core/src/net/sf/briar/db/Database.java
+++ b/briar-core/src/net/sf/briar/db/Database.java
@@ -219,6 +219,12 @@ interface Database<T> {
 	boolean containsVisibleSubscription(T txn, ContactId c, GroupId g)
 			throws DbException;
 
+	/**
+	 * Returns any groups that contacts have made visible but to which the user
+	 * does not subscribe.
+	 */
+	Collection<Group> getAvailableGroups(T txn) throws DbException;
+
 	/**
 	 * Returns the configuration for the given transport.
 	 * <p>
diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
index f830fe233b8f68b861ff6f934d5b5270f6c83c3c..45568d4d61025c374818f49791a207c7c663b74c 100644
--- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
+++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java
@@ -883,6 +883,23 @@ DatabaseCleaner.Callback {
 		}
 	}
 
+	public Collection<Group> getAvailableGroups() throws DbException {
+		subscriptionLock.readLock().lock();
+		try {
+			T txn = db.startTransaction();
+			try {
+				Collection<Group> groups = db.getAvailableGroups(txn);
+				db.commitTransaction(txn);
+				return groups;
+			} catch(DbException e) {
+				db.abortTransaction(txn);
+				throw e;
+			}
+		} finally {
+			subscriptionLock.readLock().unlock();
+		}
+	}
+
 	public TransportConfig getConfig(TransportId t) throws DbException {
 		transportLock.readLock().lock();
 		try {
diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
index 8e3033e88c61a3daaf5dc7799399a8a4baa27591..2aa42ee656e1d4622fcc1894563ec0971b06eb06 100644
--- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java
+++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java
@@ -1146,6 +1146,36 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public Collection<Group> getAvailableGroups(Connection txn)
+			throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT cg.groupId, cg.name, cg.publicKey"
+					+ " FROM contactGroups AS cg"
+					+ " LEFT OUTER JOIN groups AS g"
+					+ " ON cg.groupId = g.groupId"
+					+ " WHERE g.groupId IS NULL"
+					+ " GROUP BY cg.groupId";
+			ps = txn.prepareStatement(sql);
+			rs = ps.executeQuery();
+			List<Group> groups = new ArrayList<Group>();
+			while(rs.next()) {
+				GroupId id = new GroupId(rs.getBytes(1));
+				String name = rs.getString(2);
+				byte[] publicKey = rs.getBytes(3);
+				groups.add(new Group(id, name, publicKey));
+			}
+			rs.close();
+			ps.close();
+			return Collections.unmodifiableList(groups);
+		} catch(SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public TransportConfig getConfig(Connection txn, TransportId t)
 			throws DbException {
 		PreparedStatement ps = null;
diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
index b461ad1e3a9869607a26bcbe9b0d2e9891377c3d..1fae6c77fb45575705a90d1b040c721508d10733 100644
--- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
+++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java
@@ -1817,6 +1817,29 @@ public class H2DatabaseTest extends BriarTestCase {
 		db.close();
 	}
 
+	@Test
+	public void testGetAvailableGroups() 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.setSubscriptions(txn, contactId, Arrays.asList(group), 1);
+
+		// The group should be available
+		assertEquals(Collections.emptyList(), db.getSubscriptions(txn));
+		assertEquals(Arrays.asList(group), db.getAvailableGroups(txn));
+
+		// Subscribe to the group - it should no longer be available
+		db.addSubscription(txn, group);
+		assertEquals(Arrays.asList(group), db.getSubscriptions(txn));
+		assertEquals(Collections.emptyList(), db.getAvailableGroups(txn));
+
+		db.commitTransaction(txn);
+		db.close();
+	}
+
 	@Test
 	public void testExceptionHandling() throws Exception {
 		Database<Connection> db = open(false);