diff --git a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTest.java index ae3e770a4e944e48fc201490cee7e8965c0876e0..306cc25dc1b37b06c5a8c0fe6e12b29c000c7a2c 100644 --- a/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTest.java +++ b/briar-android-tests/src/test/java/org/briarproject/ForumSharingIntegrationTest.java @@ -34,7 +34,7 @@ import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.IdentityManager; import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.lifecycle.LifecycleManager; -import org.briarproject.api.sharing.InvitationItem; +import org.briarproject.api.sharing.SharingInvitationItem; import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sync.Group; import org.briarproject.api.sync.SyncSession; @@ -762,7 +762,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase { "Sharer2 to Invitee"); // make sure we now have two invitations to the same forum available - Collection<InvitationItem> forums = + Collection<SharingInvitationItem> forums = forumSharingManager1.getInvitations(); assertEquals(1, forums.size()); assertEquals(2, forums.iterator().next().getNewSharers().size()); @@ -986,7 +986,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase { try { eventWaiter.assertEquals(1, forumSharingManager1.getInvitations().size()); - InvitationItem invitation = + SharingInvitationItem invitation = forumSharingManager1.getInvitations().iterator() .next(); eventWaiter.assertEquals(f, invitation.getShareable()); diff --git a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java index 310611361b960acc44ef6b5e9f6d30207f488360..9b57d999108f822d2d777cef6ef391db9c7a6810 100644 --- a/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java +++ b/briar-api/src/org/briarproject/api/privategroup/PrivateGroup.java @@ -3,6 +3,7 @@ package org.briarproject.api.privategroup; import org.briarproject.api.clients.NamedGroup; import org.briarproject.api.identity.Author; import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.sharing.Shareable; import org.briarproject.api.sync.Group; import org.jetbrains.annotations.NotNull; @@ -10,7 +11,7 @@ import javax.annotation.concurrent.Immutable; @Immutable @NotNullByDefault -public class PrivateGroup extends NamedGroup { +public class PrivateGroup extends NamedGroup implements Shareable { private final Author author; diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationConstants.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..a22272b22b2f3bb4549e7abebc4e65dc2677694a --- /dev/null +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationConstants.java @@ -0,0 +1,8 @@ +package org.briarproject.api.privategroup.invitation; + +public interface GroupInvitationConstants { + + // Group Metadata Keys + String CONTACT_ID = "contactId"; + +} diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java new file mode 100644 index 0000000000000000000000000000000000000000..c7f350be45f1c8bc4ed07476ceb6cabcc9b46344 --- /dev/null +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationItem.java @@ -0,0 +1,22 @@ +package org.briarproject.api.privategroup.invitation; + +import org.briarproject.api.contact.Contact; +import org.briarproject.api.sharing.InvitationItem; +import org.briarproject.api.sharing.Shareable; + +public class GroupInvitationItem extends InvitationItem { + + private final Contact creator; + + public GroupInvitationItem(Shareable shareable, boolean subscribed, + Contact creator) { + super(shareable, subscribed); + + this.creator = creator; + } + + public Contact getCreator() { + return creator; + } + +} diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java new file mode 100644 index 0000000000000000000000000000000000000000..a5ca33dae143336d889b38e657f3c10ca6090a3b --- /dev/null +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationManager.java @@ -0,0 +1,48 @@ +package org.briarproject.api.privategroup.invitation; + +import org.briarproject.api.clients.MessageTracker; +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.db.DbException; +import org.briarproject.api.privategroup.PrivateGroup; +import org.briarproject.api.sharing.InvitationMessage; +import org.briarproject.api.sync.ClientId; +import org.briarproject.api.sync.GroupId; + +import java.util.Collection; + +public interface GroupInvitationManager extends MessageTracker { + + /** Returns the unique ID of the private group invitation client. */ + ClientId getClientId(); + + /** + * Sends an invitation to share the given forum with the given contact + * and sends an optional message along with it. + */ + void sendInvitation(GroupId groupId, ContactId contactId, + String message) throws DbException; + + /** + * Responds to a pending private group invitation + */ + void respondToInvitation(PrivateGroup g, Contact c, boolean accept) + throws DbException; + + /** + * Responds to a pending private group invitation + */ + void respondToInvitation(SessionId id, boolean accept) throws DbException; + + /** + * Returns all private group invitation messages related to the contact + * identified by contactId. + */ + Collection<InvitationMessage> getInvitationMessages( + ContactId contactId) throws DbException; + + /** Returns all private groups to which the user has been invited. */ + Collection<GroupInvitationItem> getInvitations() throws DbException; + +} diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..3c9b25760f4611216c9abe1e9cde628d678a2afc --- /dev/null +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationRequest.java @@ -0,0 +1,38 @@ +package org.briarproject.api.privategroup.invitation; + +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.identity.Author; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.sharing.InvitationRequest; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; + +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +@NotNullByDefault +public class GroupInvitationRequest extends InvitationRequest { + + private final String groupName; + private final Author creator; + + public GroupInvitationRequest(MessageId id, SessionId sessionId, + GroupId groupId, Author creator, ContactId contactId, + String groupName, String message, boolean available, long time, + boolean local, boolean sent, boolean seen, boolean read) { + super(id, sessionId, groupId, contactId, message, available, time, + local, sent, seen, read); + this.groupName = groupName; + this.creator = creator; + } + + public String getGroupName() { + return groupName; + } + + public Author getCreator() { + return creator; + } + +} diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..554e731bcde3eef262dd25586d2b75090ca0b055 --- /dev/null +++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationResponse.java @@ -0,0 +1,39 @@ +package org.briarproject.api.privategroup.invitation; + +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.identity.Author; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.sharing.InvitationResponse; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.MessageId; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +@NotNullByDefault +public class GroupInvitationResponse extends InvitationResponse { + + private final String groupName; + private final Author creator; + + public GroupInvitationResponse(MessageId id, SessionId sessionId, + GroupId groupId, String groupName, Author creator, + ContactId contactId, boolean accept, long time, boolean local, + boolean sent, boolean seen, boolean read) { + super(id, sessionId, groupId, contactId, accept, time, local, sent, + seen, read); + this.groupName = groupName; + this.creator = creator; + } + + public String getGroupName() { + return groupName; + } + + public Author getCreator() { + return creator; + } + +} diff --git a/briar-api/src/org/briarproject/api/sharing/InvitationItem.java b/briar-api/src/org/briarproject/api/sharing/InvitationItem.java index bc180ea13f011cd895048d46c4b54ee79030462b..a813982b1025f8ad6c4c294458abff86a47dfb53 100644 --- a/briar-api/src/org/briarproject/api/sharing/InvitationItem.java +++ b/briar-api/src/org/briarproject/api/sharing/InvitationItem.java @@ -1,32 +1,36 @@ package org.briarproject.api.sharing; -import org.briarproject.api.contact.Contact; +import org.briarproject.api.nullsafety.NotNullByDefault; +import org.briarproject.api.sync.GroupId; -import java.util.Collection; +import javax.annotation.concurrent.ThreadSafe; -public class InvitationItem { +@ThreadSafe +@NotNullByDefault +public abstract class InvitationItem { private final Shareable shareable; private final boolean subscribed; - private final Collection<Contact> newSharers; - - public InvitationItem(Shareable shareable, boolean subscribed, - Collection<Contact> newSharers) { + public InvitationItem(Shareable shareable, boolean subscribed) { this.shareable = shareable; this.subscribed = subscribed; - this.newSharers = newSharers; } public Shareable getShareable() { return shareable; } + public GroupId getId() { + return shareable.getId(); + } + + public String getName() { + return shareable.getName(); + } + public boolean isSubscribed() { return subscribed; } - public Collection<Contact> getNewSharers() { - return newSharers; - } } diff --git a/briar-api/src/org/briarproject/api/sharing/Shareable.java b/briar-api/src/org/briarproject/api/sharing/Shareable.java index 13c11fdeeff5b39d3f775b293281bcfabc08d097..144b06dde1db37991a3258b4970d91dfbbfa7443 100644 --- a/briar-api/src/org/briarproject/api/sharing/Shareable.java +++ b/briar-api/src/org/briarproject/api/sharing/Shareable.java @@ -8,4 +8,7 @@ public interface Shareable { GroupId getId(); Group getGroup(); + + String getName(); + } diff --git a/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java b/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java new file mode 100644 index 0000000000000000000000000000000000000000..8993f232001c73dc1b525f69248ea886302c35a4 --- /dev/null +++ b/briar-api/src/org/briarproject/api/sharing/SharingInvitationItem.java @@ -0,0 +1,22 @@ +package org.briarproject.api.sharing; + +import org.briarproject.api.contact.Contact; + +import java.util.Collection; + +public class SharingInvitationItem extends InvitationItem { + + private final Collection<Contact> newSharers; + + public SharingInvitationItem(Shareable shareable, boolean subscribed, + Collection<Contact> newSharers) { + super(shareable, subscribed); + + this.newSharers = newSharers; + } + + public Collection<Contact> getNewSharers() { + return newSharers; + } + +} diff --git a/briar-api/src/org/briarproject/api/sharing/SharingManager.java b/briar-api/src/org/briarproject/api/sharing/SharingManager.java index b4613cd94035ef209d7d82b24c83a6126a841453..3e37191ddddb23a10b17e528103ab7aa9dffa1dc 100644 --- a/briar-api/src/org/briarproject/api/sharing/SharingManager.java +++ b/briar-api/src/org/briarproject/api/sharing/SharingManager.java @@ -38,7 +38,7 @@ public interface SharingManager<S extends Shareable> extends MessageTracker { /** * Returns all invitations to groups. */ - Collection<InvitationItem> getInvitations() throws DbException; + Collection<SharingInvitationItem> getInvitations() throws DbException; /** * Returns all contacts who are sharing the given group with us. diff --git a/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java b/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java index 562b95c308cf3d25ca63a982631a56bc72f14c9d..570f697a629d952be8790dd5b27e04e1a21a2a90 100644 --- a/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java +++ b/briar-core/src/org/briarproject/privategroup/PrivateGroupModule.java @@ -1,17 +1,19 @@ package org.briarproject.privategroup; import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.contact.ContactManager; import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.identity.AuthorFactory; +import org.briarproject.api.lifecycle.LifecycleManager; +import org.briarproject.api.messaging.ConversationManager; import org.briarproject.api.privategroup.GroupMessageFactory; import org.briarproject.api.privategroup.PrivateGroupFactory; import org.briarproject.api.privategroup.PrivateGroupManager; -import org.briarproject.api.sync.GroupFactory; +import org.briarproject.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.system.Clock; - -import java.security.SecureRandom; +import org.briarproject.privategroup.invitation.GroupInvitationManagerImpl; import javax.inject.Inject; import javax.inject.Singleton; @@ -25,6 +27,8 @@ public class PrivateGroupModule { public static class EagerSingletons { @Inject GroupMessageValidator groupMessageValidator; + @Inject + GroupInvitationManager groupInvitationManager; } @Provides @@ -65,4 +69,22 @@ public class PrivateGroupModule { return validator; } + @Provides + @Singleton + GroupInvitationManager provideGroupInvitationManager( + LifecycleManager lifecycleManager, ContactManager contactManager, + GroupInvitationManagerImpl groupInvitationManager, + ConversationManager conversationManager, + ValidationManager validationManager) { + + validationManager.registerIncomingMessageHook( + groupInvitationManager.getClientId(), groupInvitationManager); + lifecycleManager.registerClient(groupInvitationManager); + contactManager.registerAddContactHook(groupInvitationManager); + contactManager.registerRemoveContactHook(groupInvitationManager); + conversationManager.registerConversationClient(groupInvitationManager); + + return groupInvitationManager; + } + } diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..12477ab20b994595192b2e53b2bef17944e0f9c2 --- /dev/null +++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationManagerImpl.java @@ -0,0 +1,141 @@ +package org.briarproject.privategroup.invitation; + +import org.briarproject.api.FormatException; +import org.briarproject.api.clients.Client; +import org.briarproject.api.clients.ClientHelper; +import org.briarproject.api.clients.ContactGroupFactory; +import org.briarproject.api.clients.SessionId; +import org.briarproject.api.contact.Contact; +import org.briarproject.api.contact.ContactId; +import org.briarproject.api.contact.ContactManager; +import org.briarproject.api.data.BdfDictionary; +import org.briarproject.api.data.BdfList; +import org.briarproject.api.data.MetadataParser; +import org.briarproject.api.db.DatabaseComponent; +import org.briarproject.api.db.DbException; +import org.briarproject.api.db.Transaction; +import org.briarproject.api.messaging.ConversationManager; +import org.briarproject.api.privategroup.PrivateGroup; +import org.briarproject.api.privategroup.invitation.GroupInvitationItem; +import org.briarproject.api.privategroup.invitation.GroupInvitationManager; +import org.briarproject.api.sharing.InvitationMessage; +import org.briarproject.api.sync.ClientId; +import org.briarproject.api.sync.Group; +import org.briarproject.api.sync.GroupId; +import org.briarproject.api.sync.Message; +import org.briarproject.clients.ConversationClientImpl; +import org.briarproject.util.StringUtils; + +import java.util.ArrayList; +import java.util.Collection; + +import javax.inject.Inject; + +import static org.briarproject.api.privategroup.invitation.GroupInvitationConstants.CONTACT_ID; + +public class GroupInvitationManagerImpl extends ConversationClientImpl + implements GroupInvitationManager, Client, + ContactManager.AddContactHook, ContactManager.RemoveContactHook, + ConversationManager.ConversationClient { + + private static final ClientId CLIENT_ID = + new ClientId(StringUtils.fromHexString( + "B55231ABFC4A10666CD93D649B1D7F4F" + + "016E65B87BB4C04F4E35613713DBCD13")); + + private final ContactGroupFactory contactGroupFactory; + private final Group localGroup; + + @Inject + protected GroupInvitationManagerImpl(DatabaseComponent db, + ClientHelper clientHelper, MetadataParser metadataParser, + ContactGroupFactory contactGroupFactory) { + super(db, clientHelper, metadataParser); + this.contactGroupFactory = contactGroupFactory; + localGroup = contactGroupFactory.createLocalGroup(getClientId()); + } + + @Override + public ClientId getClientId() { + return CLIENT_ID; + } + + @Override + public void createLocalState(Transaction txn) throws DbException { + db.addGroup(txn, localGroup); + // Ensure we've set things up for any pre-existing contacts + for (Contact c : db.getContacts(txn)) addingContact(txn, c); + } + + @Override + public void addingContact(Transaction txn, Contact c) throws DbException { + try { + // Create a group to share with the contact + Group g = getContactGroup(c); + // Return if we've already set things up for this contact + if (db.containsGroup(txn, g.getId())) return; + // Store the group and share it with the contact + db.addGroup(txn, g); + db.setVisibleToContact(txn, c.getId(), g.getId(), true); + // Attach the contact ID to the group + BdfDictionary meta = new BdfDictionary(); + meta.put(CONTACT_ID, c.getId().getInt()); + clientHelper.mergeGroupMetadata(txn, g.getId(), meta); + } catch (FormatException e) { + throw new DbException(e); + } + } + + @Override + public void removingContact(Transaction txn, Contact c) throws DbException { + // remove the contact group (all messages will be removed with it) + db.removeGroup(txn, getContactGroup(c)); + } + + @Override + protected Group getContactGroup(Contact c) { + return contactGroupFactory.createContactGroup(getClientId(), c); + } + + @Override + protected boolean incomingMessage(Transaction txn, Message m, BdfList body, + BdfDictionary meta) throws DbException, FormatException { + return false; + } + + @Override + public void sendInvitation(GroupId groupId, ContactId contactId, + String message) throws DbException { + + } + + @Override + public void respondToInvitation(PrivateGroup g, Contact c, boolean accept) + throws DbException { + + } + + @Override + public void respondToInvitation(SessionId id, boolean accept) + throws DbException { + + } + + @Override + public Collection<InvitationMessage> getInvitationMessages( + ContactId contactId) throws DbException { + Collection<InvitationMessage> invitations = + new ArrayList<InvitationMessage>(); + + return invitations; + } + + @Override + public Collection<GroupInvitationItem> getInvitations() throws DbException { + Collection<GroupInvitationItem> invitations = + new ArrayList<GroupInvitationItem>(); + + return invitations; + } + +} diff --git a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java index a7c90d0a78f45edcf201803856d1cce078572517..dd06fc2c447630f69ebf5ba7e4dc9025bf01dfdc 100644 --- a/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java +++ b/briar-core/src/org/briarproject/sharing/SharingManagerImpl.java @@ -25,7 +25,7 @@ import org.briarproject.api.event.Event; import org.briarproject.api.event.InvitationRequestReceivedEvent; import org.briarproject.api.event.InvitationResponseReceivedEvent; import org.briarproject.api.identity.LocalAuthor; -import org.briarproject.api.sharing.InvitationItem; +import org.briarproject.api.sharing.SharingInvitationItem; import org.briarproject.api.sharing.InvitationMessage; import org.briarproject.api.sharing.Shareable; import org.briarproject.api.sharing.SharingManager; @@ -418,8 +418,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS } @Override - public Collection<InvitationItem> getInvitations() throws DbException { - List<InvitationItem> invitations = new ArrayList<InvitationItem>(); + public Collection<SharingInvitationItem> getInvitations() throws DbException { + List<SharingInvitationItem> invitations = new ArrayList<SharingInvitationItem>(); Transaction txn = db.startTransaction(true); try { Set<S> shareables = new HashSet<S>(); @@ -445,8 +445,8 @@ abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IS for (S s : shareables) { Collection<Contact> newS = newSharers.get(s.getId()); boolean subscribed = db.containsGroup(txn, s.getId()); - InvitationItem invitation = - new InvitationItem(s, subscribed, newS); + SharingInvitationItem invitation = + new SharingInvitationItem(s, subscribed, newS); invitations.add(invitation); } txn.setComplete();