mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 06:09:55 +01:00
Added the ability to remove pseudonyms from the database.
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user