Added the ability to remove pseudonyms from the database.

This commit is contained in:
akwizgran
2013-12-10 22:23:37 +00:00
parent 667dbfdd4a
commit 47708d489d
10 changed files with 368 additions and 41 deletions

View File

@@ -49,6 +49,8 @@ import net.sf.briar.api.transport.TemporarySecret;
* <li> transport
* <li> window
* </ul>
* If table A has a foreign key pointing to table B, we get a read lock on A to
* read A, a write lock on A to write A, and write locks on A and B to write B.
*/
interface Database<T> {
@@ -80,8 +82,8 @@ interface Database<T> {
* Stores a contact with the given pseudonym, associated with the given
* local pseudonym, and returns an ID for the contact.
* <p>
* Locking: contact write, retention write, subscription write, transport
* write, window write.
* Locking: contact write, message write, retention write,
* subscription write, transport write, window write.
*/
ContactId addContact(T txn, Author remote, AuthorId local)
throws DbException;
@@ -103,9 +105,10 @@ interface Database<T> {
throws DbException;
/**
* Stores a pseudonym that the user can use to sign messages.
* Stores a local pseudonym.
* <p>
* Locking: contact write, identity write.
* Locking: contact write, identity write, message write, retention write,
* subscription write, transport write, window write.
*/
void addLocalAuthor(T txn, LocalAuthor a) throws DbException;
@@ -181,6 +184,13 @@ interface Database<T> {
*/
boolean containsContact(T txn, ContactId c) throws DbException;
/**
* Returns true if the database contains the given local pseudonym.
* <p>
* Locking: identity read.
*/
boolean containsLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns true if the database contains the given message.
* <p>
@@ -246,6 +256,13 @@ interface Database<T> {
*/
Collection<Contact> getContacts(T txn) throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p>
* Locking: contact read.
*/
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/**
* Returns all endpoints.
* <p>
@@ -291,14 +308,14 @@ interface Database<T> {
Map<ContactId, Long> getLastConnected(T txn) throws DbException;
/**
* Returns the pseudonym with the given ID.
* Returns the local pseudonym with the given ID.
* <p>
* Locking: identitiy read.
* Locking: identity read.
*/
LocalAuthor getLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Returns all pseudonyms that the user can use to sign messages.
* Returns all local pseudonyms.
* <p>
* Locking: identity read.
*/
@@ -566,6 +583,15 @@ interface Database<T> {
*/
void removeContact(T txn, ContactId c) throws DbException;
/**
* Removes the local pseudonym with the given ID (and all associated
* state) from the database.
* <p>
* Locking: contact write, identity write, message write, retention write,
* subscription write, transport write, window write.
*/
void removeLocalAuthor(T txn, AuthorId a) throws DbException;
/**
* Removes a message (and all associated state) from the database.
* <p>

View File

@@ -38,7 +38,9 @@ import net.sf.briar.api.db.ContactExistsException;
import net.sf.briar.api.db.DatabaseComponent;
import net.sf.briar.api.db.DbException;
import net.sf.briar.api.db.GroupMessageHeader;
import net.sf.briar.api.db.LocalAuthorExistsException;
import net.sf.briar.api.db.NoSuchContactException;
import net.sf.briar.api.db.NoSuchLocalAuthorException;
import net.sf.briar.api.db.NoSuchMessageException;
import net.sf.briar.api.db.NoSuchSubscriptionException;
import net.sf.briar.api.db.NoSuchTransportException;
@@ -48,6 +50,8 @@ 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.GroupMessageAddedEvent;
import net.sf.briar.api.db.event.LocalAuthorAddedEvent;
import net.sf.briar.api.db.event.LocalAuthorRemovedEvent;
import net.sf.briar.api.db.event.LocalSubscriptionsUpdatedEvent;
import net.sf.briar.api.db.event.LocalTransportsUpdatedEvent;
import net.sf.briar.api.db.event.MessageExpiredEvent;
@@ -186,35 +190,47 @@ DatabaseCleaner.Callback {
ContactId c;
contactLock.writeLock().lock();
try {
retentionLock.writeLock().lock();
identityLock.readLock().lock();
try {
subscriptionLock.writeLock().lock();
messageLock.writeLock().lock();
try {
transportLock.writeLock().lock();
retentionLock.writeLock().lock();
try {
windowLock.writeLock().lock();
subscriptionLock.writeLock().lock();
try {
T txn = db.startTransaction();
transportLock.writeLock().lock();
try {
if(db.containsContact(txn, remote.getId()))
throw new ContactExistsException();
c = db.addContact(txn, remote, local);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
windowLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if(db.containsContact(txn, remote.getId()))
throw new ContactExistsException();
if(!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
c = db.addContact(txn, remote, local);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
}
} finally {
transportLock.writeLock().unlock();
}
} finally {
windowLock.writeLock().unlock();
subscriptionLock.writeLock().unlock();
}
} finally {
transportLock.writeLock().unlock();
retentionLock.writeLock().unlock();
}
} finally {
subscriptionLock.writeLock().unlock();
messageLock.writeLock().unlock();
}
} finally {
retentionLock.writeLock().unlock();
identityLock.readLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
@@ -263,13 +279,40 @@ DatabaseCleaner.Callback {
try {
identityLock.writeLock().lock();
try {
T txn = db.startTransaction();
messageLock.writeLock().lock();
try {
db.addLocalAuthor(txn, a);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
retentionLock.writeLock().lock();
try {
subscriptionLock.writeLock().lock();
try {
transportLock.writeLock().lock();
try {
windowLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if(db.containsLocalAuthor(txn, a.getId()))
throw new LocalAuthorExistsException();
db.addLocalAuthor(txn, a);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
}
} finally {
transportLock.writeLock().unlock();
}
} finally {
subscriptionLock.writeLock().unlock();
}
} finally {
retentionLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
identityLock.writeLock().unlock();
@@ -277,6 +320,7 @@ DatabaseCleaner.Callback {
} finally {
contactLock.writeLock().unlock();
}
callListeners(new LocalAuthorAddedEvent(a.getId()));
}
public void addLocalGroupMessage(Message m) throws DbException {
@@ -942,6 +986,8 @@ DatabaseCleaner.Callback {
try {
T txn = db.startTransaction();
try {
if(!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
LocalAuthor localAuthor = db.getLocalAuthor(txn, a);
db.commitTransaction(txn);
return localAuthor;
@@ -1643,6 +1689,58 @@ DatabaseCleaner.Callback {
callListeners(new ContactRemovedEvent(c));
}
public void removeLocalAuthor(AuthorId a) throws DbException {
Collection<ContactId> affected;
contactLock.writeLock().lock();
try {
identityLock.writeLock().lock();
try {
messageLock.writeLock().lock();
try {
retentionLock.writeLock().lock();
try {
subscriptionLock.writeLock().lock();
try {
transportLock.writeLock().lock();
try {
windowLock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if(!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
affected = db.getContacts(txn, a);
db.removeLocalAuthor(txn, a);
db.commitTransaction(txn);
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
windowLock.writeLock().unlock();
}
} finally {
transportLock.writeLock().unlock();
}
} finally {
subscriptionLock.writeLock().unlock();
}
} finally {
retentionLock.writeLock().unlock();
}
} finally {
messageLock.writeLock().unlock();
}
} finally {
identityLock.writeLock().unlock();
}
} finally {
contactLock.writeLock().unlock();
}
for(ContactId c : affected) callListeners(new ContactRemovedEvent(c));
callListeners(new LocalAuthorRemovedEvent(a));
}
public void removeTransport(TransportId t) throws DbException {
transportLock.writeLock().lock();
try {

View File

@@ -60,6 +60,7 @@ import net.sf.briar.api.transport.TemporarySecret;
abstract class JdbcDatabase implements Database<Connection> {
// Locking: identity
// Dependents: contact, message, retention, subscription, transport, window
private static final String CREATE_LOCAL_AUTHORS =
"CREATE TABLE localAuthors"
+ " (authorId HASH NOT NULL,"
@@ -81,10 +82,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " UNIQUE (authorId),"
+ " FOREIGN KEY (localAuthorId)"
+ " REFERENCES localAuthors (authorId)"
+ " ON DELETE RESTRICT)"; // Deletion not allowed
private static final String INDEX_CONTACTS_BY_AUTHOR =
"CREATE INDEX contactsByAuthor ON contacts (authorId)";
+ " ON DELETE CASCADE)";
// Locking: subscription
// Dependents: message
@@ -376,7 +374,6 @@ abstract class JdbcDatabase implements Database<Connection> {
s = txn.createStatement();
s.executeUpdate(insertTypeNames(CREATE_LOCAL_AUTHORS));
s.executeUpdate(insertTypeNames(CREATE_CONTACTS));
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR);
s.executeUpdate(insertTypeNames(CREATE_GROUPS));
s.executeUpdate(insertTypeNames(CREATE_GROUP_VISIBILITIES));
s.executeUpdate(insertTypeNames(CREATE_CONTACT_GROUPS));
@@ -1000,6 +997,27 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public boolean containsLocalAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT NULL FROM localAuthors WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
boolean found = rs.next();
if(rs.next()) throw new DbStateException();
rs.close();
ps.close();
return found;
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public boolean containsMessage(Connection txn, MessageId m)
throws DbException {
PreparedStatement ps = null;
@@ -1228,6 +1246,28 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<ContactId> getContacts(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT contactId FROM contacts"
+ " WHERE localAuthorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
rs = ps.executeQuery();
List<ContactId> ids = new ArrayList<ContactId>();
while(rs.next()) ids.add(new ContactId(rs.getInt(1)));
rs.close();
ps.close();
return Collections.unmodifiableList(ids);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public Collection<Endpoint> getEndpoints(Connection txn)
throws DbException {
PreparedStatement ps = null;
@@ -2543,6 +2583,22 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void removeLocalAuthor(Connection txn, AuthorId a)
throws DbException {
PreparedStatement ps = null;
try {
String sql = "DELETE FROM localAuthors WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getBytes());
int affected = ps.executeUpdate();
if(affected != 1) throw new DbStateException();
ps.close();
} catch(SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void removeMessage(Connection txn, MessageId m) throws DbException {
PreparedStatement ps = null;
try {