From 00275e260fcb69db3010717a41484a27e190f185 Mon Sep 17 00:00:00 2001 From: akwizgran <akwizgran@users.sourceforge.net> Date: Tue, 9 Feb 2016 11:14:52 +0000 Subject: [PATCH] Allow messages to be deleted. --- .../api/db/DatabaseComponent.java | 6 +++ .../src/org/briarproject/db/Database.java | 10 ++++ .../db/DatabaseComponentImpl.java | 17 +++++++ .../src/org/briarproject/db/JdbcDatabase.java | 26 ++++++++-- .../org/briarproject/db/H2DatabaseTest.java | 47 +++++++++++++++++++ 5 files changed, 101 insertions(+), 5 deletions(-) diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 465a886bd7..80fd14a7e6 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -63,6 +63,12 @@ public interface DatabaseComponent { */ void addTransportKeys(ContactId c, TransportKeys k) throws DbException; + /** + * Deletes the message with the given ID. The message ID and any other + * associated data are not deleted. + */ + void deleteMessage(MessageId m) throws DbException; + /** * Returns an acknowledgement for the given contact, or null if there are * no messages to acknowledge. diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index a5a22b135c..d21e6c3495 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -215,6 +215,16 @@ interface Database<T> { */ int countOfferedMessages(T txn, ContactId c) throws DbException; + /** + * Deletes the message with the given ID. Unlike + * {@link #removeMessage(Object, MessageId)}, the message ID and any other + * associated data are not deleted, and + * {@link #containsMessage(Object, MessageId)} will continue to return true. + * <p> + * Locking: write. + */ + void deleteMessage(T txn, MessageId m) throws DbException; + /** * Returns the contact with the given ID. * <p> diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 9e929d86c8..d55501a101 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -293,6 +293,23 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { } } + public void deleteMessage(MessageId m) throws DbException { + lock.writeLock().lock(); + try { + T txn = db.startTransaction(); + try { + if (!db.containsMessage(txn, m)) + throw new NoSuchMessageException(); + db.deleteMessage(txn, m); + } catch (DbException e) { + db.abortTransaction(txn); + throw e; + } + } finally { + lock.writeLock().unlock(); + } + } + public Ack generateAck(ContactId c, int maxMessages) throws DbException { Collection<MessageId> ids; lock.writeLock().lock(); diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index 79c07da039..2f32e8fd8b 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -136,7 +136,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " valid INT NOT NULL," + " shared BOOLEAN NOT NULL," + " length INT NOT NULL," - + " raw BLOB NOT NULL," + + " raw BLOB," // Null if message has been deleted + " PRIMARY KEY (messageId)," + " FOREIGN KEY (groupId)" + " REFERENCES groups (groupId)" @@ -933,6 +933,22 @@ abstract class JdbcDatabase implements Database<Connection> { } } + public void deleteMessage(Connection txn, MessageId m) throws DbException { + PreparedStatement ps = null; + try { + String sql = "UPDATE messages SET raw = NULL WHERE messageId = ?"; + ps = txn.prepareStatement(sql); + ps.setBytes(1, m.getBytes()); + int affected = ps.executeUpdate(); + if (affected < 0) throw new DbStateException(); + if (affected > 1) throw new DbStateException(); + ps.close(); + } catch (SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + public Contact getContact(Connection txn, ContactId c) throws DbException { PreparedStatement ps = null; ResultSet rs = null; @@ -1308,7 +1324,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " ON m.messageId = s.messageId" + " AND gv.contactId = s.contactId" + " WHERE gv.contactId = ?" - + " AND valid = ? AND shared = TRUE" + + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND seen = FALSE AND requested = FALSE" + " AND s.expiry < ?" + " ORDER BY timestamp DESC LIMIT ?"; @@ -1367,7 +1383,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " ON m.messageId = s.messageId" + " AND gv.contactId = s.contactId" + " WHERE gv.contactId = ?" - + " AND valid = ? AND shared = TRUE" + + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND seen = FALSE" + " AND s.expiry < ?" + " ORDER BY timestamp DESC"; @@ -1401,7 +1417,7 @@ abstract class JdbcDatabase implements Database<Connection> { try { String sql = "SELECT messageId FROM messages AS m" + " JOIN groups AS g ON m.groupId = g.groupId" - + " WHERE valid = ? AND clientId = ?"; + + " WHERE valid = ? AND clientId = ? AND raw IS NOT NULL"; ps = txn.prepareStatement(sql); ps.setInt(1, UNKNOWN.getValue()); ps.setBytes(2, c.getBytes()); @@ -1453,7 +1469,7 @@ abstract class JdbcDatabase implements Database<Connection> { + " ON m.messageId = s.messageId" + " AND gv.contactId = s.contactId" + " WHERE gv.contactId = ?" - + " AND valid = ? AND shared = TRUE" + + " AND valid = ? AND shared = TRUE AND raw IS NOT NULL" + " AND seen = FALSE AND requested = TRUE" + " AND s.expiry < ?" + " ORDER BY timestamp DESC"; diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java index 2fe48c6c3e..ec0674e03f 100644 --- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java +++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java @@ -52,6 +52,8 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -1071,6 +1073,51 @@ public class H2DatabaseTest extends BriarTestCase { db.close(); } + @Test + public void testDeleteMessage() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, a group and a message + db.addLocalAuthor(txn, localAuthor); + assertEquals(contactId, db.addContact(txn, author, localAuthorId)); + db.addGroup(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addMessage(txn, message, VALID, true); + db.addStatus(txn, contactId, messageId, false, false); + + // The message should be visible to the contact + assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); + + // The message should be sendable + Collection<MessageId> ids = db.getMessagesToSend(txn, contactId, + ONE_MEGABYTE); + assertEquals(Collections.singletonList(messageId), ids); + ids = db.getMessagesToOffer(txn, contactId, 100); + assertEquals(Collections.singletonList(messageId), ids); + + // The raw message should not be null + assertNotNull(db.getRawMessage(txn, messageId)); + + // Delete the message + db.deleteMessage(txn, messageId); + + // The message should be visible to the contact + assertTrue(db.containsVisibleMessage(txn, contactId, messageId)); + + // The message should not be sendable + ids = db.getMessagesToSend(txn, contactId, ONE_MEGABYTE); + assertTrue(ids.isEmpty()); + ids = db.getMessagesToOffer(txn, contactId, 100); + assertTrue(ids.isEmpty()); + + // The raw message should be null + assertNull(db.getRawMessage(txn, messageId)); + + db.commitTransaction(txn); + db.close(); + } + @Test public void testExceptionHandling() throws Exception { Database<Connection> db = open(false); -- GitLab