Allow messages to be deleted.

This commit is contained in:
akwizgran
2016-02-09 11:14:52 +00:00
parent 43558ca089
commit 00275e260f
5 changed files with 101 additions and 5 deletions

View File

@@ -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.

View File

@@ -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>

View File

@@ -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();

View File

@@ -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";

View File

@@ -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);