diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java index ece42f725c2eb7b8be65a840088aeb8040edcc2a..ed3bc3cf4ace5e5a44150d3e8c3faed7db08f275 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java @@ -7,6 +7,7 @@ import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.mailbox.MailboxInfo; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.settings.Settings; @@ -114,6 +115,13 @@ public interface DatabaseComponent { KeySetId addTransportKeys(Transaction txn, ContactId c, TransportKeys k) throws DbException; + /** + * Set the @{Link ContactType#CONTACT_MAILBOX} for a contact. + * key set ID. + */ + void setMailboxForContact(Transaction txn, ContactId c, @Nullable ContactId mailboxId, + @Nullable ContactId aliasId) throws DbException; + /** * Returns true if the database contains the given contact for the given * local pseudonym. @@ -235,6 +243,14 @@ public interface DatabaseComponent { Collection<ContactId> getContacts(Transaction txn, AuthorId a) throws DbException; + /** + * Returns @{MailboxInfo}s for all contact mailboxes. + * <p/> + * Read-only. + */ + Collection<MailboxInfo> getContactMailboxes(Transaction txn) + throws DbException; + /** * Returns the group with the given ID. * <p/> diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..0bd84867fba93f2cbb31714993d2b339411a9595 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/mailbox/MailboxInfo.java @@ -0,0 +1,41 @@ +package org.briarproject.bramble.api.mailbox; + +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.Nullable; + +@NotNullByDefault +public class MailboxInfo { + + private final ContactId contactId; + // Null if the contact has no mailbox, our own will be used if possible. + @Nullable + private final ContactId mailboxId; + // The alias of the contact for the mailbox. The mailbox must not know + // the read id. + @Nullable + private final ContactId aliasId; + + public MailboxInfo(ContactId contactId, + ContactId mailboxId, + ContactId aliasId) { + this.contactId = contactId; + this.mailboxId = mailboxId; + this.aliasId = aliasId; + } + + public ContactId getContactId() { + return contactId; + } + + @Nullable + public ContactId getMailboxId() { + return mailboxId; + } + + @Nullable + public ContactId getAliasId() { + return aliasId; + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java index 3c0f476a6d1521bfd9dfc48fc2a1e0a2feafb34f..dd75128f88b03e4052a76a1a53164d3096f9aaa6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java @@ -13,6 +13,7 @@ import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.mailbox.MailboxInfo; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.settings.Settings; @@ -135,6 +136,13 @@ interface Database<T> { KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) throws DbException; + /** + * Set the @{Link ContactType#CONTACT_MAILBOX} for a contact. + * key set ID. + */ + void setMailboxForContact(T txn, ContactId c, @Nullable ContactId mailboxId, + @Nullable ContactId aliasId) throws DbException; + /** * Returns true if the database contains the given contact for the given * local pseudonym. @@ -246,6 +254,13 @@ interface Database<T> { */ Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException; + /** + * Returns @{MailboxInfo}s for all contact mailboxes. + * <p/> + * Read-only. + */ + Collection<MailboxInfo> getContactMailboxes(T txn) throws DbException; + /** * Returns the amount of free storage space available to the database, in * bytes. This is based on the minimum of the space available on the device diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java index 76e68f696b21b459b3439dbdf1d11765c56db023..75832b9b020628c35a88ffcc930d50d0d12c3fb6 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java @@ -27,6 +27,7 @@ import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.event.LocalAuthorAddedEvent; import org.briarproject.bramble.api.identity.event.LocalAuthorRemovedEvent; import org.briarproject.bramble.api.lifecycle.ShutdownManager; +import org.briarproject.bramble.api.mailbox.MailboxInfo; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.settings.Settings; @@ -249,6 +250,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { return db.addTransportKeys(txn, c, k); } + @Override + public void setMailboxForContact(Transaction transaction, ContactId c, + @Nullable ContactId mailboxId, @Nullable ContactId aliasId) + throws DbException { + if (transaction.isReadOnly()) throw new IllegalArgumentException(); + T txn = unbox(transaction); + if (!db.containsContact(txn, c)) + throw new NoSuchContactException(); + db.setMailboxForContact(txn, c, mailboxId, aliasId); + } + @Override public boolean containsContact(Transaction transaction, AuthorId remote, AuthorId local) throws DbException { @@ -421,6 +433,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent { return db.getContacts(txn, a); } + @Override + public Collection<MailboxInfo> getContactMailboxes(Transaction transaction) + throws DbException { + T txn = unbox(transaction); + return db.getContactMailboxes(txn); + } + @Override public Group getGroup(Transaction transaction, GroupId g) throws DbException { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java index eadad3e42c66c15606a9630dc405cf636ca49f61..eb88aaf4f75cc7a27d3edf1085d5602df57f25d8 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java @@ -15,6 +15,7 @@ import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.mailbox.MailboxInfo; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.settings.Settings; @@ -278,6 +279,19 @@ abstract class JdbcDatabase implements Database<Connection> { + " REFERENCES contacts (contactId)" + " ON DELETE CASCADE)"; + private static final String CREATE_MAILBOXES = + "CREATE TABLE mailboxes" + + " (contactId INT NOT NULL," + + " mailboxId INT," + + " aliasId INT," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (mailboxId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"; + private static final String INDEX_CONTACTS_BY_AUTHOR_ID = "CREATE INDEX IF NOT EXISTS contactsByAuthorId" + " ON contacts (authorId)"; @@ -439,6 +453,7 @@ abstract class JdbcDatabase implements Database<Connection> { s.executeUpdate(insertTypeNames(CREATE_TRANSPORTS)); s.executeUpdate(insertTypeNames(CREATE_OUTGOING_KEYS)); s.executeUpdate(insertTypeNames(CREATE_INCOMING_KEYS)); + s.executeUpdate(insertTypeNames(CREATE_MAILBOXES)); s.close(); } catch (SQLException e) { tryToClose(s); @@ -970,6 +985,29 @@ abstract class JdbcDatabase implements Database<Connection> { } } + @Override + public void setMailboxForContact(Connection txn, ContactId contactId, + ContactId mailboxId, ContactId aliasId) + throws DbException { + PreparedStatement ps = null; + try { + String sql = "INSERT INTO mailboxes (contactId, mailboxId, aliasId)" + + " VALUES (?, ?, ?)"; + ps = txn.prepareStatement(sql); + ps.setInt(1, contactId.getInt()); + if (mailboxId == null) ps.setNull(2, INTEGER); + else ps.setInt(1, mailboxId.getInt()); + if (aliasId == null) ps.setNull(3, INTEGER); + else ps.setInt(3, aliasId.getInt()); + int affected = ps.executeUpdate(); + if (affected != 1) throw new DbStateException(); + ps.close(); + } catch (SQLException e) { + tryToClose(ps); + throw new DbException(e); + } + } + @Override public boolean containsContact(Connection txn, AuthorId remote, AuthorId local) throws DbException { @@ -1369,6 +1407,34 @@ abstract class JdbcDatabase implements Database<Connection> { } } + @Override + public Collection<MailboxInfo> getContactMailboxes(Connection txn) + throws DbException { + PreparedStatement ps = null; + ResultSet rs = null; + try { + String sql = "SELECT contactId, mailboxId, aliasId FROM mailboxes"; + ps = txn.prepareStatement(sql); + rs = ps.executeQuery(); + List<MailboxInfo> mailboxInfos = new ArrayList<>(); + while (rs.next()) { + int contactId = rs.getInt(1); + int mailboxid = rs.getInt(2); + int aliasId = rs.getInt(3); + mailboxInfos.add(new MailboxInfo(new ContactId(contactId), + mailboxid == 0 ? null : new ContactId(mailboxid), + aliasId == 0 ? null : new ContactId(aliasId))); + } + rs.close(); + ps.close(); + return mailboxInfos; + } catch (SQLException e) { + tryToClose(rs); + tryToClose(ps); + throw new DbException(e); + } + } + @Override public Group getGroup(Connection txn, GroupId g) throws DbException { PreparedStatement ps = null; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java index ca86b1d0675f0ea0477bacca5d75aa8c2e7cd889..ba9e56071afa81e3b495d7aae8a763226f2c02c7 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Migration39_40.java @@ -41,6 +41,17 @@ class Migration39_40 implements Migration<Connection> { // Set type NOT NULL s.execute("ALTER TABLE contacts" + " ALTER COLUMN type INT NOT NULL"); + s.execute("CREATE TABLE mailboxes" + + " (contactId INT NOT NULL," + + " mailboxId INT," + + " aliasId INT," + + " PRIMARY KEY (contactId)," + + " FOREIGN KEY (contactId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE," + + " FOREIGN KEY (mailboxId)" + + " REFERENCES contacts (contactId)" + + " ON DELETE CASCADE)"); } catch (SQLException e) { tryToClose(s); throw new DbException(e); diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java index 811f3a351008aaca919437fc2519d3d47087ba57..56709159dca46cdb4cf6f914e22357475d6355ba 100644 --- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java @@ -173,6 +173,7 @@ public class TestDataCreatorImpl implements TestDataCreator { .addContact(txn, author, localAuthor.getId(), secretKey, timestamp, true, verified, true); transportPropertyManager.addRemoteProperties(txn, contactId, props); + db.setMailboxForContact(txn, contactId, null, contactId); contact = db.getContact(txn, contactId); db.commitTransaction(txn); } finally {