diff --git a/api/net/sf/briar/api/protocol/MessageHeaderFactory.java b/api/net/sf/briar/api/protocol/MessageHeaderFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9f51a46c559a428cee24a639112703b5b321e49
--- /dev/null
+++ b/api/net/sf/briar/api/protocol/MessageHeaderFactory.java
@@ -0,0 +1,7 @@
+package net.sf.briar.api.protocol;
+
+public interface MessageHeaderFactory {
+
+	MessageHeader createMessageHeader(MessageId id, MessageId parent,
+			GroupId group, AuthorId author, String subject, long timestamp);
+}
diff --git a/components/net/sf/briar/db/Database.java b/components/net/sf/briar/db/Database.java
index 9a7eaf6beafec274381dfdfeade182f1fc31a91e..796c163c619b5ca531d140e0dffb983c849d221d 100644
--- a/components/net/sf/briar/db/Database.java
+++ b/components/net/sf/briar/db/Database.java
@@ -16,6 +16,7 @@ import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
+import net.sf.briar.api.protocol.MessageHeader;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.transport.ConnectionWindow;
 
@@ -252,6 +253,14 @@ interface Database<T> {
 	 */
 	byte[] getMessageBody(T txn, MessageId m) throws DbException;
 
+	/**
+	 * Returns the headers of all messages in the given group.
+	 * <p>
+	 * Locking: messages read.
+	 */
+	Collection<MessageHeader> getMessageHeaders(T txn, GroupId g)
+	throws DbException;
+
 	/**
 	 * Returns the message identified by the given ID, in raw format, or null
 	 * if the message is not present in the database or is not sendable to the
diff --git a/components/net/sf/briar/db/DatabaseModule.java b/components/net/sf/briar/db/DatabaseModule.java
index 903fb43458772d61ca785b05757753a4412554a0..d6ec12810da252d6ebe2d0291b9cd55fc02619fc 100644
--- a/components/net/sf/briar/db/DatabaseModule.java
+++ b/components/net/sf/briar/db/DatabaseModule.java
@@ -9,6 +9,7 @@ import net.sf.briar.api.db.DatabaseDirectory;
 import net.sf.briar.api.db.DatabaseMaxSize;
 import net.sf.briar.api.db.DatabasePassword;
 import net.sf.briar.api.protocol.GroupFactory;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
 import net.sf.briar.api.transport.ConnectionWindowFactory;
 
 import com.google.inject.AbstractModule;
@@ -26,9 +27,10 @@ public class DatabaseModule extends AbstractModule {
 	Database<Connection> getDatabase(@DatabaseDirectory File dir,
 			@DatabasePassword Password password, @DatabaseMaxSize long maxSize,
 			ConnectionWindowFactory connectionWindowFactory,
-			GroupFactory groupFactory) {
+			GroupFactory groupFactory,
+			MessageHeaderFactory messageHeaderFactory) {
 		return new H2Database(dir, password, maxSize, connectionWindowFactory,
-				groupFactory);
+				groupFactory, messageHeaderFactory);
 	}
 
 	@Provides @Singleton
diff --git a/components/net/sf/briar/db/H2Database.java b/components/net/sf/briar/db/H2Database.java
index 18aff631c6e31920983fce34d1f7f7973d3eae07..4b116ee32364a9f8280d599418ec65b0e1b3a766 100644
--- a/components/net/sf/briar/db/H2Database.java
+++ b/components/net/sf/briar/db/H2Database.java
@@ -16,6 +16,7 @@ import net.sf.briar.api.db.DatabaseMaxSize;
 import net.sf.briar.api.db.DatabasePassword;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.protocol.GroupFactory;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
 import net.sf.briar.api.transport.ConnectionWindowFactory;
 
 import org.apache.commons.io.FileSystemUtils;
@@ -37,9 +38,10 @@ class H2Database extends JdbcDatabase {
 	H2Database(@DatabaseDirectory File dir, @DatabasePassword Password password,
 			@DatabaseMaxSize long maxSize,
 			ConnectionWindowFactory connectionWindowFactory,
-			GroupFactory groupFactory) {
-		super(connectionWindowFactory, groupFactory, "BINARY(32)", "BINARY",
-				"INT NOT NULL AUTO_INCREMENT");
+			GroupFactory groupFactory,
+			MessageHeaderFactory messageHeaderFactory) {
+		super(connectionWindowFactory, groupFactory, messageHeaderFactory,
+				"BINARY(32)", "BINARY", "INT NOT NULL AUTO_INCREMENT");
 		home = new File(dir, "db");
 		this.password = password;
 		url = "jdbc:h2:split:" + home.getPath()
diff --git a/components/net/sf/briar/db/JdbcDatabase.java b/components/net/sf/briar/db/JdbcDatabase.java
index 64c3ba450bc92a8d6ca1c0083a1b7a2e49c38c1e..07b244a77cc745d7c32ea4acab57696643e278c0 100644
--- a/components/net/sf/briar/db/JdbcDatabase.java
+++ b/components/net/sf/briar/db/JdbcDatabase.java
@@ -30,6 +30,8 @@ import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
+import net.sf.briar.api.protocol.MessageHeader;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.transport.ConnectionWindow;
 import net.sf.briar.api.transport.ConnectionWindowFactory;
@@ -230,6 +232,7 @@ abstract class JdbcDatabase implements Database<Connection> {
 	private final String hashType, binaryType, counterType;
 	private final ConnectionWindowFactory connectionWindowFactory;
 	private final GroupFactory groupFactory;
+	private final MessageHeaderFactory messageHeaderFactory;
 	private final LinkedList<Connection> connections =
 		new LinkedList<Connection>(); // Locking: self
 
@@ -239,10 +242,12 @@ abstract class JdbcDatabase implements Database<Connection> {
 	protected abstract Connection createConnection() throws SQLException;
 
 	JdbcDatabase(ConnectionWindowFactory connectionWindowFactory,
-			GroupFactory groupFactory, String hashType, String binaryType,
-			String counterType) {
+			GroupFactory groupFactory,
+			MessageHeaderFactory messageHeaderFactory,
+			String hashType, String binaryType, String counterType) {
 		this.connectionWindowFactory = connectionWindowFactory;
 		this.groupFactory = groupFactory;
+		this.messageHeaderFactory = messageHeaderFactory;
 		this.hashType = hashType;
 		this.binaryType = binaryType;
 		this.counterType = counterType;
@@ -1094,6 +1099,37 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	public Collection<MessageHeader> getMessageHeaders(Connection txn,
+			GroupId g) throws DbException {
+		PreparedStatement ps = null;
+		ResultSet rs = null;
+		try {
+			String sql = "SELECT messageId, parentId, authorId, subject,"
+				+ " timestamp FROM messages"
+				+ " WHERE groupId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setBytes(1, g.getBytes());
+			rs = ps.executeQuery();
+			Collection<MessageHeader> headers = new ArrayList<MessageHeader>();
+			while(rs.next()) {
+				MessageId id = new MessageId(rs.getBytes(1));
+				MessageId parent = new MessageId(rs.getBytes(2));
+				AuthorId author = new AuthorId(rs.getBytes(3));
+				String subject = rs.getString(4);
+				long timestamp = rs.getLong(5);
+				headers.add(messageHeaderFactory.createMessageHeader(id, parent,
+						g, author, subject, timestamp));
+			}
+			rs.close();
+			ps.close();
+			return headers;
+		} catch(SQLException e) {
+			tryToClose(rs);
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	public byte[] getMessageIfSendable(Connection txn, ContactId c, MessageId m)
 	throws DbException {
 		PreparedStatement ps = null;
diff --git a/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java b/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..dbe2abadf15268ce7cd1c6571cf7b36d4282528f
--- /dev/null
+++ b/components/net/sf/briar/protocol/MessageHeaderFactoryImpl.java
@@ -0,0 +1,16 @@
+package net.sf.briar.protocol;
+
+import net.sf.briar.api.protocol.AuthorId;
+import net.sf.briar.api.protocol.GroupId;
+import net.sf.briar.api.protocol.MessageHeader;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
+import net.sf.briar.api.protocol.MessageId;
+
+class MessageHeaderFactoryImpl implements MessageHeaderFactory {
+
+	public MessageHeader createMessageHeader(MessageId id, MessageId parent,
+			GroupId group, AuthorId author, String subject, long timestamp) {
+		return new MessageHeaderImpl(id, parent, group, author, subject,
+				timestamp);
+	}
+}
diff --git a/components/net/sf/briar/protocol/MessageHeaderImpl.java b/components/net/sf/briar/protocol/MessageHeaderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..b173c4f4e44f6c498cc8ce08552f065e4cd3b05a
--- /dev/null
+++ b/components/net/sf/briar/protocol/MessageHeaderImpl.java
@@ -0,0 +1,49 @@
+package net.sf.briar.protocol;
+
+import net.sf.briar.api.protocol.AuthorId;
+import net.sf.briar.api.protocol.GroupId;
+import net.sf.briar.api.protocol.MessageHeader;
+import net.sf.briar.api.protocol.MessageId;
+
+class MessageHeaderImpl implements MessageHeader {
+
+	private final MessageId id, parent;
+	private final GroupId group;
+	private final AuthorId author;
+	private final String subject;
+	private final long timestamp;
+
+	MessageHeaderImpl(MessageId id, MessageId parent, GroupId group,
+			AuthorId author, String subject, long timestamp) {
+		this.id = id;
+		this.parent = parent;
+		this.group = group;
+		this.author = author;
+		this.subject = subject;
+		this.timestamp = timestamp;
+	}
+
+	public MessageId getId() {
+		return id;
+	}
+
+	public MessageId getParent() {
+		return parent;
+	}
+
+	public GroupId getGroup() {
+		return group;
+	}
+
+	public AuthorId getAuthor() {
+		return author;
+	}
+
+	public String getSubject() {
+		return subject;
+	}
+
+	public long getTimestamp() {
+		return timestamp;
+	}
+}
diff --git a/components/net/sf/briar/protocol/ProtocolModule.java b/components/net/sf/briar/protocol/ProtocolModule.java
index 6d36d8efb38cc18522aa7a1f9d5a7f83dcb95c53..4ced04e08367332466509aab4ea451e54d56dea4 100644
--- a/components/net/sf/briar/protocol/ProtocolModule.java
+++ b/components/net/sf/briar/protocol/ProtocolModule.java
@@ -10,6 +10,7 @@ import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageEncoder;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.Offer;
 import net.sf.briar.api.protocol.ProtocolReaderFactory;
@@ -30,6 +31,7 @@ public class ProtocolModule extends AbstractModule {
 		bind(BatchFactory.class).to(BatchFactoryImpl.class);
 		bind(GroupFactory.class).to(GroupFactoryImpl.class);
 		bind(MessageEncoder.class).to(MessageEncoderImpl.class);
+		bind(MessageHeaderFactory.class).to(MessageHeaderFactoryImpl.class);
 		bind(OfferFactory.class).to(OfferFactoryImpl.class);
 		bind(ProtocolReaderFactory.class).to(ProtocolReaderFactoryImpl.class);
 		bind(RequestFactory.class).to(RequestFactoryImpl.class);
diff --git a/test/net/sf/briar/db/H2DatabaseTest.java b/test/net/sf/briar/db/H2DatabaseTest.java
index 2e8691ede953e63f1307dd1a41dde83810779490..29792f445181bacd492c7e02ac54e4dc0e161220 100644
--- a/test/net/sf/briar/db/H2DatabaseTest.java
+++ b/test/net/sf/briar/db/H2DatabaseTest.java
@@ -33,6 +33,7 @@ import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.Message;
+import net.sf.briar.api.protocol.MessageHeaderFactory;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.transport.ConnectionWindow;
 import net.sf.briar.api.transport.ConnectionWindowFactory;
@@ -61,6 +62,7 @@ public class H2DatabaseTest extends TestCase {
 	private final Random random = new Random();
 	private final ConnectionWindowFactory connectionWindowFactory;
 	private final GroupFactory groupFactory;
+	private final MessageHeaderFactory messageHeaderFactory;
 
 	private final AuthorId authorId;
 	private final BatchId batchId;
@@ -87,6 +89,7 @@ public class H2DatabaseTest extends TestCase {
 				new TransportModule(), new TestDatabaseModule(testDir));
 		connectionWindowFactory = i.getInstance(ConnectionWindowFactory.class);
 		groupFactory = i.getInstance(GroupFactory.class);
+		messageHeaderFactory = i.getInstance(MessageHeaderFactory.class);
 		authorId = new AuthorId(TestUtils.getRandomId());
 		batchId = new BatchId(TestUtils.getRandomId());
 		contactId = new ContactId(1);
@@ -1665,7 +1668,7 @@ public class H2DatabaseTest extends TestCase {
 
 	private Database<Connection> open(boolean resume) throws Exception {
 		Database<Connection> db = new H2Database(testDir, password, MAX_SIZE,
-				connectionWindowFactory, groupFactory);
+				connectionWindowFactory, groupFactory, messageHeaderFactory);
 		db.open(resume);
 		return db;
 	}