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 fbfba41c33f27a069565af1bc8b0a6643c3de4a3..e301eea38b74887faaad1d49fd56565516090cca 100644 --- a/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java +++ b/briar-api/src/net/sf/briar/api/db/DatabaseComponent.java @@ -166,11 +166,13 @@ public interface DatabaseComponent { /** Returns the body of the message with the given ID. */ byte[] getMessageBody(MessageId m) throws DbException; - /** Returns the header of the message with the given ID. */ - MessageHeader getMessageHeader(MessageId m) throws DbException; - /** Returns the headers of all messages in the given group. */ - Collection<MessageHeader> getMessageHeaders(GroupId g) throws DbException; + Collection<GroupMessageHeader> getMessageHeaders(GroupId g) + throws DbException; + + /** Returns the headers of all private messages. */ + Collection<PrivateMessageHeader> getPrivateMessageHeaders() + throws DbException; /** Returns the user's rating for the given author. */ Rating getRating(AuthorId a) throws DbException; diff --git a/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java b/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..af8add02b337bac51c6ced34c8614dd34c8e4425 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/db/GroupMessageHeader.java @@ -0,0 +1,31 @@ +package net.sf.briar.api.db; + +import net.sf.briar.api.messaging.Author; +import net.sf.briar.api.messaging.GroupId; +import net.sf.briar.api.messaging.MessageId; + +public class GroupMessageHeader extends MessageHeader { + + private final GroupId groupId; + private final Author author; + + public GroupMessageHeader(MessageId id, MessageId parent, String subject, + long timestamp, boolean read, boolean starred, GroupId groupId, + Author author) { + super(id, parent, subject, timestamp, read, starred); + this.groupId = groupId; + this.author = author; + } + + /** Returns the ID of the group to which the message belongs. */ + public GroupId getGroupId() { + return groupId; + } + + /** + * Returns the message's author, or null if this is an anonymous message. + */ + public Author getAuthor() { + return author; + } +} 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 7d9190227708a906ba7f389f8295c176c118e234..b1f11d1bd549b5add829b7dbd69af065b3121d24 100644 --- a/briar-api/src/net/sf/briar/api/db/MessageHeader.java +++ b/briar-api/src/net/sf/briar/api/db/MessageHeader.java @@ -1,25 +1,18 @@ package net.sf.briar.api.db; -import net.sf.briar.api.messaging.AuthorId; -import net.sf.briar.api.messaging.GroupId; import net.sf.briar.api.messaging.MessageId; -public class MessageHeader { +public abstract class MessageHeader { private final MessageId id, parent; - private final GroupId group; - private final AuthorId author; private final String subject; private final long timestamp; private final boolean read, starred; - public MessageHeader(MessageId id, MessageId parent, GroupId group, - AuthorId author, String subject, long timestamp, boolean read, - boolean starred) { + protected MessageHeader(MessageId id, MessageId parent, String subject, + long timestamp, boolean read, boolean starred) { this.id = id; this.parent = parent; - this.group = group; - this.author = author; this.subject = subject; this.timestamp = timestamp; this.read = read; @@ -39,22 +32,6 @@ public class MessageHeader { return parent; } - /** - * Returns the ID of the group to which the message belongs, or null if - * this is a private message. - */ - public GroupId getGroup() { - return group; - } - - /** - * Returns the ID of the message's author, or null if this is an - * anonymous message. - */ - public AuthorId getAuthor() { - return author; - } - /** Returns the message's subject line. */ public String getSubject() { return subject; diff --git a/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java b/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..900c2cb2a92fa2c43c196594694ed8659d8381a2 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/db/PrivateMessageHeader.java @@ -0,0 +1,31 @@ +package net.sf.briar.api.db; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.messaging.MessageId; + +public class PrivateMessageHeader extends MessageHeader { + + private final ContactId contactId; + private final boolean incoming; + + public PrivateMessageHeader(MessageId id, MessageId parent, String subject, + long timestamp, boolean read, boolean starred, ContactId contactId, + boolean incoming) { + super(id, parent, subject, timestamp, read, starred); + this.contactId = contactId; + this.incoming = incoming; + } + + /** + * Returns the ID of the contact who is the sender (if incoming) or + * recipient (if outgoing) of this message. + */ + public ContactId getContactId() { + return contactId; + } + + /** Returns true if this is an incoming message. */ + public boolean isIncoming() { + return incoming; + } +} diff --git a/briar-api/src/net/sf/briar/api/db/event/LocalRetentionTimeUpdatedEvent.java b/briar-api/src/net/sf/briar/api/db/event/LocalRetentionTimeUpdatedEvent.java deleted file mode 100644 index c28ce107552f56a9d2bebac1e5adbeff6b351d9e..0000000000000000000000000000000000000000 --- a/briar-api/src/net/sf/briar/api/db/event/LocalRetentionTimeUpdatedEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.sf.briar.api.db.event; - -/** - * An event that is broadcast when the retention time of the local database - * changes. - */ -public class LocalRetentionTimeUpdatedEvent extends DatabaseEvent { - -} diff --git a/briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java b/briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..5f130f78a7f5fb00f80c0197549168c2d69c2e02 --- /dev/null +++ b/briar-api/src/net/sf/briar/api/db/event/MessageExpiredEvent.java @@ -0,0 +1,9 @@ +package net.sf.briar.api.db.event; + +/** + * An event that is broadcast when one or messages expire from the database, + * potentially changing the database's retention time. + */ +public class MessageExpiredEvent extends DatabaseEvent { + +} diff --git a/briar-api/src/net/sf/briar/api/messaging/Message.java b/briar-api/src/net/sf/briar/api/messaging/Message.java index 5140ffc0e93a5919e83d3d6a1b725ca94b6aeafd..f2aac57fba2adfc47b144e07a5954398075d780e 100644 --- a/briar-api/src/net/sf/briar/api/messaging/Message.java +++ b/briar-api/src/net/sf/briar/api/messaging/Message.java @@ -12,16 +12,16 @@ public interface Message { MessageId getParent(); /** - * Returns the identifier of the {@link Group} to which the message - * belongs, or null if this is a private message. + * Returns the {@link Group} to which the message belongs, or null if this + * is a private message. */ - GroupId getGroup(); + Group getGroup(); /** - * Returns the identifier of the message's {@link Author}, or null if this - * is an anonymous message. + * Returns the message's {@link Author}, or null if this is an anonymous + * message. */ - AuthorId getAuthor(); + Author getAuthor(); /** Returns the message's subject line. */ String getSubject(); diff --git a/briar-core/src/net/sf/briar/db/Database.java b/briar-core/src/net/sf/briar/db/Database.java index 7fd09fa9a1856bd80dab1142abf66c2b234f0d98..f735dcc7654f1e4b3f80d52024a1b3498e4be27c 100644 --- a/briar-core/src/net/sf/briar/db/Database.java +++ b/briar-core/src/net/sf/briar/db/Database.java @@ -10,7 +10,8 @@ import net.sf.briar.api.Rating; import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.db.DbException; -import net.sf.briar.api.db.MessageHeader; +import net.sf.briar.api.db.GroupMessageHeader; +import net.sf.briar.api.db.PrivateMessageHeader; import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.GroupId; @@ -264,18 +265,19 @@ interface Database<T> { byte[] getMessageBody(T txn, MessageId m) throws DbException; /** - * Returns the header of the message identified by the given ID. + * Returns the headers of all messages in the given group. * <p> * Locking: message read. */ - MessageHeader getMessageHeader(T txn, MessageId m) throws DbException; + Collection<GroupMessageHeader> getMessageHeaders(T txn, GroupId g) + throws DbException; /** - * Returns the headers of all messages in the given group. + * Returns the headers of all private messages. * <p> * Locking: message read. */ - Collection<MessageHeader> getMessageHeaders(T txn, GroupId g) + Collection<PrivateMessageHeader> getPrivateMessageHeaders(T txn) throws DbException; /** diff --git a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java index c4455971a9c5b4da12764d185b100f1ecd26209a..7cb268aaef2d1fa4e64a978fa64e0757b670dd51 100644 --- a/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java +++ b/briar-core/src/net/sf/briar/db/DatabaseComponentImpl.java @@ -30,19 +30,20 @@ import net.sf.briar.api.TransportProperties; import net.sf.briar.api.clock.Clock; import net.sf.briar.api.db.DatabaseComponent; import net.sf.briar.api.db.DbException; -import net.sf.briar.api.db.MessageHeader; +import net.sf.briar.api.db.GroupMessageHeader; import net.sf.briar.api.db.NoSuchContactException; import net.sf.briar.api.db.NoSuchMessageException; import net.sf.briar.api.db.NoSuchSubscriptionException; import net.sf.briar.api.db.NoSuchTransportException; +import net.sf.briar.api.db.PrivateMessageHeader; import net.sf.briar.api.db.event.ContactAddedEvent; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalRetentionTimeUpdatedEvent; import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; +import net.sf.briar.api.db.event.MessageExpiredEvent; import net.sf.briar.api.db.event.MessageReceivedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; import net.sf.briar.api.db.event.RemoteRetentionTimeUpdatedEvent; @@ -52,6 +53,7 @@ import net.sf.briar.api.db.event.TransportAddedEvent; import net.sf.briar.api.db.event.TransportRemovedEvent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.messaging.Ack; +import net.sf.briar.api.messaging.Author; import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.GroupId; @@ -258,7 +260,7 @@ DatabaseCleaner.Callback { try { // Don't store the message if the user has // unsubscribed from the group - if(db.containsSubscription(txn, m.getGroup())) + if(db.containsSubscription(txn, m.getGroup().getId())) added = storeGroupMessage(txn, m, null); db.commitTransaction(txn); } catch(DbException e) { @@ -318,8 +320,8 @@ DatabaseCleaner.Callback { private int calculateSendability(T txn, Message m) throws DbException { int sendability = 0; // One point for a good rating - AuthorId a = m.getAuthor(); - if(a != null && db.getRating(txn, a) == GOOD) sendability++; + 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; @@ -445,10 +447,10 @@ DatabaseCleaner.Callback { } /** + * If the given message is already in the database, returns false. * Otherwise stores the message and marks it as new or seen with respect to * the given contact, depending on whether the message is outgoing or - * incoming, respectively; or returns false if the message is already in - * the database. + * incoming, respectively. * <p> * Locking: contact read, message write. */ @@ -457,9 +459,7 @@ DatabaseCleaner.Callback { if(m.getGroup() != null) throw new IllegalArgumentException(); if(m.getAuthor() != null) throw new IllegalArgumentException(); if(!db.addPrivateMessage(txn, m, c)) return false; - MessageId id = m.getId(); - if(incoming) db.addStatus(txn, c, id, true); - else db.addStatus(txn, c, id, false); + db.addStatus(txn, c, m.getId(), incoming); // Count the bytes stored synchronized(spaceLock) { bytesStoredSinceLastCheck += m.getSerialised().length; @@ -878,16 +878,18 @@ DatabaseCleaner.Callback { } } - public MessageHeader getMessageHeader(MessageId m) throws DbException { + public Collection<GroupMessageHeader> getMessageHeaders(GroupId g) + throws DbException { messageLock.readLock().lock(); try { T txn = db.startTransaction(); try { - if(!db.containsMessage(txn, m)) - throw new NoSuchMessageException(); - MessageHeader h = db.getMessageHeader(txn, m); + if(!db.containsSubscription(txn, g)) + throw new NoSuchSubscriptionException(); + Collection<GroupMessageHeader> headers = + db.getMessageHeaders(txn, g); db.commitTransaction(txn); - return h; + return headers; } catch(DbException e) { db.abortTransaction(txn); throw e; @@ -897,16 +899,14 @@ DatabaseCleaner.Callback { } } - public Collection<MessageHeader> getMessageHeaders(GroupId g) + public Collection<PrivateMessageHeader> getPrivateMessageHeaders() throws DbException { messageLock.readLock().lock(); try { T txn = db.startTransaction(); try { - if(!db.containsSubscription(txn, g)) - throw new NoSuchSubscriptionException(); - Collection<MessageHeader> headers = - db.getMessageHeaders(txn, g); + Collection<PrivateMessageHeader> headers = + db.getPrivateMessageHeaders(txn); db.commitTransaction(txn); return headers; } catch(DbException e) { @@ -1285,9 +1285,9 @@ DatabaseCleaner.Callback { private boolean storeMessage(T txn, ContactId c, Message m) throws DbException { if(m.getTimestamp() > clock.currentTimeMillis()) return false; - GroupId g = m.getGroup(); + Group g = m.getGroup(); if(g == null) return storePrivateMessage(txn, m, c, true); - if(!db.containsVisibleSubscription(txn, c, g)) return false; + if(!db.containsVisibleSubscription(txn, c, g.getId())) return false; return storeGroupMessage(txn, m, c); } @@ -1846,7 +1846,7 @@ DatabaseCleaner.Callback { } finally { contactLock.readLock().unlock(); } - if(removed) callListeners(new LocalRetentionTimeUpdatedEvent()); + if(removed) callListeners(new MessageExpiredEvent()); return removed; } diff --git a/briar-core/src/net/sf/briar/db/JdbcDatabase.java b/briar-core/src/net/sf/briar/db/JdbcDatabase.java index 0ea09d93ddf8585ba28f30e2904bb010afc69cb8..4be4420fd19ad1c4230d0e45868dd91ce18305c9 100644 --- a/briar-core/src/net/sf/briar/db/JdbcDatabase.java +++ b/briar-core/src/net/sf/briar/db/JdbcDatabase.java @@ -1,6 +1,7 @@ package net.sf.briar.db; import static java.sql.Types.BINARY; +import static java.sql.Types.VARCHAR; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static net.sf.briar.api.Rating.UNRATED; @@ -34,7 +35,9 @@ import net.sf.briar.api.TransportProperties; import net.sf.briar.api.clock.Clock; import net.sf.briar.api.db.DbClosedException; import net.sf.briar.api.db.DbException; -import net.sf.briar.api.db.MessageHeader; +import net.sf.briar.api.db.GroupMessageHeader; +import net.sf.briar.api.db.PrivateMessageHeader; +import net.sf.briar.api.messaging.Author; import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.GroupId; @@ -117,7 +120,9 @@ abstract class JdbcDatabase implements Database<Connection> { + " (messageId HASH NOT NULL," + " parentId HASH," // Null for the first msg in a thread + " groupId HASH," // Null for private messages - + " authorId HASH," // Null for private or anonymous msgs + + " authorId HASH," // Null for private/anon messages + + " authorName VARCHAR," // Null for private/anon messages + + " authorKey VARCHAR," // Null for private/anon messages + " subject VARCHAR NOT NULL," + " timestamp BIGINT NOT NULL," + " length INT NOT NULL," @@ -620,29 +625,38 @@ abstract class JdbcDatabase implements Database<Connection> { public boolean addGroupMessage(Connection txn, Message m) throws DbException { - assert m.getGroup() != null; + if(m.getGroup() == null) throw new IllegalArgumentException(); if(containsMessage(txn, m.getId())) return false; PreparedStatement ps = null; try { String sql = "INSERT INTO messages (messageId, parentId, groupId," - + " authorId, subject, timestamp, length, bodyStart," - + " bodyLength, raw, sendability, read, starred)" - + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO(), FALSE," - + " FALSE)"; + + " authorId, authorName, authorKey, subject, timestamp," + + " length, bodyStart, bodyLength, raw, sendability, read," + + " starred)" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ZERO()," + + " FALSE, FALSE)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getId().getBytes()); if(m.getParent() == null) ps.setNull(2, BINARY); else ps.setBytes(2, m.getParent().getBytes()); - ps.setBytes(3, m.getGroup().getBytes()); - if(m.getAuthor() == null) ps.setNull(4, BINARY); - else ps.setBytes(4, m.getAuthor().getBytes()); - ps.setString(5, m.getSubject()); - ps.setLong(6, m.getTimestamp()); + ps.setBytes(3, m.getGroup().getId().getBytes()); + Author a = m.getAuthor(); + if(a == null) { + ps.setNull(4, BINARY); + ps.setNull(5, VARCHAR); + ps.setNull(6, BINARY); + } else { + ps.setBytes(4, a.getId().getBytes()); + ps.setString(5, a.getName()); + ps.setBytes(6, a.getPublicKey()); + } + ps.setString(7, m.getSubject()); + ps.setLong(8, m.getTimestamp()); byte[] raw = m.getSerialised(); - ps.setInt(7, raw.length); - ps.setInt(8, m.getBodyStart()); - ps.setInt(9, m.getBodyLength()); - ps.setBytes(10, raw); + ps.setInt(9, raw.length); + ps.setInt(10, m.getBodyStart()); + ps.setInt(11, m.getBodyLength()); + ps.setBytes(12, raw); int affected = ps.executeUpdate(); if(affected != 1) throw new DbStateException(); ps.close(); @@ -686,13 +700,13 @@ abstract class JdbcDatabase implements Database<Connection> { public boolean addPrivateMessage(Connection txn, Message m, ContactId c) throws DbException { - assert m.getGroup() == null; + if(m.getGroup() != null) throw new IllegalArgumentException(); if(containsMessage(txn, m.getId())) return false; PreparedStatement ps = null; try { - String sql = "INSERT INTO messages" - + " (messageId, parentId, subject, timestamp, length," - + " bodyStart, bodyLength, raw, contactId, read, starred)" + String sql = "INSERT INTO messages (messageId, parentId, subject," + + " timestamp, length, bodyStart, bodyLength, raw," + + " contactId, read, starred)" + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, FALSE, FALSE)"; ps = txn.prepareStatement(sql); ps.setBytes(1, m.getId().getBytes()); @@ -1211,33 +1225,42 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public MessageHeader getMessageHeader(Connection txn, MessageId m) - throws DbException { + public Collection<GroupMessageHeader> getMessageHeaders(Connection txn, + GroupId g) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT parentId, authorId, groupId, subject," - + " timestamp, read, starred" + String sql = "SELECT messageId, parentId, authorId, authorName," + + " authorKey, subject, timestamp, read, starred" + " FROM messages" - + " WHERE messageId = ?"; + + " WHERE groupId = ?"; ps = txn.prepareStatement(sql); - ps.setBytes(1, m.getBytes()); + ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); - if(!rs.next()) throw new DbStateException(); - byte[] b = rs.getBytes(1); - MessageId parent = b == null ? null : new MessageId(b); - AuthorId author = new AuthorId(rs.getBytes(2)); - b = rs.getBytes(3); - GroupId group = b == null ? null : new GroupId(b); - String subject = rs.getString(4); - long timestamp = rs.getLong(5); - boolean read = rs.getBoolean(6); - boolean starred = rs.getBoolean(7); - if(rs.next()) throw new DbStateException(); + List<GroupMessageHeader> headers = + new ArrayList<GroupMessageHeader>(); + while(rs.next()) { + MessageId id = new MessageId(rs.getBytes(1)); + byte[] b = rs.getBytes(2); + MessageId parent = b == null ? null : new MessageId(b); + Author author = null; + b = rs.getBytes(3); + if(b != null) { + AuthorId authorId = new AuthorId(b); + String authorName = rs.getString(4); + byte[] authorKey = rs.getBytes(5); + author = new Author(authorId, authorName, authorKey); + } + String subject = rs.getString(6); + long timestamp = rs.getLong(7); + boolean read = rs.getBoolean(8); + boolean starred = rs.getBoolean(9); + headers.add(new GroupMessageHeader(id, parent, subject, + timestamp, read, starred, g, author)); + } rs.close(); ps.close(); - return new MessageHeader(m, parent, group, author, subject, - timestamp, read, starred); + return Collections.unmodifiableList(headers); } catch(SQLException e) { tryToClose(rs); tryToClose(ps); @@ -1245,30 +1268,33 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Collection<MessageHeader> getMessageHeaders(Connection txn, - GroupId g) throws DbException { + public Collection<PrivateMessageHeader> getPrivateMessageHeaders( + Connection txn) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT messageId, parentId, authorId, subject," - + " timestamp, read, starred" - + " FROM messages" - + " WHERE groupId = ?"; + String sql = "SELECT m.messageId, parentId, subject, timestamp," + + " contactId, read, starred, seen" + + " FROM messages AS m" + + " JOIN statuses AS s" + + " ON m.messageId = s.messageId" + + " WHERE groupId IS NULL"; ps = txn.prepareStatement(sql); - ps.setBytes(1, g.getBytes()); rs = ps.executeQuery(); - List<MessageHeader> headers = new ArrayList<MessageHeader>(); + List<PrivateMessageHeader> headers = + new ArrayList<PrivateMessageHeader>(); while(rs.next()) { MessageId id = new MessageId(rs.getBytes(1)); - byte[] p = rs.getBytes(2); - MessageId parent = p == null ? null : new MessageId(p); - AuthorId author = new AuthorId(rs.getBytes(3)); - String subject = rs.getString(4); - long timestamp = rs.getLong(5); + byte[] b = rs.getBytes(2); + MessageId parent = b == null ? null : new MessageId(b); + String subject = rs.getString(3); + long timestamp = rs.getLong(4); + ContactId contactId = new ContactId(rs.getInt(5)); boolean read = rs.getBoolean(6); boolean starred = rs.getBoolean(7); - headers.add(new MessageHeader(id, parent, g, author, subject, - timestamp, read, starred)); + boolean seen = rs.getBoolean(8); + headers.add(new PrivateMessageHeader(id, parent, subject, + timestamp, read, starred, contactId, !seen)); } rs.close(); ps.close(); diff --git a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java index c6c6011b462cadd842da85d4de0d9d70be5f74ec..16f0411243601532836e5813a46716f8445ffd1d 100644 --- a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java +++ b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java @@ -20,9 +20,7 @@ import net.sf.briar.api.clock.Clock; import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.messaging.Author; -import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; -import net.sf.briar.api.messaging.GroupId; import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.MessageFactory; import net.sf.briar.api.messaging.MessageId; @@ -150,12 +148,9 @@ class MessageFactoryImpl implements MessageFactory { } // Hash the message, including the signatures, to get the message ID w.removeConsumer(digestingConsumer); - byte[] raw = out.toByteArray(); MessageId id = new MessageId(messageDigest.digest()); - GroupId groupId = group == null ? null : group.getId(); - AuthorId authorId = author == null ? null : author.getId(); - return new MessageImpl(id, parent, groupId, authorId, subject, - timestamp, raw, bodyStart, body.length); + return new MessageImpl(id, parent, group, author, subject, + timestamp, out.toByteArray(), bodyStart, body.length); } private void writeGroup(Writer w, Group g) throws IOException { diff --git a/briar-core/src/net/sf/briar/messaging/MessageImpl.java b/briar-core/src/net/sf/briar/messaging/MessageImpl.java index e7090a9612dcbf17067131b0308298e02a259ceb..3472613223a9090e575442c51979b25d1eae5edd 100644 --- a/briar-core/src/net/sf/briar/messaging/MessageImpl.java +++ b/briar-core/src/net/sf/briar/messaging/MessageImpl.java @@ -1,8 +1,8 @@ package net.sf.briar.messaging; import static net.sf.briar.api.messaging.MessagingConstants.MAX_BODY_LENGTH; -import net.sf.briar.api.messaging.AuthorId; -import net.sf.briar.api.messaging.GroupId; +import net.sf.briar.api.messaging.Author; +import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.MessageId; @@ -10,15 +10,15 @@ import net.sf.briar.api.messaging.MessageId; class MessageImpl implements Message { private final MessageId id, parent; - private final GroupId group; - private final AuthorId author; + private final Group group; + private final Author author; private final String subject; private final long timestamp; private final byte[] raw; private final int bodyStart, bodyLength; - public MessageImpl(MessageId id, MessageId parent, GroupId group, - AuthorId author, String subject, long timestamp, byte[] raw, + public MessageImpl(MessageId id, MessageId parent, Group group, + Author author, String subject, long timestamp, byte[] raw, int bodyStart, int bodyLength) { if(bodyStart + bodyLength > raw.length) throw new IllegalArgumentException(); @@ -43,11 +43,11 @@ class MessageImpl implements Message { return parent; } - public GroupId getGroup() { + public Group getGroup() { return group; } - public AuthorId getAuthor() { + public Author getAuthor() { return author; } diff --git a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java index 1296481152f6b38ada4945a93319d6d854ace09f..e1dc4896e70b94ae2e22bb86692c2ee544451d0e 100644 --- a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java +++ b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java @@ -8,9 +8,7 @@ import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.crypto.KeyParser; import net.sf.briar.api.crypto.MessageDigest; import net.sf.briar.api.messaging.Author; -import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; -import net.sf.briar.api.messaging.GroupId; import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.MessageId; import net.sf.briar.api.messaging.MessageVerifier; @@ -55,10 +53,7 @@ class MessageVerifierImpl implements MessageVerifier { if(!signature.verify(m.getGroupSignature())) throw new GeneralSecurityException(); } - GroupId groupId = group == null ? null : group.getId(); - AuthorId authorId = author == null ? null : author.getId(); - return new MessageImpl(id, m.getParent(), groupId, authorId, - m.getSubject(), m.getTimestamp(), raw, m.getBodyStart(), - m.getBodyLength()); + return new MessageImpl(id, m.getParent(), group, author, m.getSubject(), + m.getTimestamp(), raw, m.getBodyStart(), m.getBodyLength()); } } 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 42d0acb290b4152d7fb439ff2f7265c9187239a0..310c4d304c9d14ae9e42fc354f52bebf2827a252 100644 --- a/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java +++ b/briar-core/src/net/sf/briar/messaging/duplex/DuplexConnection.java @@ -29,7 +29,7 @@ import net.sf.briar.api.db.DbException; import net.sf.briar.api.db.event.ContactRemovedEvent; import net.sf.briar.api.db.event.DatabaseEvent; import net.sf.briar.api.db.event.DatabaseListener; -import net.sf.briar.api.db.event.LocalRetentionTimeUpdatedEvent; +import net.sf.briar.api.db.event.MessageExpiredEvent; import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent; import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent; import net.sf.briar.api.db.event.MessageAddedEvent; @@ -134,7 +134,7 @@ abstract class DuplexConnection implements DatabaseListener { if(e instanceof ContactRemovedEvent) { ContactRemovedEvent c = (ContactRemovedEvent) e; if(contactId.equals(c.getContactId())) dispose(false, true); - } else if(e instanceof LocalRetentionTimeUpdatedEvent) { + } else if(e instanceof MessageExpiredEvent) { dbExecutor.execute(new GenerateRetentionUpdate()); } else if(e instanceof LocalSubscriptionsUpdatedEvent) { LocalSubscriptionsUpdatedEvent l = diff --git a/briar-tests/src/net/sf/briar/TestMessage.java b/briar-tests/src/net/sf/briar/TestMessage.java index 881b02777a95167b750f8381790df831d4985cc1..0c4ae532a9e201036ec24d5901456b977d0b4389 100644 --- a/briar-tests/src/net/sf/briar/TestMessage.java +++ b/briar-tests/src/net/sf/briar/TestMessage.java @@ -3,28 +3,28 @@ package net.sf.briar; import java.io.ByteArrayInputStream; import java.io.InputStream; -import net.sf.briar.api.messaging.AuthorId; -import net.sf.briar.api.messaging.GroupId; +import net.sf.briar.api.messaging.Author; +import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.Message; import net.sf.briar.api.messaging.MessageId; public class TestMessage implements Message { private final MessageId id, parent; - private final GroupId group; - private final AuthorId author; + private final Group group; + private final Author author; private final String subject; private final long timestamp; private final byte[] raw; private final int bodyStart, bodyLength; - public TestMessage(MessageId id, MessageId parent, GroupId group, - AuthorId author, String subject, long timestamp, byte[] raw) { + public TestMessage(MessageId id, MessageId parent, Group group, + Author author, String subject, long timestamp, byte[] raw) { this(id, parent, group, author, subject, timestamp, raw, 0, raw.length); } - public TestMessage(MessageId id, MessageId parent, GroupId group, - AuthorId author, String subject, long timestamp, byte[] raw, + public TestMessage(MessageId id, MessageId parent, Group group, + Author author, String subject, long timestamp, byte[] raw, int bodyStart, int bodyLength) { this.id = id; this.parent = parent; @@ -45,11 +45,11 @@ public class TestMessage implements Message { return parent; } - public GroupId getGroup() { + public Group getGroup() { return group; } - public AuthorId getAuthor() { + public Author getAuthor() { return author; } diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java index db67d27c52133d62df2fb83d19809eeb1a6457f1..1cf9044ffa54b389ef8759186aa3d3ac4b373d77 100644 --- a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java @@ -29,6 +29,7 @@ import net.sf.briar.api.db.event.MessageAddedEvent; import net.sf.briar.api.db.event.RatingChangedEvent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.messaging.Ack; +import net.sf.briar.api.messaging.Author; import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.GroupId; @@ -53,43 +54,46 @@ import org.junit.Test; public abstract class DatabaseComponentTest extends BriarTestCase { protected final Object txn = new Object(); - protected final AuthorId authorId; - protected final ContactId contactId; protected final GroupId groupId; + protected final Group group; + protected final AuthorId authorId; + protected final Author author; protected final MessageId messageId, messageId1; - private final String contactName, subject; - private final long timestamp; - private final int size; - private final byte[] raw; - private final Contact contact; - private final Message message, privateMessage; - private final Group group; - private final TransportId transportId; - private final TransportProperties transportProperties; - private final Endpoint endpoint; - private final TemporarySecret temporarySecret; + protected final String subject; + protected final long timestamp; + protected final int size; + protected final byte[] raw; + protected final Message message, privateMessage; + protected final TransportId transportId; + protected final TransportProperties transportProperties; + protected final ContactId contactId; + protected final String contactName; + protected final Contact contact; + protected final Endpoint endpoint; + protected final TemporarySecret temporarySecret; public DatabaseComponentTest() { super(); - authorId = new AuthorId(TestUtils.getRandomId()); - contactId = new ContactId(234); groupId = new GroupId(TestUtils.getRandomId()); + group = new Group(groupId, "Group name", null); + authorId = new AuthorId(TestUtils.getRandomId()); + author = new Author(authorId, "Alice", new byte[60]); messageId = new MessageId(TestUtils.getRandomId()); messageId1 = new MessageId(TestUtils.getRandomId()); - contactName = "Alice"; subject = "Foo"; timestamp = System.currentTimeMillis(); size = 1234; raw = new byte[size]; - contact = new Contact(contactId, contactName, timestamp); - message = new TestMessage(messageId, null, groupId, authorId, subject, + message = new TestMessage(messageId, null, group, author, subject, timestamp, raw); privateMessage = new TestMessage(messageId, null, null, null, subject, timestamp, raw); - group = new Group(groupId, "The really exciting group", null); transportId = new TransportId(TestUtils.getRandomId()); - transportProperties = new TransportProperties( - Collections.singletonMap("foo", "bar")); + transportProperties = new TransportProperties(Collections.singletonMap( + "foo", "bar")); + contactId = new ContactId(234); + contactName = "Alice"; + contact = new Contact(contactId, contactName, timestamp); endpoint = new Endpoint(contactId, transportId, 123, 234, 345, true); temporarySecret = new TemporarySecret(contactId, transportId, 1, 2, 3, false, 4, new byte[32], 5, 6, new byte[4]); diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java index 37cc20a629b2ad97235ebaa45c56575219ae5808..ec00dd3f61c2d5f858832727622e8ee0c94bdcd0 100644 --- a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java +++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java @@ -28,7 +28,8 @@ import net.sf.briar.api.TransportConfig; import net.sf.briar.api.TransportProperties; import net.sf.briar.api.clock.SystemClock; import net.sf.briar.api.db.DbException; -import net.sf.briar.api.db.MessageHeader; +import net.sf.briar.api.db.GroupMessageHeader; +import net.sf.briar.api.messaging.Author; import net.sf.briar.api.messaging.AuthorId; import net.sf.briar.api.messaging.Group; import net.sf.briar.api.messaging.GroupId; @@ -50,37 +51,40 @@ public class H2DatabaseTest extends BriarTestCase { private final File testDir = TestUtils.getTestDirectory(); private final Random random = new Random(); + private final GroupId groupId; private final Group group; private final AuthorId authorId; - private final ContactId contactId; - private final GroupId groupId; + private final Author author; private final MessageId messageId, messageId1; - private final String contactName, subject; + private final String subject; private final long timestamp; private final int size; private final byte[] raw; private final Message message, privateMessage; private final TransportId transportId; + private final ContactId contactId; + private final String contactName; public H2DatabaseTest() throws Exception { super(); - authorId = new AuthorId(TestUtils.getRandomId()); - contactId = new ContactId(1); groupId = new GroupId(TestUtils.getRandomId()); + group = new Group(groupId, "Group name", null); + authorId = new AuthorId(TestUtils.getRandomId()); + author = new Author(authorId, "Alice", new byte[60]); messageId = new MessageId(TestUtils.getRandomId()); messageId1 = new MessageId(TestUtils.getRandomId()); - group = new Group(groupId, "Foo", null); - contactName = "Alice"; subject = "Foo"; timestamp = System.currentTimeMillis(); size = 1234; raw = new byte[size]; random.nextBytes(raw); - message = new TestMessage(messageId, null, groupId, authorId, subject, + message = new TestMessage(messageId, null, group, author, subject, timestamp, raw); privateMessage = new TestMessage(messageId1, null, null, null, subject, timestamp, raw); transportId = new TransportId(TestUtils.getRandomId()); + contactId = new ContactId(1); + contactName = "Alice"; } @Before @@ -545,8 +549,9 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetMessagesByAuthor() throws Exception { AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); + Author author1 = new Author(authorId1, "Bob", new byte[60]); MessageId messageId1 = new MessageId(TestUtils.getRandomId()); - Message message1 = new TestMessage(messageId1, null, groupId, authorId1, + Message message1 = new TestMessage(messageId1, null, group, author1, subject, timestamp, raw); Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -577,15 +582,14 @@ public class H2DatabaseTest extends BriarTestCase { 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 name", - null); - Message child1 = new TestMessage(childId1, messageId, groupId, - authorId, subject, timestamp, raw); - Message child2 = new TestMessage(childId2, messageId, groupId, - authorId, subject, timestamp, raw); + Group group1 = new Group(groupId1, "Group name", null); + Message child1 = new TestMessage(childId1, messageId, group, author, + subject, timestamp, raw); + Message child2 = new TestMessage(childId2, messageId, group, author, + subject, timestamp, raw); // The third child is in a different group - Message child3 = new TestMessage(childId3, messageId, groupId1, - authorId, subject, timestamp, raw); + Message child3 = new TestMessage(childId3, messageId, group1, author, + subject, timestamp, raw); Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -615,7 +619,7 @@ public class H2DatabaseTest extends BriarTestCase { @Test public void testGetOldMessages() throws Exception { MessageId messageId1 = new MessageId(TestUtils.getRandomId()); - Message message1 = new TestMessage(messageId1, null, groupId, authorId, + Message message1 = new TestMessage(messageId1, null, group, author, subject, timestamp + 1000, raw); Database<Connection> db = open(false); Connection txn = db.startTransaction(); @@ -646,7 +650,7 @@ public class H2DatabaseTest extends BriarTestCase { public void testGetFreeSpace() throws Exception { byte[] largeBody = new byte[ONE_MEGABYTE]; for(int i = 0; i < largeBody.length; i++) largeBody[i] = (byte) i; - Message message1 = new TestMessage(messageId, null, groupId, authorId, + Message message1 = new TestMessage(messageId, null, group, author, subject, timestamp, largeBody); Database<Connection> db = open(false); @@ -1126,7 +1130,7 @@ public class H2DatabaseTest extends BriarTestCase { // A message with no parent should return null MessageId childId = new MessageId(TestUtils.getRandomId()); - Message child = new TestMessage(childId, null, groupId, null, subject, + Message child = new TestMessage(childId, null, group, null, subject, timestamp, raw); db.addGroupMessage(txn, child); assertTrue(db.containsMessage(txn, childId)); @@ -1147,7 +1151,7 @@ public class H2DatabaseTest extends BriarTestCase { // A message with an absent parent should return null MessageId childId = new MessageId(TestUtils.getRandomId()); MessageId parentId = new MessageId(TestUtils.getRandomId()); - Message child = new TestMessage(childId, parentId, groupId, null, + Message child = new TestMessage(childId, parentId, group, null, subject, timestamp, raw); db.addGroupMessage(txn, child); assertTrue(db.containsMessage(txn, childId)); @@ -1173,9 +1177,9 @@ public class H2DatabaseTest extends BriarTestCase { // A message with a parent in another group should return null MessageId childId = new MessageId(TestUtils.getRandomId()); MessageId parentId = new MessageId(TestUtils.getRandomId()); - Message child = new TestMessage(childId, parentId, groupId, null, + Message child = new TestMessage(childId, parentId, group, null, subject, timestamp, raw); - Message parent = new TestMessage(parentId, null, groupId1, null, + Message parent = new TestMessage(parentId, null, group1, null, subject, timestamp, raw); db.addGroupMessage(txn, child); db.addGroupMessage(txn, parent); @@ -1198,8 +1202,8 @@ public class H2DatabaseTest extends BriarTestCase { // A message with a private parent should return null MessageId childId = new MessageId(TestUtils.getRandomId()); - Message child = new TestMessage(childId, messageId1, groupId, - null, subject, timestamp, raw); + Message child = new TestMessage(childId, messageId1, group, null, + subject, timestamp, raw); db.addGroupMessage(txn, child); db.addPrivateMessage(txn, privateMessage, contactId); assertTrue(db.containsMessage(txn, childId)); @@ -1222,10 +1226,10 @@ public class H2DatabaseTest extends BriarTestCase { // A message with a parent in the same group should return the parent MessageId childId = new MessageId(TestUtils.getRandomId()); MessageId parentId = new MessageId(TestUtils.getRandomId()); - Message child = new TestMessage(childId, parentId, groupId, null, - subject, timestamp, raw); - Message parent = new TestMessage(parentId, null, groupId, null, + Message child = new TestMessage(childId, parentId, group, null, subject, timestamp, raw); + Message parent = new TestMessage(parentId, null, group, null, subject, + timestamp, raw); db.addGroupMessage(txn, child); db.addGroupMessage(txn, parent); assertTrue(db.containsMessage(txn, childId)); @@ -1247,7 +1251,7 @@ public class H2DatabaseTest extends BriarTestCase { // Store a couple of messages int bodyLength = raw.length - 20; - Message message1 = new TestMessage(messageId, null, groupId, null, + Message message1 = new TestMessage(messageId, null, group, null, subject, timestamp, raw, 5, bodyLength); Message privateMessage1 = new TestMessage(messageId1, null, null, null, subject, timestamp, raw, 10, bodyLength); @@ -1289,19 +1293,20 @@ public class H2DatabaseTest extends BriarTestCase { MessageId messageId1 = new MessageId(TestUtils.getRandomId()); MessageId parentId = new MessageId(TestUtils.getRandomId()); long timestamp1 = System.currentTimeMillis(); - Message message1 = new TestMessage(messageId1, parentId, groupId, - authorId, subject, timestamp1, raw); + Message message1 = new TestMessage(messageId1, parentId, group, author, + subject, timestamp1, raw); db.addGroupMessage(txn, message1); // Mark one of the messages read assertFalse(db.setReadFlag(txn, messageId, true)); // Retrieve the message headers - Collection<MessageHeader> headers = db.getMessageHeaders(txn, groupId); - Iterator<MessageHeader> it = headers.iterator(); + Collection<GroupMessageHeader> headers = + db.getMessageHeaders(txn, groupId); + Iterator<GroupMessageHeader> it = headers.iterator(); boolean messageFound = false, message1Found = false; // First header (order is undefined) assertTrue(it.hasNext()); - MessageHeader header = it.next(); + GroupMessageHeader header = it.next(); if(messageId.equals(header.getId())) { assertHeadersMatch(message, header); assertTrue(header.getRead()); @@ -1340,12 +1345,11 @@ public class H2DatabaseTest extends BriarTestCase { db.close(); } - private void assertHeadersMatch(Message m, MessageHeader h) { + private void assertHeadersMatch(Message m, GroupMessageHeader h) { assertEquals(m.getId(), h.getId()); if(m.getParent() == null) assertNull(h.getParent()); else assertEquals(m.getParent(), h.getParent()); - if(m.getGroup() == null) assertNull(h.getGroup()); - else assertEquals(m.getGroup(), h.getGroup()); + assertEquals(m.getGroup().getId(), h.getGroupId()); if(m.getAuthor() == null) assertNull(h.getAuthor()); else assertEquals(m.getAuthor(), h.getAuthor()); assertEquals(m.getSubject(), h.getSubject()); @@ -1412,20 +1416,20 @@ public class H2DatabaseTest extends BriarTestCase { // Subscribe to a couple of groups db.addSubscription(txn, group); GroupId groupId1 = new GroupId(TestUtils.getRandomId()); - Group group1 = new Group(groupId1, "Another group", null); + Group group1 = new Group(groupId1, "Group name", null); db.addSubscription(txn, group1); // Store two messages in the first group db.addGroupMessage(txn, message); MessageId messageId1 = new MessageId(TestUtils.getRandomId()); - Message message1 = new TestMessage(messageId1, null, groupId, - authorId, subject, timestamp, raw); + Message message1 = new TestMessage(messageId1, null, group, author, + subject, timestamp, raw); db.addGroupMessage(txn, message1); // Store one message in the second group MessageId messageId2 = new MessageId(TestUtils.getRandomId()); - Message message2 = new TestMessage(messageId2, null, groupId1, - authorId, subject, timestamp, raw); + Message message2 = new TestMessage(messageId2, null, group1, author, + subject, timestamp, raw); db.addGroupMessage(txn, message2); // Mark one of the messages in the first group read