Factor out generic sharing code from ForumSharingManger

This commit is contained in:
str4d
2016-06-16 04:26:49 +00:00
parent 5df2776dc2
commit 9ae64124d3
49 changed files with 1723 additions and 1130 deletions

View File

@@ -31,6 +31,7 @@ import org.briarproject.crypto.CryptoModule;
import org.briarproject.forum.ForumModule; import org.briarproject.forum.ForumModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import org.briarproject.util.StringUtils; import org.briarproject.util.StringUtils;
@@ -188,7 +189,7 @@ public class ForumManagerTest {
// share forum // share forum
GroupId g = forum0.getId(); GroupId g = forum0.getId();
forumSharingManager0.sendForumInvitation(g, contactId1, null); forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1(); sync0To1();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0); Contact c0 = contactManager1.getContact(contactId0);
@@ -218,7 +219,7 @@ public class ForumManagerTest {
// share forum // share forum
GroupId g = forum0.getId(); GroupId g = forum0.getId();
forumSharingManager0.sendForumInvitation(g, contactId1, null); forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1(); sync0To1();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0); Contact c0 = contactManager1.getContact(contactId0);
@@ -259,7 +260,7 @@ public class ForumManagerTest {
// share forum // share forum
GroupId g = forum0.getId(); GroupId g = forum0.getId();
forumSharingManager0.sendForumInvitation(g, contactId1, null); forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1(); sync0To1();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0); Contact c0 = contactManager1.getContact(contactId0);
@@ -270,7 +271,7 @@ public class ForumManagerTest {
// share a second forum // share a second forum
Forum forum1 = forumManager0.addForum("Test Forum1"); Forum forum1 = forumManager0.addForum("Test Forum1");
GroupId g1 = forum1.getId(); GroupId g1 = forum1.getId();
forumSharingManager0.sendForumInvitation(g1, contactId1, null); forumSharingManager0.sendInvitation(g1, contactId1, null);
sync0To1(); sync0To1();
deliveryWaiter.await(TIMEOUT, 1); deliveryWaiter.await(TIMEOUT, 1);
forumSharingManager1.respondToInvitation(forum1, c0, true); forumSharingManager1.respondToInvitation(forum1, c0, true);
@@ -426,6 +427,7 @@ public class ForumManagerTest {
component.inject(new CryptoModule.EagerSingletons()); component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons()); component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons()); component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons()); component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons()); component.inject(new PropertiesModule.EagerSingletons());
} }

View File

@@ -17,6 +17,7 @@ import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule; import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
@@ -40,6 +41,7 @@ import dagger.Component;
IdentityModule.class, IdentityModule.class,
LifecycleModule.class, LifecycleModule.class,
PropertiesModule.class, PropertiesModule.class,
SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class
@@ -58,6 +60,8 @@ public interface ForumManagerTestComponent {
void inject(PropertiesModule.EagerSingletons init); void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);

View File

@@ -39,6 +39,7 @@ import org.briarproject.crypto.CryptoModule;
import org.briarproject.forum.ForumModule; import org.briarproject.forum.ForumModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
import org.junit.After; import org.junit.After;
@@ -61,8 +62,8 @@ import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY; import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED; import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID; import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -159,7 +160,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -172,13 +173,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
// forum was added successfully // forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size()); assertEquals(0, forumSharingManager0.getAvailable().size());
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
// invitee has one invitation message from sharer // invitee has one invitation message from sharer
List<ForumInvitationMessage> list = List<ForumInvitationMessage> list =
new ArrayList<>(forumSharingManager1 new ArrayList<>(forumSharingManager1
.getForumInvitationMessages(contactId0)); .getInvitationMessages(contactId0));
assertEquals(1, list.size()); assertEquals(1, list.size());
// check other things are alright with the forum message // check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0); ForumInvitationMessage invitation = list.get(0);
@@ -188,7 +189,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals("Hi!", invitation.getMessage()); assertEquals("Hi!", invitation.getMessage());
// sharer has own invitation message // sharer has own invitation message
assertEquals(1, assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1) forumSharingManager0.getInvitationMessages(contactId1)
.size()); .size());
// forum can not be shared again // forum can not be shared again
Contact c1 = contactManager0.getContact(contactId1); Contact c1 = contactManager0.getContact(contactId1);
@@ -209,7 +210,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null); .sendInvitation(forum0.getId(), contactId1, null);
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -222,15 +223,15 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
// forum was not added // forum was not added
assertEquals(0, forumSharingManager0.getAvailableForums().size()); assertEquals(0, forumSharingManager0.getAvailable().size());
assertEquals(0, forumManager1.getForums().size()); assertEquals(0, forumManager1.getForums().size());
// forum is no longer available to invitee who declined // forum is no longer available to invitee who declined
assertEquals(0, forumSharingManager1.getAvailableForums().size()); assertEquals(0, forumSharingManager1.getAvailable().size());
// invitee has one invitation message from sharer // invitee has one invitation message from sharer
List<ForumInvitationMessage> list = List<ForumInvitationMessage> list =
new ArrayList<>(forumSharingManager1 new ArrayList<>(forumSharingManager1
.getForumInvitationMessages(contactId0)); .getInvitationMessages(contactId0));
assertEquals(1, list.size()); assertEquals(1, list.size());
// check other things are alright with the forum message // check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0); ForumInvitationMessage invitation = list.get(0);
@@ -240,7 +241,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(null, invitation.getMessage()); assertEquals(null, invitation.getMessage());
// sharer has own invitation message // sharer has own invitation message
assertEquals(1, assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1) forumSharingManager0.getInvitationMessages(contactId1)
.size()); .size());
// forum can be shared again // forum can be shared again
Contact c1 = contactManager0.getContact(contactId1); Contact c1 = contactManager0.getContact(contactId1);
@@ -259,7 +260,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -272,7 +273,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
// forum was added successfully // forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size()); assertEquals(0, forumSharingManager0.getAvailable().size());
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0)); assertTrue(forumManager1.getForums().contains(forum0));
@@ -292,7 +293,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
syncToSharer(); syncToSharer();
// forum is gone // forum is gone
assertEquals(0, forumSharingManager0.getAvailableForums().size()); assertEquals(0, forumSharingManager0.getAvailable().size());
assertEquals(0, forumManager1.getForums().size()); assertEquals(0, forumManager1.getForums().size());
// sharer no longer shares forum with invitee // sharer no longer shares forum with invitee
@@ -319,7 +320,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null); .sendInvitation(forum0.getId(), contactId1, null);
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -332,7 +333,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
// forum was added successfully // forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size()); assertEquals(0, forumSharingManager0.getAvailable().size());
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0)); assertTrue(forumManager1.getForums().contains(forum0));
@@ -378,7 +379,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null); .sendInvitation(forum0.getId(), contactId1, null);
// sharer un-subscribes from forum // sharer un-subscribes from forum
forumManager0.removeForum(forum0); forumManager0.removeForum(forum0);
@@ -392,7 +393,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener1.requestReceived); assertTrue(listener1.requestReceived);
// invitee has no forums available // invitee has no forums available
assertEquals(0, forumSharingManager1.getAvailableForums().size()); assertEquals(0, forumSharingManager1.getAvailable().size());
} finally { } finally {
stopLifecycles(); stopLifecycles();
} }
@@ -407,7 +408,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -428,7 +429,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// get SessionId from invitation // get SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>( List<ForumInvitationMessage> list = new ArrayList<>(
forumSharingManager1 forumSharingManager1
.getForumInvitationMessages(contactId0)); .getInvitationMessages(contactId0));
assertEquals(1, list.size()); assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0); ForumInvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
@@ -475,7 +476,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -491,7 +492,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(1, forumManager1.getForums().size()); assertEquals(1, forumManager1.getForums().size());
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendForumInvitation(forum0.getId(), forumSharingManager1.sendInvitation(forum0.getId(),
contactId0, contactId0,
"I am re-sharing this forum with you."); "I am re-sharing this forum with you.");
@@ -501,7 +502,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// make sure that no new request was received // make sure that no new request was received
assertFalse(listener0.requestReceived); assertFalse(listener0.requestReceived);
assertEquals(1, assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1) forumSharingManager0.getInvitationMessages(contactId1)
.size()); .size());
} finally { } finally {
stopLifecycles(); stopLifecycles();
@@ -524,10 +525,10 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// invitee now shares same forum back // invitee now shares same forum back
forumSharingManager1.sendForumInvitation(forum0.getId(), forumSharingManager1.sendInvitation(forum0.getId(),
contactId0, "I am re-sharing this forum with you."); contactId0, "I am re-sharing this forum with you.");
// find out who should be Alice, because of random keys // find out who should be Alice, because of random keys
@@ -555,9 +556,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived); assertTrue(listener0.responseReceived);
assertEquals(1, forumSharingManager0 assertEquals(1, forumSharingManager0
.getForumInvitationMessages(contactId1).size()); .getInvitationMessages(contactId1).size());
assertEquals(2, forumSharingManager1 assertEquals(2, forumSharingManager1
.getForumInvitationMessages(contactId0).size()); .getInvitationMessages(contactId0).size());
} else { } else {
eventWaiter.await(TIMEOUT, 1); eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.requestReceived); assertTrue(listener0.requestReceived);
@@ -568,9 +569,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener1.responseReceived); assertTrue(listener1.responseReceived);
assertEquals(2, forumSharingManager0 assertEquals(2, forumSharingManager0
.getForumInvitationMessages(contactId1).size()); .getInvitationMessages(contactId1).size());
assertEquals(1, forumSharingManager1 assertEquals(1, forumSharingManager1
.getForumInvitationMessages(contactId0).size()); .getInvitationMessages(contactId0).size());
} }
} finally { } finally {
stopLifecycles(); stopLifecycles();
@@ -586,7 +587,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
@@ -606,7 +607,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// remember SessionId from invitation // remember SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>( List<ForumInvitationMessage> list = new ArrayList<>(
forumSharingManager1 forumSharingManager1
.getForumInvitationMessages(contactId0)); .getInvitationMessages(contactId0));
assertEquals(1, list.size()); assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0); ForumInvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
@@ -687,20 +688,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation // send invitation
forumSharingManager0 forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!"); .sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message // sync first request message
syncToInvitee(); syncToInvitee();
// second sharer sends invitation for same forum // second sharer sends invitation for same forum
forumSharingManager2 forumSharingManager2
.sendForumInvitation(forum0.getId(), contactId1, null); .sendInvitation(forum0.getId(), contactId1, null);
// sync second request message // sync second request message
deliverMessage(sync2, contactId2, sync1, contactId1, deliverMessage(sync2, contactId2, sync1, contactId1,
"Sharer2 to Invitee"); "Sharer2 to Invitee");
// make sure we have only one forum available // make sure we have only one forum available
Collection<Forum> forums = Collection<Forum> forums =
forumSharingManager1.getAvailableForums(); forumSharingManager1.getAvailable();
assertEquals(1, forums.size()); assertEquals(1, forums.size());
// make sure both sharers actually share the forum // make sure both sharers actually share the forum
@@ -958,6 +959,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
component.inject(new CryptoModule.EagerSingletons()); component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons()); component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons()); component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons()); component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons()); component.inject(new PropertiesModule.EagerSingletons());
} }

View File

@@ -21,6 +21,7 @@ import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule; import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule; import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule; import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
@@ -44,6 +45,7 @@ import dagger.Component;
IdentityModule.class, IdentityModule.class,
LifecycleModule.class, LifecycleModule.class,
PropertiesModule.class, PropertiesModule.class,
SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class
@@ -62,6 +64,8 @@ public interface ForumSharingIntegrationTestComponent {
void inject(PropertiesModule.EagerSingletons init); void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init); void inject(TransportModule.EagerSingletons init);

View File

@@ -352,7 +352,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
now = System.currentTimeMillis(); now = System.currentTimeMillis();
Collection<ForumInvitationMessage> invitations = Collection<ForumInvitationMessage> invitations =
forumSharingManager.getForumInvitationMessages(id); forumSharingManager.getInvitationMessages(id);
for (ForumInvitationMessage i : invitations) { for (ForumInvitationMessage i : invitations) {
messages.add(ConversationItem.from(i)); messages.add(ConversationItem.from(i));
} }

View File

@@ -316,7 +316,7 @@ public class ConversationActivity extends BriarActivity
.getIntroductionMessages(contactId); .getIntroductionMessages(contactId);
Collection<ForumInvitationMessage> invitations = Collection<ForumInvitationMessage> invitations =
forumSharingManager forumSharingManager
.getForumInvitationMessages(contactId); .getInvitationMessages(contactId);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Loading headers took " + duration + " ms"); LOG.info("Loading headers took " + duration + " ms");

View File

@@ -82,7 +82,7 @@ public class AvailableForumsActivity extends BriarActivity
try { try {
Collection<ForumContacts> available = new ArrayList<>(); Collection<ForumContacts> available = new ArrayList<>();
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
for (Forum f : forumSharingManager.getAvailableForums()) { for (Forum f : forumSharingManager.getAvailable()) {
try { try {
Collection<Contact> c = Collection<Contact> c =
forumSharingManager.getSharedBy(f.getId()); forumSharingManager.getSharedBy(f.getId());

View File

@@ -38,7 +38,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.android.forum.ShareForumActivity.CONTACTS; import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds; import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ContactSelectorFragment extends BaseFragment implements public class ContactSelectorFragment extends BaseFragment implements
BaseContactListAdapter.OnItemClickListener { BaseContactListAdapter.OnItemClickListener {

View File

@@ -177,7 +177,7 @@ public class ForumListFragment extends BaseEventFragment implements
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
int available = int available =
forumSharingManager.getAvailableForums().size(); forumSharingManager.getAvailable().size();
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Loading available took " + duration + " ms"); LOG.info("Loading available took " + duration + " ms");

View File

@@ -28,7 +28,7 @@ import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.android.forum.ShareForumActivity.CONTACTS; import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds; import static org.briarproject.android.forum.ShareForumActivity.getContactsFromIds;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
public class ShareForumMessageFragment extends BaseFragment { public class ShareForumMessageFragment extends BaseFragment {
@@ -136,7 +136,7 @@ public class ShareForumMessageFragment extends BaseFragment {
public void run() { public void run() {
try { try {
for (ContactId c : contacts) { for (ContactId c : contacts) {
forumSharingManager.sendForumInvitation(groupId, c, forumSharingManager.sendInvitation(groupId, c,
msg); msg);
} }
} catch (DbException e) { } catch (DbException e) {

View File

@@ -4,21 +4,16 @@ import org.briarproject.api.contact.ContactId;
import org.briarproject.api.forum.Forum; import org.briarproject.api.forum.Forum;
import org.briarproject.api.introduction.IntroductionRequest; import org.briarproject.api.introduction.IntroductionRequest;
public class ForumInvitationReceivedEvent extends Event { public class ForumInvitationReceivedEvent extends InvitationReceivedEvent {
private final Forum forum; private final Forum forum;
private final ContactId contactId;
public ForumInvitationReceivedEvent(Forum forum, ContactId contactId) { public ForumInvitationReceivedEvent(Forum forum, ContactId contactId) {
super(contactId);
this.forum = forum; this.forum = forum;
this.contactId = contactId;
} }
public Forum getForum() { public Forum getForum() {
return forum; return forum;
} }
public ContactId getContactId() {
return contactId;
}
} }

View File

@@ -2,23 +2,17 @@ package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
public class ForumInvitationResponseReceivedEvent extends Event { public class ForumInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
private final String forumName; private final String forumName;
private final ContactId contactId;
public ForumInvitationResponseReceivedEvent(String forumName, public ForumInvitationResponseReceivedEvent(String forumName,
ContactId contactId) { ContactId contactId) {
super(contactId);
this.forumName = forumName; this.forumName = forumName;
this.contactId = contactId;
} }
public String getForumName() { public String getForumName() {
return forumName; return forumName;
} }
public ContactId getContactId() {
return contactId;
}
} }

View File

@@ -0,0 +1,17 @@
package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.forum.Forum;
public abstract class InvitationReceivedEvent extends Event {
private final ContactId contactId;
public InvitationReceivedEvent(ContactId contactId) {
this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.api.event;
import org.briarproject.api.contact.ContactId;
public abstract class InvitationResponseReceivedEvent extends Event {
private final ContactId contactId;
public InvitationResponseReceivedEvent(ContactId contactId) {
this.contactId = contactId;
}
public ContactId getContactId() {
return contactId;
}
}

View File

@@ -1,9 +1,10 @@
package org.briarproject.api.forum; package org.briarproject.api.forum;
import org.briarproject.api.sharing.Shareable;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
public class Forum { public class Forum implements Shareable {
private final Group group; private final Group group;
private final String name; private final String name;

View File

@@ -17,37 +17,8 @@ public interface ForumConstants {
int MAX_FORUM_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024; int MAX_FORUM_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
/* Forum Sharing Constants */ /* Forum Sharing Constants */
String CONTACT_ID = "contactId";
String GROUP_ID = "groupId";
String TO_BE_SHARED_BY_US = "toBeSharedByUs";
String SHARED_BY_US = "sharedByUs";
String SHARED_WITH_US = "sharedWithUs";
String TYPE = "type";
String SESSION_ID = "sessionId";
String STORAGE_ID = "storageId";
String STATE = "state";
String LOCAL = "local";
String TIME = "time";
String READ = "read";
String IS_SHARER = "isSharer";
String FORUM_ID = "forumId";
String FORUM_NAME = "forumName"; String FORUM_NAME = "forumName";
String FORUM_SALT = "forumSalt"; String FORUM_SALT = "forumSalt";
String INVITATION_MSG = "invitationMsg";
int SHARE_MSG_TYPE_INVITATION = 1;
int SHARE_MSG_TYPE_ACCEPT = 2;
int SHARE_MSG_TYPE_DECLINE = 3;
int SHARE_MSG_TYPE_LEAVE = 4;
int SHARE_MSG_TYPE_ABORT = 5;
String TASK = "task";
int TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US = 0;
int TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US = 1;
int TASK_ADD_SHARED_FORUM = 2;
int TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US = 3;
int TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US = 4;
int TASK_SHARE_FORUM = 5;
int TASK_UNSHARE_FORUM_SHARED_BY_US = 6;
int TASK_UNSHARE_FORUM_SHARED_WITH_US = 7;
// Database keys // Database keys
String KEY_TIMESTAMP = "timestamp"; String KEY_TIMESTAMP = "timestamp";

View File

@@ -3,46 +3,25 @@ package org.briarproject.api.forum;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.messaging.BaseMessage; import org.briarproject.api.messaging.BaseMessage;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
public class ForumInvitationMessage extends BaseMessage { public class ForumInvitationMessage extends InvitationMessage {
private final SessionId sessionId; private final String forumName;
private final ContactId contactId;
private final String forumName, message;
private final boolean available;
public ForumInvitationMessage(MessageId id, SessionId sessionId, public ForumInvitationMessage(MessageId id, SessionId sessionId,
ContactId contactId, String forumName, String message, ContactId contactId, String forumName, String message,
boolean available, long time, boolean local, boolean sent, boolean available, long time, boolean local, boolean sent,
boolean seen, boolean read) { boolean seen, boolean read) {
super(id, time, local, read, sent, seen); super(id, sessionId, contactId, message, available, time, local, sent,
this.sessionId = sessionId; seen, read);
this.contactId = contactId;
this.forumName = forumName; this.forumName = forumName;
this.message = message;
this.available = available;
}
public SessionId getSessionId() {
return sessionId;
}
public ContactId getContactId() {
return contactId;
} }
public String getForumName() { public String getForumName() {
return forumName; return forumName;
} }
public String getMessage() {
return message;
}
public boolean isAvailable() {
return available;
}
} }

View File

@@ -1,15 +1,15 @@
package org.briarproject.api.forum; package org.briarproject.api.forum;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.Contact; import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException; import org.briarproject.api.db.DbException;
import org.briarproject.api.sharing.SharingManager;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import java.util.Collection; import java.util.Collection;
public interface ForumSharingManager { public interface ForumSharingManager extends SharingManager<Forum, ForumInvitationMessage> {
/** Returns the unique ID of the forum sharing client. */ /** Returns the unique ID of the forum sharing client. */
ClientId getClientId(); ClientId getClientId();
@@ -18,7 +18,7 @@ public interface ForumSharingManager {
* Sends an invitation to share the given forum with the given contact * Sends an invitation to share the given forum with the given contact
* and sends an optional message along with it. * and sends an optional message along with it.
*/ */
void sendForumInvitation(GroupId groupId, ContactId contactId, void sendInvitation(GroupId groupId, ContactId contactId,
String message) throws DbException; String message) throws DbException;
/** /**
@@ -31,11 +31,11 @@ public interface ForumSharingManager {
* Returns all forum sharing messages sent by the Contact * Returns all forum sharing messages sent by the Contact
* identified by contactId. * identified by contactId.
*/ */
Collection<ForumInvitationMessage> getForumInvitationMessages( Collection<ForumInvitationMessage> getInvitationMessages(
ContactId contactId) throws DbException; ContactId contactId) throws DbException;
/** Returns all forums to which the user could subscribe. */ /** Returns all forums to which the user could subscribe. */
Collection<Forum> getAvailableForums() throws DbException; Collection<Forum> getAvailable() throws DbException;
/** Returns all contacts who are sharing the given forum with us. */ /** Returns all contacts who are sharing the given forum with us. */
Collection<Contact> getSharedBy(GroupId g) throws DbException; Collection<Contact> getSharedBy(GroupId g) throws DbException;

View File

@@ -3,89 +3,29 @@ package org.briarproject.api.forum;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.data.BdfDictionary; import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList; import org.briarproject.api.data.BdfList;
import org.briarproject.api.sharing.SharingMessage.Invitation;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID; import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.TYPE;
public interface ForumSharingMessage { public interface ForumSharingMessage {
abstract class BaseMessage { class ForumInvitation extends Invitation {
private final GroupId groupId;
private final SessionId sessionId;
public BaseMessage(GroupId groupId, SessionId sessionId) {
this.groupId = groupId;
this.sessionId = sessionId;
}
public BdfList toBdfList() {
return BdfList.of(getType(), getSessionId());
}
public abstract BdfDictionary toBdfDictionary();
protected BdfDictionary toBdfDictionaryHelper() {
return BdfDictionary.of(
new BdfEntry(TYPE, getType()),
new BdfEntry(GROUP_ID, groupId),
new BdfEntry(SESSION_ID, sessionId)
);
}
public static BaseMessage from(GroupId groupId, BdfDictionary d)
throws FormatException {
long type = d.getLong(TYPE);
if (type == SHARE_MSG_TYPE_INVITATION)
return Invitation.from(groupId, d);
else
return SimpleMessage.from(type, groupId, d);
}
public abstract long getType();
public GroupId getGroupId() {
return groupId;
}
public SessionId getSessionId() {
return sessionId;
}
}
class Invitation extends BaseMessage {
private final String forumName; private final String forumName;
private final byte[] forumSalt; private final byte[] forumSalt;
private final String message;
public Invitation(GroupId groupId, SessionId sessionId, public ForumInvitation(GroupId groupId, SessionId sessionId,
String forumName, byte[] forumSalt, String message) { String forumName, byte[] forumSalt, String message) {
super(groupId, sessionId); super(groupId, sessionId, message);
this.forumName = forumName; this.forumName = forumName;
this.forumSalt = forumSalt; this.forumSalt = forumSalt;
this.message = message;
}
@Override
public long getType() {
return SHARE_MSG_TYPE_INVITATION;
} }
@Override @Override
@@ -106,7 +46,7 @@ public interface ForumSharingMessage {
return d; return d;
} }
public static Invitation from(GroupId groupId, BdfDictionary d) public static ForumInvitation from(GroupId groupId, BdfDictionary d)
throws FormatException { throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID)); SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
@@ -114,7 +54,7 @@ public interface ForumSharingMessage {
byte[] forumSalt = d.getRaw(FORUM_SALT); byte[] forumSalt = d.getRaw(FORUM_SALT);
String message = d.getOptionalString(INVITATION_MSG); String message = d.getOptionalString(INVITATION_MSG);
return new Invitation(groupId, sessionId, forumName, forumSalt, return new ForumInvitation(groupId, sessionId, forumName, forumSalt,
message); message);
} }
@@ -125,42 +65,5 @@ public interface ForumSharingMessage {
public byte[] getForumSalt() { public byte[] getForumSalt() {
return forumSalt; return forumSalt;
} }
public String getMessage() {
return message;
}
} }
class SimpleMessage extends BaseMessage {
private final long type;
public SimpleMessage(long type, GroupId groupId, SessionId sessionId) {
super(groupId, sessionId);
this.type = type;
}
@Override
public long getType() {
return type;
}
@Override
public BdfDictionary toBdfDictionary() {
return toBdfDictionaryHelper();
}
public static SimpleMessage from(long type, GroupId groupId,
BdfDictionary d) throws FormatException {
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
return new SimpleMessage(type, groupId, sessionId);
}
}
} }

View File

@@ -0,0 +1,10 @@
package org.briarproject.api.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
public interface InvitationFactory<I extends SharingMessage.Invitation> {
I build(GroupId groupId, BdfDictionary d) throws FormatException;
}

View File

@@ -0,0 +1,43 @@
package org.briarproject.api.sharing;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.messaging.BaseMessage;
import org.briarproject.api.sync.MessageId;
public abstract class InvitationMessage extends BaseMessage {
private final SessionId sessionId;
private final ContactId contactId;
private final String message;
private final boolean available;
public InvitationMessage(MessageId id, SessionId sessionId,
ContactId contactId, String message,
boolean available, long time, boolean local, boolean sent,
boolean seen, boolean read) {
super(id, time, local, read, sent, seen);
this.sessionId = sessionId;
this.contactId = contactId;
this.message = message;
this.available = available;
}
public SessionId getSessionId() {
return sessionId;
}
public ContactId getContactId() {
return contactId;
}
public String getMessage() {
return message;
}
public boolean isAvailable() {
return available;
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.api.sharing;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
public interface Shareable {
GroupId getId();
Group getGroup();
}

View File

@@ -0,0 +1,38 @@
package org.briarproject.api.sharing;
public interface SharingConstants {
/** The length of a sharing session's random salt in bytes. */
int SHARING_SALT_LENGTH = 32;
String CONTACT_ID = "contactId";
String GROUP_ID = "groupId";
String TO_BE_SHARED_BY_US = "toBeSharedByUs";
String SHARED_BY_US = "sharedByUs";
String SHARED_WITH_US = "sharedWithUs";
String TYPE = "type";
String SESSION_ID = "sessionId";
String STORAGE_ID = "storageId";
String STATE = "state";
String LOCAL = "local";
String TIME = "time";
String READ = "read";
String IS_SHARER = "isSharer";
String SHAREABLE_ID = "shareableId";
String INVITATION_MSG = "invitationMsg";
int SHARE_MSG_TYPE_INVITATION = 1;
int SHARE_MSG_TYPE_ACCEPT = 2;
int SHARE_MSG_TYPE_DECLINE = 3;
int SHARE_MSG_TYPE_LEAVE = 4;
int SHARE_MSG_TYPE_ABORT = 5;
String TASK = "task";
int TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US = 0;
int TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US = 1;
int TASK_ADD_SHARED_SHAREABLE = 2;
int TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US = 3;
int TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US = 4;
int TASK_SHARE_SHAREABLE = 5;
int TASK_UNSHARE_SHAREABLE_SHARED_BY_US = 6;
int TASK_UNSHARE_SHAREABLE_SHARED_WITH_US = 7;
}

View File

@@ -0,0 +1,50 @@
package org.briarproject.api.sharing;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId;
import java.util.Collection;
public interface SharingManager<S extends Shareable, IM extends InvitationMessage> {
/** Returns the unique ID of the group sharing client. */
ClientId getClientId();
/**
* Sends an invitation to share the given group 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 group invitation
*/
void respondToInvitation(S s, Contact c, boolean accept)
throws DbException;
/**
* Returns all group sharing messages sent by the Contact
* identified by contactId.
*/
Collection<IM> getInvitationMessages(
ContactId contactId) throws DbException;
/** Returns all groups to which the user could subscribe. */
Collection<S> getAvailable() throws DbException;
/** Returns all contacts who are sharing the given group with us. */
Collection<Contact> getSharedBy(GroupId g) throws DbException;
/** Returns the IDs of all contacts with whom the given group is shared. */
Collection<Contact> getSharedWith(GroupId g) throws DbException;
/** Returns true if the group not already shared and no invitation is open */
boolean canBeShared(GroupId g, Contact c) throws DbException;
}

View File

@@ -0,0 +1,122 @@
package org.briarproject.api.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfEntry;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.sync.GroupId;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.sharing.SharingConstants.TYPE;
public interface SharingMessage {
abstract class BaseMessage {
private final GroupId groupId;
private final SessionId sessionId;
public BaseMessage(GroupId groupId, SessionId sessionId) {
this.groupId = groupId;
this.sessionId = sessionId;
}
public BdfList toBdfList() {
return BdfList.of(getType(), getSessionId());
}
public abstract BdfDictionary toBdfDictionary();
protected BdfDictionary toBdfDictionaryHelper() {
return BdfDictionary.of(
new BdfEntry(TYPE, getType()),
new BdfEntry(GROUP_ID, groupId),
new BdfEntry(SESSION_ID, sessionId)
);
}
public static BaseMessage from(InvitationFactory invitationFactory,
GroupId groupId, BdfDictionary d)
throws FormatException {
long type = d.getLong(TYPE);
if (type == SHARE_MSG_TYPE_INVITATION)
return invitationFactory.build(groupId, d);
else
return SimpleMessage.from(type, groupId, d);
}
public abstract long getType();
public GroupId getGroupId() {
return groupId;
}
public SessionId getSessionId() {
return sessionId;
}
}
abstract class Invitation extends BaseMessage {
protected final String message;
public Invitation(GroupId groupId, SessionId sessionId,
String message) {
super(groupId, sessionId);
this.message = message;
}
@Override
public long getType() {
return SHARE_MSG_TYPE_INVITATION;
}
public String getMessage() {
return message;
}
}
class SimpleMessage extends BaseMessage {
private final long type;
public SimpleMessage(long type, GroupId groupId, SessionId sessionId) {
super(groupId, sessionId);
this.type = type;
}
@Override
public long getType() {
return type;
}
@Override
public BdfDictionary toBdfDictionary() {
return toBdfDictionaryHelper();
}
public static SimpleMessage from(long type, GroupId groupId,
BdfDictionary d) throws FormatException {
if (type != SHARE_MSG_TYPE_ACCEPT &&
type != SHARE_MSG_TYPE_DECLINE &&
type != SHARE_MSG_TYPE_LEAVE &&
type != SHARE_MSG_TYPE_ABORT) throw new FormatException();
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
return new SimpleMessage(type, groupId, sessionId);
}
}
}

View File

@@ -10,6 +10,7 @@ import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule; import org.briarproject.messaging.MessagingModule;
import org.briarproject.plugins.PluginsModule; import org.briarproject.plugins.PluginsModule;
import org.briarproject.properties.PropertiesModule; import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule; import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
@@ -36,6 +37,8 @@ public interface CoreEagerSingletons {
void inject(PropertiesModule.EagerSingletons init); void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init); void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init); void inject(SystemModule.EagerSingletons init);

View File

@@ -20,6 +20,7 @@ import org.briarproject.properties.PropertiesModule;
import org.briarproject.reliability.ReliabilityModule; import org.briarproject.reliability.ReliabilityModule;
import org.briarproject.reporting.ReportingModule; import org.briarproject.reporting.ReportingModule;
import org.briarproject.settings.SettingsModule; import org.briarproject.settings.SettingsModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule; import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule; import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule; import org.briarproject.transport.TransportModule;
@@ -47,6 +48,7 @@ import dagger.Module;
ReliabilityModule.class, ReliabilityModule.class,
ReportingModule.class, ReportingModule.class,
SettingsModule.class, SettingsModule.class,
SharingModule.class,
SyncModule.class, SyncModule.class,
SystemModule.class, SystemModule.class,
TransportModule.class TransportModule.class
@@ -63,6 +65,7 @@ public class CoreModule {
c.inject(new MessagingModule.EagerSingletons()); c.inject(new MessagingModule.EagerSingletons());
c.inject(new PluginsModule.EagerSingletons()); c.inject(new PluginsModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons()); c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SharingModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons()); c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());

View File

@@ -1,18 +1,12 @@
package org.briarproject.forum; package org.briarproject.forum;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.data.MetadataEncoder; import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.forum.ForumFactory; import org.briarproject.api.forum.ForumFactory;
import org.briarproject.api.forum.ForumManager; import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostFactory; import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory; import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.GroupFactory; import org.briarproject.api.sync.GroupFactory;
import org.briarproject.api.sync.ValidationManager; import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
@@ -31,10 +25,6 @@ public class ForumModule {
public static class EagerSingletons { public static class EagerSingletons {
@Inject @Inject
ForumPostValidator forumPostValidator; ForumPostValidator forumPostValidator;
@Inject
ForumSharingValidator forumSharingValidator;
@Inject
ForumSharingManager forumSharingManager;
} }
@Provides @Provides
@@ -68,37 +58,4 @@ public class ForumModule {
return validator; return validator;
} }
@Provides
@Singleton
ForumSharingValidator provideSharingValidator(
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
ForumSharingValidator validator = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
messageQueueManager.registerMessageValidator(
ForumSharingManagerImpl.CLIENT_ID, validator);
return validator;
}
@Provides
@Singleton
ForumSharingManager provideForumSharingManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
MessageQueueManager messageQueueManager,
ForumManager forumManager,
ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager);
contactManager.registerAddContactHook(forumSharingManager);
contactManager.registerRemoveContactHook(forumSharingManager);
messageQueueManager.registerIncomingMessageHook(
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
forumManager.registerRemoveForumHook(forumSharingManager);
return forumSharingManager;
}
} }

View File

@@ -1,119 +0,0 @@
package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.STORAGE_ID;
// This class is not thread-safe
public abstract class ForumSharingSessionState {
private final SessionId sessionId;
private final MessageId storageId;
private final GroupId groupId;
private final ContactId contactId;
private final GroupId forumId;
private final String forumName;
private final byte[] forumSalt;
private int task = -1; // TODO get rid of task, see #376
public ForumSharingSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
this.sessionId = sessionId;
this.storageId = storageId;
this.groupId = groupId;
this.contactId = contactId;
this.forumId = forumId;
this.forumName = forumName;
this.forumSalt = forumSalt;
}
public static ForumSharingSessionState fromBdfDictionary(BdfDictionary d)
throws FormatException{
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
GroupId forumId = new GroupId(d.getRaw(FORUM_ID));
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
int intState = d.getLong(STATE).intValue();
if (d.getBoolean(IS_SHARER)) {
SharerSessionState.State state =
SharerSessionState.State.fromValue(intState);
return new SharerSessionState(sessionId, messageId, groupId, state,
contactId, forumId, forumName, forumSalt);
} else {
InviteeSessionState.State state =
InviteeSessionState.State.fromValue(intState);
return new InviteeSessionState(sessionId, messageId, groupId, state,
contactId, forumId, forumName, forumSalt);
}
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, getSessionId());
d.put(STORAGE_ID, getStorageId());
d.put(GROUP_ID, getGroupId());
d.put(CONTACT_ID, getContactId().getInt());
d.put(FORUM_ID, getForumId());
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
public SessionId getSessionId() {
return sessionId;
}
public MessageId getStorageId() {
return storageId;
}
public GroupId getGroupId() {
return groupId;
}
public ContactId getContactId() {
return contactId;
}
public GroupId getForumId() {
return forumId;
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
public void setTask(int task) {
this.task = task;
}
public int getTask() {
return task;
}
}

View File

@@ -1,239 +0,0 @@
package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumFactory;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
import static org.briarproject.forum.InviteeSessionState.Action;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ABORT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE;
import static org.briarproject.forum.InviteeSessionState.State;
import static org.briarproject.forum.InviteeSessionState.State.ERROR;
import static org.briarproject.forum.InviteeSessionState.State.FINISHED;
import static org.briarproject.forum.InviteeSessionState.State.LEFT;
public class InviteeEngine
implements ProtocolEngine<Action, InviteeSessionState, BaseMessage> {
private final ForumFactory forumFactory;
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
InviteeEngine(ForumFactory forumFactory) {
this.forumFactory = forumFactory;
}
@Override
public StateUpdate<InviteeSessionState, BaseMessage> onLocalAction(
InviteeSessionState localState, Action action) {
try {
State currentState = localState.getState();
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
}
if (nextState == ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == LOCAL_ACCEPT || action == LOCAL_DECLINE) {
BaseMessage msg;
if (action == LOCAL_ACCEPT) {
localState.setTask(TASK_ADD_SHARED_FORUM);
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
localState.getGroupId(), localState.getSessionId());
} else {
localState.setTask(
TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
localState.getGroupId(), localState.getSessionId());
}
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else if (action == LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<InviteeSessionState, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<InviteeSessionState, BaseMessage> onMessageReceived(
InviteeSessionState localState, BaseMessage msg) {
try {
State currentState = localState.getState();
Action action = Action.getRemote(msg.getType());
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == ERROR) {
if (currentState != ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
}
// the sharer left the forum she had shared with us
else if (action == REMOTE_LEAVE && currentState == FINISHED) {
localState.setTask(TASK_UNSHARE_FORUM_SHARED_WITH_US);
}
else if (currentState == FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// the sharer left the forum before we couldn't even respond
else if (action == REMOTE_LEAVE) {
localState.setTask(TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
}
// we have just received our invitation
else if (action == REMOTE_INVITATION) {
Forum forum = forumFactory
.createForum(localState.getForumName(),
localState.getForumSalt());
localState.setTask(TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US);
ContactId contactId = localState.getContactId();
Event event = new ForumInvitationReceivedEvent(forum, contactId);
events = Collections.singletonList(event);
}
else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<InviteeSessionState, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(State state,
InviteeSessionState localState, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "response";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + localState.getState().name()
);
}
private void logMessageReceived(State currentState, State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<InviteeSessionState, BaseMessage> onMessageDelivered(
InviteeSessionState localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<InviteeSessionState, BaseMessage> abortSession(
State currentState, InviteeSessionState localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(ERROR);
BaseMessage msg =
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<InviteeSessionState, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<InviteeSessionState, BaseMessage> noUpdate(
InviteeSessionState localState, boolean delete) throws FormatException {
return new StateUpdate<InviteeSessionState, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -1,228 +0,0 @@
package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_SHARE_FORUM;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_BY_US;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
import static org.briarproject.forum.SharerSessionState.Action;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_ABORT;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE;
import static org.briarproject.forum.SharerSessionState.State;
import static org.briarproject.forum.SharerSessionState.State.ERROR;
import static org.briarproject.forum.SharerSessionState.State.FINISHED;
import static org.briarproject.forum.SharerSessionState.State.LEFT;
public class SharerEngine
implements ProtocolEngine<Action, SharerSessionState, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
@Override
public StateUpdate<SharerSessionState, BaseMessage> onLocalAction(
SharerSessionState localState, Action action) {
try {
State currentState = localState.getState();
State nextState = currentState.next(action);
localState.setState(nextState);
if (action == LOCAL_ABORT && currentState != ERROR) {
return abortSession(currentState, localState);
}
if (nextState == ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == LOCAL_INVITATION) {
BaseMessage msg = new Invitation(localState.getGroupId(),
localState.getSessionId(), localState.getForumName(),
localState.getForumSalt(), localState.getMessage());
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
// remember that we offered to share this forum
localState.setTask(TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US);
}
else if (action == LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
}
else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<SharerSessionState, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<SharerSessionState, BaseMessage> onMessageReceived(
SharerSessionState localState, BaseMessage msg) {
try {
State currentState = localState.getState();
Action action = Action.getRemote(msg.getType());
State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == ERROR) {
if (currentState != ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
}
else if (action == REMOTE_LEAVE) {
localState.setTask(TASK_UNSHARE_FORUM_SHARED_BY_US);
}
else if (currentState == FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// we have sent our invitation and just got a response
else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
if (action == REMOTE_ACCEPT) {
localState.setTask(TASK_SHARE_FORUM);
} else {
// this ensures that the forum can be shared again
localState.setTask(
TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US);
}
String name = localState.getForumName();
ContactId c = localState.getContactId();
Event event = new ForumInvitationResponseReceivedEvent(name, c);
events = Collections.singletonList(event);
}
else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<SharerSessionState, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(State currentState, State nextState,
BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "invitation";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
private void logMessageReceived(State currentState, State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<SharerSessionState, BaseMessage> onMessageDelivered(
SharerSessionState localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<SharerSessionState, BaseMessage> abortSession(
State currentState, SharerSessionState localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(ERROR);
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
localState.getGroupId(), localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<SharerSessionState, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<SharerSessionState, BaseMessage> noUpdate(
SharerSessionState localState, boolean delete)
throws FormatException {
return new StateUpdate<SharerSessionState, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
public class ForumInviteeSessionState extends InviteeSessionState {
private final String forumName;
private final byte[] forumSalt;
public ForumInviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
super(sessionId, storageId, groupId, state, contactId, forumId);
this.forumName = forumName;
this.forumSalt = forumSalt;
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
}

View File

@@ -0,0 +1,40 @@
package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
public class ForumSharerSessionState extends SharerSessionState {
private final String forumName;
private final byte[] forumSalt;
public ForumSharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId,
String forumName, byte[] forumSalt) {
super(sessionId, storageId, groupId, state, contactId, forumId);
this.forumName = forumName;
this.forumSalt = forumSalt;
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = super.toBdfDictionary();
d.put(FORUM_NAME, getForumName());
d.put(FORUM_SALT, getForumSalt());
return d;
}
public String getForumName() {
return forumName;
}
public byte[] getForumSalt() {
return forumSalt;
}
}

View File

@@ -0,0 +1,269 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
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.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumFactory;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.forum.ForumSharingMessage.ForumInvitation;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.system.Clock;
import org.briarproject.util.StringUtils;
import java.security.SecureRandom;
import javax.inject.Inject;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
class ForumSharingManagerImpl extends
SharingManagerImpl<Forum, ForumInvitation, ForumInvitationMessage, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent>
implements ForumSharingManager, ForumManager.RemoveForumHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"cd11a5d04dccd9e2931d6fc3df456313"
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
private final ForumManager forumManager;
private final SFactory sFactory;
private final IFactory iFactory;
private final ISFactory isFactory;
private final SSFactory ssFactory;
private final IRFactory irFactory;
private final IRRFactory irrFactory;
@Inject
ForumSharingManagerImpl(ClientHelper clientHelper,
Clock clock, DatabaseComponent db,
ForumFactory forumFactory,
ForumManager forumManager,
MessageQueueManager messageQueueManager,
MetadataEncoder metadataEncoder,
MetadataParser metadataParser,
PrivateGroupFactory privateGroupFactory,
SecureRandom random) {
super(db, messageQueueManager, clientHelper, metadataParser,
metadataEncoder, random, privateGroupFactory, clock);
this.forumManager = forumManager;
sFactory = new SFactory(forumFactory, forumManager);
iFactory = new IFactory();
isFactory = new ISFactory();
ssFactory = new SSFactory();
irFactory = new IRFactory(sFactory);
irrFactory = new IRRFactory();
}
@Override
public ClientId getClientId() {
return CLIENT_ID;
}
@Override
protected ClientId getShareableClientId() {
return forumManager.getClientId();
}
@Override
protected ForumInvitationMessage createInvitationMessage(MessageId id,
ForumInvitation msg, ContactId contactId, boolean available,
long time, boolean local, boolean sent, boolean seen,
boolean read) {
return new ForumInvitationMessage(id, msg.getSessionId(), contactId,
msg.getForumName(), msg.getMessage(), available, time, local,
sent, seen, read);
}
@Override
protected ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> getSFactory() {
return sFactory;
}
@Override
protected InvitationFactory<ForumInvitation, ForumSharerSessionState> getIFactory() {
return iFactory;
}
@Override
protected InviteeSessionStateFactory<Forum, ForumInviteeSessionState> getISFactory() {
return isFactory;
}
@Override
protected SharerSessionStateFactory<Forum, ForumSharerSessionState> getSSFactory() {
return ssFactory;
}
@Override
protected InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationReceivedEvent> getIRFactory() {
return irFactory;
}
@Override
protected InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> getIRRFactory() {
return irrFactory;
}
@Override
public void removingForum(Transaction txn, Forum f) throws DbException {
removingShareable(txn, f);
}
static class SFactory implements
ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> {
private final ForumFactory forumFactory;
private final ForumManager forumManager;
SFactory(ForumFactory forumFactory, ForumManager forumManager) {
this.forumFactory = forumFactory;
this.forumManager = forumManager;
}
@Override
public BdfList encode(Forum f) {
return BdfList.of(f.getName(), f.getSalt());
}
@Override
public Forum get(Transaction txn, GroupId groupId)
throws DbException {
return forumManager.getForum(txn, groupId);
}
@Override
public Forum parse(BdfList shareable) throws FormatException {
return forumFactory
.createForum(shareable.getString(0), shareable.getRaw(1));
}
@Override
public Forum parse(ForumInvitation msg) {
return forumFactory
.createForum(msg.getForumName(), msg.getForumSalt());
}
public Forum parse(ForumInviteeSessionState state) {
return forumFactory
.createForum(state.getForumName(), state.getForumSalt());
}
@Override
public Forum parse(ForumSharerSessionState state) {
return forumFactory
.createForum(state.getForumName(), state.getForumSalt());
}
}
static class IFactory implements
InvitationFactory<ForumInvitation, ForumSharerSessionState> {
@Override
public ForumInvitation build(GroupId groupId, BdfDictionary d)
throws FormatException {
return ForumInvitation.from(groupId, d);
}
@Override
public ForumInvitation build(ForumSharerSessionState localState) {
return new ForumInvitation(localState.getGroupId(),
localState.getSessionId(), localState.getForumName(),
localState.getForumSalt(), localState.getMessage());
}
}
static class ISFactory implements
InviteeSessionStateFactory<Forum, ForumInviteeSessionState> {
@Override
public ForumInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
GroupId forumId, BdfDictionary d) throws FormatException {
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt);
}
@Override
public ForumInviteeSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
Forum forum) {
return new ForumInviteeSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt());
}
}
static class SSFactory implements
SharerSessionStateFactory<Forum, ForumSharerSessionState> {
@Override
public ForumSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
GroupId forumId, BdfDictionary d) throws FormatException {
String forumName = d.getString(FORUM_NAME);
byte[] forumSalt = d.getRaw(FORUM_SALT);
return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forumId, forumName, forumSalt);
}
@Override
public ForumSharerSessionState build(SessionId sessionId,
MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
Forum forum) {
return new ForumSharerSessionState(sessionId, storageId,
groupId, state, contactId, forum.getId(), forum.getName(),
forum.getSalt());
}
}
static class IRFactory implements
InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationReceivedEvent> {
private final SFactory sFactory;
IRFactory(SFactory sFactory) {
this.sFactory = sFactory;
}
@Override
public ForumInvitationReceivedEvent build(
ForumInviteeSessionState localState) {
Forum forum = sFactory.parse(localState);
ContactId contactId = localState.getContactId();
return new ForumInvitationReceivedEvent(forum, contactId);
}
}
static class IRRFactory implements
InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
@Override
public ForumInvitationResponseReceivedEvent build(
ForumSharerSessionState localState) {
String name = localState.getForumName();
ContactId c = localState.getContactId();
return new ForumInvitationResponseReceivedEvent(name, c);
}
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum; package org.briarproject.sharing;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper; import org.briarproject.api.clients.ClientHelper;
@@ -12,25 +12,27 @@ import org.briarproject.api.sync.Message;
import org.briarproject.api.system.Clock; import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator; import org.briarproject.clients.BdfMessageValidator;
import javax.inject.Inject;
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME; import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.LOCAL;
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH; import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID; import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.TIME; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.TYPE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.sharing.SharingConstants.TIME;
import static org.briarproject.api.sharing.SharingConstants.TYPE;
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
class ForumSharingValidator extends BdfMessageValidator { class ForumSharingValidator extends BdfMessageValidator {
@Inject
ForumSharingValidator(ClientHelper clientHelper, ForumSharingValidator(ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) { MetadataEncoder metadataEncoder, Clock clock) {
super(clientHelper, metadataEncoder, clock); super(clientHelper, metadataEncoder, clock);

View File

@@ -0,0 +1,9 @@
package org.briarproject.sharing;
import org.briarproject.api.sharing.SharingMessage;
public interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState> extends
org.briarproject.api.sharing.InvitationFactory<I> {
I build(SS localState);
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.sharing;
import org.briarproject.api.event.InvitationReceivedEvent;
public interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationReceivedEvent> {
IR build(IS localState);
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.sharing;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
public interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
IRR build(SS localState);
}

View File

@@ -0,0 +1,222 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.InvitationReceivedEvent;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHARED_SHAREABLE;
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
public class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceivedEvent>
implements ProtocolEngine<InviteeSessionState.Action, IS, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(InviteeEngine.class.getName());
private final InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory;
InviteeEngine(InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory) {
this.invitationReceivedEventFactory = invitationReceivedEventFactory;
}
@Override
public StateUpdate<IS, BaseMessage> onLocalAction(
IS localState, InviteeSessionState.Action action) {
try {
InviteeSessionState.State currentState = localState.getState();
InviteeSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
if (action == InviteeSessionState.Action.LOCAL_ABORT && currentState != InviteeSessionState.State.ERROR) {
return abortSession(currentState, localState);
}
if (nextState == InviteeSessionState.State.ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == InviteeSessionState.Action.LOCAL_ACCEPT || action == InviteeSessionState.Action.LOCAL_DECLINE) {
BaseMessage msg;
if (action == InviteeSessionState.Action.LOCAL_ACCEPT) {
localState.setTask(TASK_ADD_SHARED_SHAREABLE);
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
localState.getGroupId(), localState.getSessionId());
} else {
localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
localState.getGroupId(), localState.getSessionId());
}
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else if (action == InviteeSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, localState, msg);
}
else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<IS, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<IS, BaseMessage> onMessageReceived(
IS localState, BaseMessage msg) {
try {
InviteeSessionState.State currentState = localState.getState();
InviteeSessionState.Action action = InviteeSessionState.Action.getRemote(msg.getType());
InviteeSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == InviteeSessionState.State.ERROR) {
if (currentState != InviteeSessionState.State.ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == InviteeSessionState.State.LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
}
// the sharer left the forum she had shared with us
else if (action == InviteeSessionState.Action.REMOTE_LEAVE && currentState == InviteeSessionState.State.FINISHED) {
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_WITH_US);
}
else if (currentState == InviteeSessionState.State.FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// the sharer left the forum before we couldn't even respond
else if (action == InviteeSessionState.Action.REMOTE_LEAVE) {
localState.setTask(TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
}
// we have just received our invitation
else if (action == InviteeSessionState.Action.REMOTE_INVITATION) {
localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US);
Event event = invitationReceivedEventFactory.build(localState);
events = Collections.singletonList(event);
}
else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<IS, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(InviteeSessionState.State state,
InviteeSessionState localState, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "response";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + state.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + localState.getState().name()
);
}
private void logMessageReceived(InviteeSessionState.State currentState, InviteeSessionState.State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<IS, BaseMessage> onMessageDelivered(
IS localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<IS, BaseMessage> abortSession(
InviteeSessionState.State currentState, IS localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(InviteeSessionState.State.ERROR);
BaseMessage msg =
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<IS, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<IS, BaseMessage> noUpdate(
IS localState, boolean delete) throws FormatException {
return new StateUpdate<IS, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum; package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -6,28 +6,27 @@ import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE; import static org.briarproject.api.sharing.SharingConstants.STATE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT; import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_ACCEPT;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE; import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_DECLINE;
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE; import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION; import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_INVITATION;
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE; import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
// This class is not thread-safe // This class is not thread-safe
public class InviteeSessionState extends ForumSharingSessionState { public abstract class InviteeSessionState extends SharingSessionState {
private State state; private State state;
public InviteeSessionState(SessionId sessionId, MessageId storageId, public InviteeSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId, GroupId groupId, State state, ContactId contactId,
String forumName, byte[] forumSalt) { GroupId shareableId) {
super(sessionId, storageId, groupId, contactId, forumId, forumName, super(sessionId, storageId, groupId, contactId, shareableId);
forumSalt);
this.state = state; this.state = state;
} }

View File

@@ -0,0 +1,19 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sharing.Shareable;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
public interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId,
GroupId shareableId, BdfDictionary d) throws FormatException;
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
InviteeSessionState.State state, ContactId contactId, S shareable);
}

View File

@@ -0,0 +1,24 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.sharing.Shareable;
import org.briarproject.api.sharing.SharingMessage;
import org.briarproject.api.sync.GroupId;
interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> {
BdfList encode(S sh);
S get(Transaction txn, GroupId groupId) throws DbException;
S parse(BdfList shareable) throws FormatException;
S parse(I msg);
S parse(IS state);
S parse(SS state);
}

View File

@@ -0,0 +1,225 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ProtocolEngine;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.InvitationResponseReceivedEvent;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.sharing.SharingConstants.TASK_SHARE_SHAREABLE;
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_BY_US;
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
public class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> {
private static final Logger LOG =
Logger.getLogger(SharerEngine.class.getName());
private final InvitationFactory<I, SS> invitationFactory;
private final InvitationResponseReceivedEventFactory<SS, IRR>
invitationResponseReceivedEventFactory;
SharerEngine(InvitationFactory<I, SS> invitationFactory,
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory) {
this.invitationFactory = invitationFactory;
this.invitationResponseReceivedEventFactory =
invitationResponseReceivedEventFactory;
}
@Override
public StateUpdate<SS, BaseMessage> onLocalAction(
SS localState, SharerSessionState.Action action) {
try {
SharerSessionState.State currentState = localState.getState();
SharerSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
if (action == SharerSessionState.Action.LOCAL_ABORT &&
currentState != SharerSessionState.State.ERROR) {
return abortSession(currentState, localState);
}
if (nextState == SharerSessionState.State.ERROR) {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Error: Invalid action in state " +
currentState.name());
}
return noUpdate(localState, true);
}
List<BaseMessage> messages;
List<Event> events = Collections.emptyList();
if (action == SharerSessionState.Action.LOCAL_INVITATION) {
BaseMessage msg = invitationFactory.build(localState);
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
// remember that we offered to share this forum
localState
.setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US);
} else if (action == SharerSessionState.Action.LOCAL_LEAVE) {
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
localState.getGroupId(), localState.getSessionId());
messages = Collections.singletonList(msg);
logLocalAction(currentState, nextState, msg);
} else {
throw new IllegalArgumentException("Unknown Local Action");
}
return new StateUpdate<SS, BaseMessage>(false,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
@Override
public StateUpdate<SS, BaseMessage> onMessageReceived(
SS localState, BaseMessage msg) {
try {
SharerSessionState.State currentState = localState.getState();
SharerSessionState.Action action =
SharerSessionState.Action.getRemote(msg.getType());
SharerSessionState.State nextState = currentState.next(action);
localState.setState(nextState);
logMessageReceived(currentState, nextState, msg.getType(), msg);
if (nextState == SharerSessionState.State.ERROR) {
if (currentState != SharerSessionState.State.ERROR) {
return abortSession(currentState, localState);
} else {
return noUpdate(localState, true);
}
}
List<BaseMessage> messages = Collections.emptyList();
List<Event> events = Collections.emptyList();
boolean deleteMsg = false;
if (currentState == SharerSessionState.State.LEFT) {
// ignore and delete messages coming in while in that state
deleteMsg = true;
} else if (action == SharerSessionState.Action.REMOTE_LEAVE) {
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_BY_US);
} else if (currentState == SharerSessionState.State.FINISHED) {
// ignore and delete messages coming in while in that state
// note that LEAVE is possible, but was handled above
deleteMsg = true;
}
// we have sent our invitation and just got a response
else if (action == SharerSessionState.Action.REMOTE_ACCEPT ||
action == SharerSessionState.Action.REMOTE_DECLINE) {
if (action == SharerSessionState.Action.REMOTE_ACCEPT) {
localState.setTask(TASK_SHARE_SHAREABLE);
} else {
// this ensures that the forum can be shared again
localState.setTask(
TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US);
}
Event event = invitationResponseReceivedEventFactory
.build(localState);
events = Collections.singletonList(event);
} else {
throw new IllegalArgumentException("Bad state");
}
return new StateUpdate<SS, BaseMessage>(deleteMsg,
false, localState, messages, events);
} catch (FormatException e) {
throw new IllegalArgumentException(e);
}
}
private void logLocalAction(SharerSessionState.State currentState,
SharerSessionState.State nextState,
BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String a = "invitation";
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
LOG.info("Sending " + a + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
private void logMessageReceived(SharerSessionState.State currentState,
SharerSessionState.State nextState,
long type, BaseMessage msg) {
if (!LOG.isLoggable(INFO)) return;
String t = "unknown";
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
LOG.info("Received " + t + " in state " + currentState.name() +
" with session ID " +
msg.getSessionId().hashCode() + " in group " +
msg.getGroupId().hashCode() + ". " +
"Moving on to state " + nextState.name()
);
}
@Override
public StateUpdate<SS, BaseMessage> onMessageDelivered(
SS localState, BaseMessage delivered) {
try {
return noUpdate(localState, false);
} catch (FormatException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
}
}
private StateUpdate<SS, BaseMessage> abortSession(
SharerSessionState.State currentState, SS localState)
throws FormatException {
if (LOG.isLoggable(WARNING)) {
LOG.warning("Aborting protocol session " +
localState.getSessionId().hashCode() +
" in state " + currentState.name());
}
localState.setState(SharerSessionState.State.ERROR);
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
localState.getGroupId(), localState.getSessionId());
List<BaseMessage> messages = Collections.singletonList(msg);
List<Event> events = Collections.emptyList();
return new StateUpdate<SS, BaseMessage>(false, false,
localState, messages, events);
}
private StateUpdate<SS, BaseMessage> noUpdate(
SS localState, boolean delete)
throws FormatException {
return new StateUpdate<SS, BaseMessage>(delete, false,
localState, Collections.<BaseMessage>emptyList(),
Collections.<Event>emptyList());
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum; package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId; import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
@@ -6,30 +6,29 @@ import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId; import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.STATE; import static org.briarproject.api.sharing.SharingConstants.STATE;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION; import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_INVITATION;
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE; import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_LEAVE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT; import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE; import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_DECLINE;
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE; import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_LEAVE;
// This class is not thread-safe // This class is not thread-safe
public class SharerSessionState extends ForumSharingSessionState { public abstract class SharerSessionState extends SharingSessionState {
private State state; private State state;
private String msg = null; private String msg = null;
public SharerSessionState(SessionId sessionId, MessageId storageId, public SharerSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, State state, ContactId contactId, GroupId forumId, GroupId groupId, State state, ContactId contactId,
String forumName, byte[] forumSalt) { GroupId shareableId) {
super(sessionId, storageId, groupId, contactId, forumId, forumName, super(sessionId, storageId, groupId, contactId, shareableId);
forumSalt);
this.state = state; this.state = state;
} }

View File

@@ -0,0 +1,19 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sharing.Shareable;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
public interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId,
GroupId shareableId, BdfDictionary d) throws FormatException;
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
SharerSessionState.State state, ContactId contactId, S shareable);
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum; package org.briarproject.sharing;
import org.briarproject.api.Bytes; import org.briarproject.api.Bytes;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
@@ -22,12 +22,12 @@ import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.NoSuchMessageException; import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.Transaction; import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.forum.Forum; import org.briarproject.api.event.InvitationReceivedEvent;
import org.briarproject.api.forum.ForumFactory; import org.briarproject.api.event.InvitationResponseReceivedEvent;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.LocalAuthor; import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sharing.Shareable;
import org.briarproject.api.sharing.SharingManager;
import org.briarproject.api.sync.ClientId; import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group; import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId; import org.briarproject.api.sync.GroupId;
@@ -50,83 +50,90 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.clients.ProtocolEngine.StateUpdate; import static org.briarproject.api.clients.ProtocolEngine.StateUpdate;
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID; import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
import static org.briarproject.api.forum.ForumConstants.FORUM_ID; import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH; import static org.briarproject.api.sharing.SharingConstants.LOCAL;
import static org.briarproject.api.forum.ForumConstants.IS_SHARER; import static org.briarproject.api.sharing.SharingConstants.READ;
import static org.briarproject.api.forum.ForumConstants.LOCAL; import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.forum.ForumConstants.READ; import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID;
import static org.briarproject.api.forum.ForumConstants.SESSION_ID; import static org.briarproject.api.sharing.SharingConstants.SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.SHARED_WITH_US; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION; import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE; import static org.briarproject.api.sharing.SharingConstants.SHARING_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.STATE; import static org.briarproject.api.sharing.SharingConstants.STATE;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US; import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM; import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHARED_SHAREABLE;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US; import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_SHARE_FORUM; import static org.briarproject.api.sharing.SharingConstants.TASK_SHARE_SHAREABLE;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US; import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
import static org.briarproject.api.forum.ForumConstants.TIME; import static org.briarproject.api.sharing.SharingConstants.TIME;
import static org.briarproject.api.forum.ForumConstants.TO_BE_SHARED_BY_US; import static org.briarproject.api.sharing.SharingConstants.TO_BE_SHARED_BY_US;
import static org.briarproject.api.forum.ForumConstants.TYPE; import static org.briarproject.api.sharing.SharingConstants.TYPE;
import static org.briarproject.api.forum.ForumManager.RemoveForumHook; import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage; import static org.briarproject.api.sharing.SharingMessage.Invitation;
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
import static org.briarproject.forum.ForumSharingSessionState.fromBdfDictionary;
import static org.briarproject.forum.SharerSessionState.Action;
class ForumSharingManagerImpl extends BdfIncomingMessageHook abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM extends InvitationMessage, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent>
implements ForumSharingManager, Client, RemoveForumHook, extends BdfIncomingMessageHook
AddContactHook, RemoveContactHook { implements SharingManager<S, IM>, Client, AddContactHook,
RemoveContactHook {
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
"cd11a5d04dccd9e2931d6fc3df456313"
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ForumSharingManagerImpl.class.getName()); Logger.getLogger(SharingManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final ForumManager forumManager;
private final MessageQueueManager messageQueueManager; private final MessageQueueManager messageQueueManager;
private final MetadataEncoder metadataEncoder; private final MetadataEncoder metadataEncoder;
private final SecureRandom random; private final SecureRandom random;
private final PrivateGroupFactory privateGroupFactory; private final PrivateGroupFactory privateGroupFactory;
private final ForumFactory forumFactory;
private final Clock clock; private final Clock clock;
private final Group localGroup; private final Group localGroup;
@Inject SharingManagerImpl(DatabaseComponent db,
ForumSharingManagerImpl(DatabaseComponent db, ForumManager forumManager,
MessageQueueManager messageQueueManager, ClientHelper clientHelper, MessageQueueManager messageQueueManager, ClientHelper clientHelper,
MetadataParser metadataParser, MetadataEncoder metadataEncoder, MetadataParser metadataParser, MetadataEncoder metadataEncoder,
SecureRandom random, PrivateGroupFactory privateGroupFactory, SecureRandom random, PrivateGroupFactory privateGroupFactory,
ForumFactory forumFactory, Clock clock) { Clock clock) {
super(clientHelper, metadataParser); super(clientHelper, metadataParser);
this.db = db; this.db = db;
this.forumManager = forumManager;
this.messageQueueManager = messageQueueManager; this.messageQueueManager = messageQueueManager;
this.metadataEncoder = metadataEncoder; this.metadataEncoder = metadataEncoder;
this.random = random; this.random = random;
this.privateGroupFactory = privateGroupFactory; this.privateGroupFactory = privateGroupFactory;
this.forumFactory = forumFactory;
this.clock = clock; this.clock = clock;
localGroup = privateGroupFactory.createLocalGroup(getClientId()); localGroup = privateGroupFactory.createLocalGroup(getClientId());
} }
public abstract ClientId getClientId();
protected abstract ClientId getShareableClientId();
protected abstract IM createInvitationMessage(MessageId id, I msg,
ContactId contactId, boolean available, long time, boolean local,
boolean sent, boolean seen, boolean read);
protected abstract ShareableFactory<S, I, IS, SS> getSFactory();
protected abstract InvitationFactory<I, SS> getIFactory();
protected abstract InviteeSessionStateFactory<S, IS> getISFactory();
protected abstract SharerSessionStateFactory<S, SS> getSSFactory();
protected abstract InvitationReceivedEventFactory<IS, IR> getIRFactory();
protected abstract InvitationResponseReceivedEventFactory<SS, IRR> getIRRFactory();
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
db.addGroup(txn, localGroup); db.addGroup(txn, localGroup);
@@ -183,7 +190,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
protected void incomingMessage(Transaction txn, Message m, BdfList body, protected void incomingMessage(Transaction txn, Message m, BdfList body,
BdfDictionary d) throws DbException, FormatException { BdfDictionary d) throws DbException, FormatException {
BaseMessage msg = BaseMessage.from(m.getGroupId(), d); BaseMessage msg = BaseMessage.from(getIFactory(), m.getGroupId(), d);
SessionId sessionId = msg.getSessionId(); SessionId sessionId = msg.getSessionId();
if (msg.getType() == SHARE_MSG_TYPE_INVITATION) { if (msg.getType() == SHARE_MSG_TYPE_INVITATION) {
@@ -200,19 +207,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
// check if we already have a state with that sessionId // check if we already have a state with that sessionId
if (stateExists) throw new FormatException(); if (stateExists) throw new FormatException();
// check if forum can be shared // check if shareable can be shared
Invitation invitation = (Invitation) msg; I invitation = (I) msg;
Forum f = forumFactory.createForum(invitation.getForumName(), S f = getSFactory().parse(invitation);
invitation.getForumSalt());
ContactId contactId = getContactId(txn, m.getGroupId()); ContactId contactId = getContactId(txn, m.getGroupId());
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
if (!canBeShared(txn, f.getId(), contact)) if (!canBeShared(txn, f.getId(), contact))
checkForRaceCondition(txn, f, contact); checkForRaceCondition(txn, f, contact);
// initialize state and process invitation // initialize state and process invitation
InviteeSessionState state = IS state = initializeInviteeState(txn, contactId, invitation);
initializeInviteeState(txn, contactId, invitation); InviteeEngine<IS, IR> engine =
InviteeEngine engine = new InviteeEngine(forumFactory); new InviteeEngine<IS, IR>(getIRFactory());
processInviteeStateUpdate(txn, m.getId(), processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} catch (FormatException e) { } catch (FormatException e) {
@@ -222,48 +228,47 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} else if (msg.getType() == SHARE_MSG_TYPE_ACCEPT || } else if (msg.getType() == SHARE_MSG_TYPE_ACCEPT ||
msg.getType() == SHARE_MSG_TYPE_DECLINE) { msg.getType() == SHARE_MSG_TYPE_DECLINE) {
// we are a sharer who just received a response // we are a sharer who just received a response
SharerSessionState state = getSessionStateForSharer(txn, sessionId); SS state = getSessionStateForSharer(txn, sessionId);
SharerEngine engine = new SharerEngine(); SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory());
processSharerStateUpdate(txn, m.getId(), processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} else if (msg.getType() == SHARE_MSG_TYPE_LEAVE || } else if (msg.getType() == SHARE_MSG_TYPE_LEAVE ||
msg.getType() == SHARE_MSG_TYPE_ABORT) { msg.getType() == SHARE_MSG_TYPE_ABORT) {
// we don't know who we are, so figure it out // we don't know who we are, so figure it out
ForumSharingSessionState s = getSessionState(txn, sessionId, true); SharingSessionState s = getSessionState(txn, sessionId, true);
if (s instanceof SharerSessionState) { if (s instanceof SharerSessionState) {
// we are a sharer and the invitee wants to leave or abort // we are a sharer and the invitee wants to leave or abort
SharerSessionState state = (SharerSessionState) s; SS state = (SS) s;
SharerEngine engine = new SharerEngine(); SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory());
processSharerStateUpdate(txn, m.getId(), processSharerStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} else { } else {
// we are an invitee and the sharer wants to leave or abort // we are an invitee and the sharer wants to leave or abort
InviteeSessionState state = (InviteeSessionState) s; IS state = (IS) s;
InviteeEngine engine = new InviteeEngine(forumFactory); InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory());
processInviteeStateUpdate(txn, m.getId(), processInviteeStateUpdate(txn, m.getId(),
engine.onMessageReceived(state, msg)); engine.onMessageReceived(state, msg));
} }
} else { } else {
// message has passed validator, so that should never happen // message has passed validator, so that should never happen
throw new RuntimeException("Illegal Forum Sharing Message"); throw new RuntimeException("Illegal Sharing Message");
} }
} }
@Override @Override
public ClientId getClientId() { public void sendInvitation(GroupId groupId, ContactId contactId,
return CLIENT_ID;
}
@Override
public void sendForumInvitation(GroupId groupId, ContactId contactId,
String msg) throws DbException { String msg) throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// initialize local state for sharer // initialize local state for sharer
Forum f = forumManager.getForum(txn, groupId); S f = getSFactory().get(txn, groupId);
SharerSessionState localState = SS localState = initializeSharerState(txn, f, contactId);
initializeSharerState(txn, f, contactId);
// add invitation message to local state to be available for engine // add invitation message to local state to be available for engine
if (!StringUtils.isNullOrEmpty(msg)) { if (!StringUtils.isNullOrEmpty(msg)) {
@@ -271,9 +276,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
// start engine and process its state update // start engine and process its state update
SharerEngine engine = new SharerEngine(); SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory());
processSharerStateUpdate(txn, null, processSharerStateUpdate(txn, null,
engine.onLocalAction(localState, Action.LOCAL_INVITATION)); engine.onLocalAction(localState,
SharerSessionState.Action.LOCAL_INVITATION));
txn.setComplete(); txn.setComplete();
} catch (FormatException e) { } catch (FormatException e) {
@@ -284,14 +292,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
@Override @Override
public void respondToInvitation(Forum f, Contact c, boolean accept) public void respondToInvitation(S f, Contact c, boolean accept)
throws DbException { throws DbException {
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
// find session state based on forum // find session state based on shareable
InviteeSessionState localState = IS localState = getSessionStateForResponse(txn, f, c);
getSessionStateForResponse(txn, f, c);
// define action // define action
InviteeSessionState.Action localAction; InviteeSessionState.Action localAction;
@@ -302,7 +309,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
// start engine and process its state update // start engine and process its state update
InviteeEngine engine = new InviteeEngine(forumFactory); InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory());
processInviteeStateUpdate(txn, null, processInviteeStateUpdate(txn, null,
engine.onLocalAction(localState, localAction)); engine.onLocalAction(localState, localAction));
@@ -315,8 +323,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
@Override @Override
public Collection<ForumInvitationMessage> getForumInvitationMessages( public Collection<IM> getInvitationMessages(ContactId contactId)
ContactId contactId) throws DbException { throws DbException {
// query for all invitations // query for all invitations
BdfDictionary query = BdfDictionary.of( BdfDictionary query = BdfDictionary.of(
@@ -328,14 +336,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
Contact contact = db.getContact(txn, contactId); Contact contact = db.getContact(txn, contactId);
Group group = getContactGroup(contact); Group group = getContactGroup(contact);
Collection<ForumInvitationMessage> list = Collection<IM> list = new ArrayList<IM>();
new ArrayList<ForumInvitationMessage>();
Map<MessageId, BdfDictionary> map = clientHelper Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, group.getId(), query); .getMessageMetadataAsDictionary(txn, group.getId(), query);
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) { for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
BdfDictionary d = m.getValue(); BdfDictionary d = m.getValue();
try { try {
Invitation msg = Invitation.from(group.getId(), d); I msg = getIFactory().build(group.getId(), d);
MessageStatus status = MessageStatus status =
db.getMessageStatus(txn, contactId, m.getKey()); db.getMessageStatus(txn, contactId, m.getKey());
long time = d.getLong(TIME); long time = d.getLong(TIME);
@@ -343,20 +350,17 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
boolean read = d.getBoolean(READ, false); boolean read = d.getBoolean(READ, false);
boolean available = false; boolean available = false;
if (!local) { if (!local) {
// figure out whether the forum is still available // figure out whether the shareable is still available
ForumSharingSessionState s = SharingSessionState s =
getSessionState(txn, msg.getSessionId(), true); getSessionState(txn, msg.getSessionId(), true);
if (!(s instanceof InviteeSessionState)) if (!(s instanceof InviteeSessionState))
continue; continue;
available = ((InviteeSessionState) s).getState() == available = ((InviteeSessionState) s).getState() ==
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE; InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
} }
ForumInvitationMessage im = IM im = createInvitationMessage(m.getKey(), msg, contactId,
new ForumInvitationMessage(m.getKey(), available, time, local, status.isSent(),
msg.getSessionId(), contactId, status.isSeen(), read);
msg.getForumName(), msg.getMessage(),
available, time, local, status.isSent(),
status.isSeen(), read);
list.add(im); list.add(im);
} catch (FormatException e) { } catch (FormatException e) {
if (LOG.isLoggable(WARNING)) if (LOG.isLoggable(WARNING))
@@ -373,40 +377,20 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
@Override @Override
public void removingForum(Transaction txn, Forum f) throws DbException { public Collection<S> getAvailable() throws DbException {
try { try {
for (Contact c : db.getContacts(txn)) { Set<S> available = new HashSet<S>();
GroupId g = getContactGroup(c).getId();
if (removeFromList(txn, g, TO_BE_SHARED_BY_US, f)) {
leaveForum(txn, c.getId(), f);
}
if (removeFromList(txn, g, SHARED_BY_US, f)) {
leaveForum(txn, c.getId(), f);
}
if (removeFromList(txn, g, SHARED_WITH_US, f)) {
leaveForum(txn, c.getId(), f);
}
}
} catch (IOException e) {
throw new DbException(e);
}
}
@Override
public Collection<Forum> getAvailableForums() throws DbException {
try {
Set<Forum> available = new HashSet<Forum>();
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
// Get any forums we subscribe to // Get any shareables we subscribe to
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn, Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
forumManager.getClientId())); getShareableClientId()));
// Get all forums shared by contacts // Get all shareables shared by contacts
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c); Group g = getContactGroup(c);
List<Forum> forums = List<S> shareables =
getForumList(txn, g.getId(), SHARED_WITH_US); getShareableList(txn, g.getId(), SHARED_WITH_US);
for (Forum f : forums) { for (S f : shareables) {
if (!subscribed.contains(f.getGroup())) if (!subscribed.contains(f.getGroup()))
available.add(f); available.add(f);
} }
@@ -489,7 +473,26 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
} }
private void checkForRaceCondition(Transaction txn, Forum f, Contact c) protected void removingShareable(Transaction txn, S f) throws DbException {
try {
for (Contact c : db.getContacts(txn)) {
GroupId g = getContactGroup(c).getId();
if (removeFromList(txn, g, TO_BE_SHARED_BY_US, f)) {
leaveShareable(txn, c.getId(), f);
}
if (removeFromList(txn, g, SHARED_BY_US, f)) {
leaveShareable(txn, c.getId(), f);
}
if (removeFromList(txn, g, SHARED_WITH_US, f)) {
leaveShareable(txn, c.getId(), f);
}
}
} catch (IOException e) {
throw new DbException(e);
}
}
private void checkForRaceCondition(Transaction txn, S f, Contact c)
throws FormatException, DbException { throws FormatException, DbException {
GroupId contactGroup = getContactGroup(c).getId(); GroupId contactGroup = getContactGroup(c).getId();
@@ -507,12 +510,14 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
if (alice) { if (alice) {
// our own invitation takes precedence, so just delete Bob's // our own invitation takes precedence, so just delete Bob's
LOG.info("Invitation race-condition: We are Alice deleting Bob's invitation."); LOG.info(
"Invitation race-condition: We are Alice deleting Bob's invitation.");
throw new FormatException(); throw new FormatException();
} else { } else {
// we are Bob, so we need to "take back" our own invitation // we are Bob, so we need to "take back" our own invitation
LOG.info("Invitation race-condition: We are Bob taking back our invitation."); LOG.info(
ForumSharingSessionState state = "Invitation race-condition: We are Bob taking back our invitation.");
SharingSessionState state =
getSessionStateForLeaving(txn, f, c.getId()); getSessionStateForLeaving(txn, f, c.getId());
if (state instanceof SharerSessionState) { if (state instanceof SharerSessionState) {
//SharerEngine engine = new SharerEngine(); //SharerEngine engine = new SharerEngine();
@@ -529,7 +534,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
private SharerSessionState initializeSharerState(Transaction txn, Forum f, private SS initializeSharerState(Transaction txn, S f,
ContactId contactId) throws FormatException, DbException { ContactId contactId) throws FormatException, DbException {
Contact c = db.getContact(txn, contactId); Contact c = db.getContact(txn, contactId);
@@ -537,15 +542,15 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
// create local message to keep engine state // create local message to keep engine state
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
Bytes salt = new Bytes(new byte[FORUM_SALT_LENGTH]); Bytes salt = new Bytes(new byte[SHARING_SALT_LENGTH]);
random.nextBytes(salt.getBytes()); random.nextBytes(salt.getBytes());
Message m = clientHelper.createMessage(localGroup.getId(), now, Message m = clientHelper.createMessage(localGroup.getId(), now,
BdfList.of(salt)); BdfList.of(salt));
SessionId sessionId = new SessionId(m.getId().getBytes()); SessionId sessionId = new SessionId(m.getId().getBytes());
SharerSessionState s = new SharerSessionState(sessionId, sessionId, SS s = getSSFactory().build(sessionId, sessionId,
group.getId(), SharerSessionState.State.PREPARE_INVITATION, group.getId(), SharerSessionState.State.PREPARE_INVITATION,
contactId, f.getId(), f.getName(), f.getSalt()); contactId, f);
// save local state to database // save local state to database
BdfDictionary d = s.toBdfDictionary(); BdfDictionary d = s.toBdfDictionary();
@@ -554,27 +559,24 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
return s; return s;
} }
private InviteeSessionState initializeInviteeState(Transaction txn, private IS initializeInviteeState(Transaction txn,
ContactId contactId, Invitation msg) ContactId contactId, I msg)
throws FormatException, DbException { throws FormatException, DbException {
Contact c = db.getContact(txn, contactId); Contact c = db.getContact(txn, contactId);
Group group = getContactGroup(c); Group group = getContactGroup(c);
String name = msg.getForumName(); S f = getSFactory().parse(msg);
byte[] salt = msg.getForumSalt();
Forum f = forumFactory.createForum(name, salt);
// create local message to keep engine state // create local message to keep engine state
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
Bytes mSalt = new Bytes(new byte[FORUM_SALT_LENGTH]); Bytes mSalt = new Bytes(new byte[SHARING_SALT_LENGTH]);
random.nextBytes(mSalt.getBytes()); random.nextBytes(mSalt.getBytes());
Message m = clientHelper.createMessage(localGroup.getId(), now, Message m = clientHelper.createMessage(localGroup.getId(), now,
BdfList.of(mSalt)); BdfList.of(mSalt));
InviteeSessionState s = new InviteeSessionState(msg.getSessionId(), IS s = getISFactory().build(msg.getSessionId(),
m.getId(), group.getId(), m.getId(), group.getId(),
InviteeSessionState.State.AWAIT_INVITATION, contactId, InviteeSessionState.State.AWAIT_INVITATION, contactId, f);
f.getId(), f.getName(), f.getSalt());
// save local state to database // save local state to database
BdfDictionary d = s.toBdfDictionary(); BdfDictionary d = s.toBdfDictionary();
@@ -583,7 +585,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
return s; return s;
} }
private ForumSharingSessionState getSessionState(Transaction txn, private SharingSessionState getSessionState(Transaction txn,
SessionId sessionId, boolean warn) SessionId sessionId, boolean warn)
throws DbException, FormatException { throws DbException, FormatException {
@@ -612,11 +614,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
throw new FormatException(); throw new FormatException();
} }
return fromBdfDictionary(map.values().iterator().next()); return SharingSessionState
.fromBdfDictionary(getISFactory(), getSSFactory(),
map.values().iterator().next());
} }
} }
private SharerSessionState getSessionStateForSharer(Transaction txn, private SS getSessionStateForSharer(Transaction txn,
SessionId sessionId) SessionId sessionId)
throws DbException, FormatException { throws DbException, FormatException {
@@ -626,17 +630,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
if (!d.getBoolean(IS_SHARER)) throw new FormatException(); if (!d.getBoolean(IS_SHARER)) throw new FormatException();
return (SharerSessionState) fromBdfDictionary(d); return (SS) SharingSessionState
.fromBdfDictionary(getISFactory(), getSSFactory(), d);
} }
private InviteeSessionState getSessionStateForResponse(Transaction txn, private IS getSessionStateForResponse(Transaction txn,
Forum f, Contact c) throws DbException, FormatException { S f, Contact c) throws DbException, FormatException {
// query for invitee states for that forum in state await response // query for invitee states for that shareable in state await response
BdfDictionary query = BdfDictionary.of( BdfDictionary query = BdfDictionary.of(
new BdfEntry(IS_SHARER, false), new BdfEntry(IS_SHARER, false),
new BdfEntry(CONTACT_ID, c.getId().getInt()), new BdfEntry(CONTACT_ID, c.getId().getInt()),
new BdfEntry(FORUM_ID, f.getId()), new BdfEntry(SHAREABLE_ID, f.getId()),
new BdfEntry(STATE, new BdfEntry(STATE,
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE InviteeSessionState.State.AWAIT_LOCAL_RESPONSE
.getValue()) .getValue())
@@ -647,7 +652,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
if (map.size() > 1 && LOG.isLoggable(WARNING)) { if (map.size() > 1 && LOG.isLoggable(WARNING)) {
LOG.warning( LOG.warning(
"More than one session state found for forum with ID " + "More than one session state found for shareable with ID " +
Arrays.hashCode(f.getId().getBytes()) + Arrays.hashCode(f.getId().getBytes()) +
" in state AWAIT_LOCAL_RESPONSE for contact " + " in state AWAIT_LOCAL_RESPONSE for contact " +
c.getAuthor().getName()); c.getAuthor().getName());
@@ -655,35 +660,38 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
if (map.isEmpty()) { if (map.isEmpty()) {
if (LOG.isLoggable(WARNING)) { if (LOG.isLoggable(WARNING)) {
LOG.warning( LOG.warning(
"No session state found for forum with ID " + "No session state found for shareable with ID " +
Arrays.hashCode(f.getId().getBytes()) + Arrays.hashCode(f.getId().getBytes()) +
" in state AWAIT_LOCAL_RESPONSE"); " in state AWAIT_LOCAL_RESPONSE");
} }
throw new DbException(); throw new DbException();
} }
return (InviteeSessionState) fromBdfDictionary( return (IS) SharingSessionState
map.values().iterator().next()); .fromBdfDictionary(getISFactory(), getSSFactory(),
map.values().iterator().next());
} }
private ForumSharingSessionState getSessionStateForLeaving(Transaction txn, private SharingSessionState getSessionStateForLeaving(Transaction txn,
Forum f, ContactId c) throws DbException, FormatException { S f, ContactId c) throws DbException, FormatException {
BdfDictionary query = BdfDictionary.of( BdfDictionary query = BdfDictionary.of(
new BdfEntry(CONTACT_ID, c.getInt()), new BdfEntry(CONTACT_ID, c.getInt()),
new BdfEntry(FORUM_ID, f.getId()) new BdfEntry(SHAREABLE_ID, f.getId())
); );
Map<MessageId, BdfDictionary> map = clientHelper Map<MessageId, BdfDictionary> map = clientHelper
.getMessageMetadataAsDictionary(txn, localGroup.getId(), query); .getMessageMetadataAsDictionary(txn, localGroup.getId(), query);
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) { for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
BdfDictionary d = m.getValue(); BdfDictionary d = m.getValue();
try { try {
ForumSharingSessionState s = fromBdfDictionary(d); SharingSessionState s = SharingSessionState
.fromBdfDictionary(getISFactory(), getSSFactory(), d);
// check that a forum get be left in current session // check that a shareable get be left in current session
if (s instanceof SharerSessionState) { if (s instanceof SharerSessionState) {
SharerSessionState state = (SharerSessionState) s; SharerSessionState state = (SharerSessionState) s;
SharerSessionState.State nextState = SharerSessionState.State nextState =
state.getState().next(Action.LOCAL_LEAVE); state.getState()
.next(SharerSessionState.Action.LOCAL_LEAVE);
if (nextState != SharerSessionState.State.ERROR) { if (nextState != SharerSessionState.State.ERROR) {
return state; return state;
} }
@@ -703,11 +711,11 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
private void processStateUpdate(Transaction txn, MessageId messageId, private void processStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<ForumSharingSessionState, BaseMessage> result) StateUpdate<SharingSessionState, BaseMessage> result, S f)
throws DbException, FormatException { throws DbException, FormatException {
// perform actions based on new local state // perform actions based on new local state
performTasks(txn, result.localState); performTasks(txn, result.localState, f);
// save new local state // save new local state
MessageId storageId = result.localState.getStorageId(); MessageId storageId = result.localState.getStorageId();
@@ -735,31 +743,37 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
private void processSharerStateUpdate(Transaction txn, MessageId messageId, private void processSharerStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<SharerSessionState, BaseMessage> result) StateUpdate<SS, BaseMessage> result)
throws DbException, FormatException { throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BaseMessage> r = StateUpdate<SharingSessionState, BaseMessage> r =
new StateUpdate<ForumSharingSessionState, BaseMessage>( new StateUpdate<SharingSessionState, BaseMessage>(
result.deleteMessage, result.deleteState, result.deleteMessage, result.deleteState,
result.localState, result.toSend, result.toBroadcast); result.localState, result.toSend, result.toBroadcast);
processStateUpdate(txn, messageId, r); // get shareable for later
S f = getSFactory().parse(result.localState);
processStateUpdate(txn, messageId, r, f);
} }
private void processInviteeStateUpdate(Transaction txn, MessageId messageId, private void processInviteeStateUpdate(Transaction txn, MessageId messageId,
StateUpdate<InviteeSessionState, BaseMessage> result) StateUpdate<IS, BaseMessage> result)
throws DbException, FormatException { throws DbException, FormatException {
StateUpdate<ForumSharingSessionState, BaseMessage> r = StateUpdate<SharingSessionState, BaseMessage> r =
new StateUpdate<ForumSharingSessionState, BaseMessage>( new StateUpdate<SharingSessionState, BaseMessage>(
result.deleteMessage, result.deleteState, result.deleteMessage, result.deleteState,
result.localState, result.toSend, result.toBroadcast); result.localState, result.toSend, result.toBroadcast);
processStateUpdate(txn, messageId, r); // get shareable for later
S f = getSFactory().parse(result.localState);
processStateUpdate(txn, messageId, r, f);
} }
private void performTasks(Transaction txn, ForumSharingSessionState localState) private void performTasks(Transaction txn, SharingSessionState localState,
throws FormatException, DbException { S f) throws FormatException, DbException {
if (localState.getTask() == -1) return; if (localState.getTask() == -1) return;
@@ -772,37 +786,26 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
// get contact ID for later // get contact ID for later
ContactId contactId = localState.getContactId(); ContactId contactId = localState.getContactId();
// get forum for later
String name = localState.getForumName();
byte[] salt = localState.getForumSalt();
Forum f = forumFactory.createForum(name, salt);
// perform tasks // perform tasks
if (task == TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US) { if (task == TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US) {
addToList(txn, groupId, SHARED_WITH_US, f); addToList(txn, groupId, SHARED_WITH_US, f);
} } else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
else if (task == TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US) {
removeFromList(txn, groupId, SHARED_WITH_US, f); removeFromList(txn, groupId, SHARED_WITH_US, f);
} } else if (task == TASK_ADD_SHARED_SHAREABLE) {
else if (task == TASK_ADD_SHARED_FORUM) {
db.addGroup(txn, f.getGroup()); db.addGroup(txn, f.getGroup());
db.setVisibleToContact(txn, contactId, f.getId(), true); db.setVisibleToContact(txn, contactId, f.getId(), true);
} } else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
else if (task == TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US) {
addToList(txn, groupId, TO_BE_SHARED_BY_US, f); addToList(txn, groupId, TO_BE_SHARED_BY_US, f);
} } else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US) {
else if (task == TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US) {
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f); removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
} } else if (task == TASK_SHARE_SHAREABLE) {
else if (task == TASK_SHARE_FORUM) {
db.setVisibleToContact(txn, contactId, f.getId(), true); db.setVisibleToContact(txn, contactId, f.getId(), true);
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f); removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
addToList(txn, groupId, SHARED_BY_US, f); addToList(txn, groupId, SHARED_BY_US, f);
} } else if (task == TASK_UNSHARE_SHAREABLE_SHARED_BY_US) {
else if (task == TASK_UNSHARE_FORUM_SHARED_BY_US) {
db.setVisibleToContact(txn, contactId, f.getId(), false); db.setVisibleToContact(txn, contactId, f.getId(), false);
removeFromList(txn, groupId, SHARED_BY_US, f); removeFromList(txn, groupId, SHARED_BY_US, f);
} else if (task == TASK_UNSHARE_FORUM_SHARED_WITH_US) { } else if (task == TASK_UNSHARE_SHAREABLE_SHARED_WITH_US) {
db.setVisibleToContact(txn, contactId, f.getId(), false); db.setVisibleToContact(txn, contactId, f.getId(), false);
removeFromList(txn, groupId, SHARED_WITH_US, f); removeFromList(txn, groupId, SHARED_WITH_US, f);
} }
@@ -826,7 +829,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
} }
private Group getContactGroup(Contact c) { private Group getContactGroup(Contact c) {
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c); return privateGroupFactory.createPrivateGroup(getClientId(), c);
} }
private ContactId getContactId(Transaction txn, GroupId contactGroupId) private ContactId getContactId(Transaction txn, GroupId contactGroupId)
@@ -836,90 +839,94 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
return new ContactId(meta.getLong(CONTACT_ID).intValue()); return new ContactId(meta.getLong(CONTACT_ID).intValue());
} }
private void leaveForum(Transaction txn, ContactId c, Forum f) private void leaveShareable(Transaction txn, ContactId c, S f)
throws DbException, FormatException { throws DbException, FormatException {
ForumSharingSessionState state = getSessionStateForLeaving(txn, f, c); SharingSessionState state = getSessionStateForLeaving(txn, f, c);
if (state instanceof SharerSessionState) { if (state instanceof SharerSessionState) {
Action action = Action.LOCAL_LEAVE; SharerSessionState.Action action =
SharerEngine engine = new SharerEngine(); SharerSessionState.Action.LOCAL_LEAVE;
SharerEngine<I, SS, IRR> engine =
new SharerEngine<I, SS, IRR>(getIFactory(),
getIRRFactory());
processSharerStateUpdate(txn, null, processSharerStateUpdate(txn, null,
engine.onLocalAction((SharerSessionState) state, action)); engine.onLocalAction((SS) state, action));
} else { } else {
InviteeSessionState.Action action = InviteeSessionState.Action action =
InviteeSessionState.Action.LOCAL_LEAVE; InviteeSessionState.Action.LOCAL_LEAVE;
InviteeEngine engine = new InviteeEngine(forumFactory); InviteeEngine<IS, IR> engine =
new InviteeEngine<IS, IR>(getIRFactory());
processInviteeStateUpdate(txn, null, processInviteeStateUpdate(txn, null,
engine.onLocalAction((InviteeSessionState) state, action)); engine.onLocalAction((IS) state, action));
} }
} }
private boolean listContains(Transaction txn, GroupId contactGroup, private boolean listContains(Transaction txn, GroupId contactGroup,
GroupId forum, String key) throws DbException, FormatException { GroupId shareable, String key) throws DbException, FormatException {
List<Forum> list = getForumList(txn, contactGroup, key); List<S> list = getShareableList(txn, contactGroup, key);
for (Forum f : list) { for (S f : list) {
if (f.getId().equals(forum)) return true; if (f.getId().equals(shareable)) return true;
} }
return false; return false;
} }
private boolean addToList(Transaction txn, GroupId groupId, String key, private boolean addToList(Transaction txn, GroupId groupId, String key,
Forum f) throws DbException, FormatException { S f) throws DbException, FormatException {
List<Forum> forums = getForumList(txn, groupId, key); List<S> shareables = getShareableList(txn, groupId, key);
if (forums.contains(f)) return false; if (shareables.contains(f)) return false;
forums.add(f); shareables.add(f);
storeForumList(txn, groupId, key, forums); storeShareableList(txn, groupId, key, shareables);
return true; return true;
} }
private boolean removeFromList(Transaction txn, GroupId groupId, String key, private boolean removeFromList(Transaction txn, GroupId groupId, String key,
Forum f) throws DbException, FormatException { S f) throws DbException, FormatException {
List<Forum> forums = getForumList(txn, groupId, key); List<S> shareables = getShareableList(txn, groupId, key);
if (forums.remove(f)) { if (shareables.remove(f)) {
storeForumList(txn, groupId, key, forums); storeShareableList(txn, groupId, key, shareables);
return true; return true;
} }
return false; return false;
} }
private List<Forum> getForumList(Transaction txn, GroupId groupId, private List<S> getShareableList(Transaction txn, GroupId groupId,
String key) throws DbException, FormatException { String key) throws DbException, FormatException {
BdfDictionary metadata = BdfDictionary metadata =
clientHelper.getGroupMetadataAsDictionary(txn, groupId); clientHelper.getGroupMetadataAsDictionary(txn, groupId);
BdfList list = metadata.getList(key); BdfList list = metadata.getList(key);
return parseForumList(list); return parseShareableList(list);
} }
private void storeForumList(Transaction txn, GroupId groupId, String key, private void storeShareableList(Transaction txn, GroupId groupId,
List<Forum> forums) throws DbException, FormatException { String key,
List<S> shareables) throws DbException, FormatException {
BdfList list = encodeForumList(forums); BdfList list = encodeShareableList(shareables);
BdfDictionary metadata = BdfDictionary.of( BdfDictionary metadata = BdfDictionary.of(
new BdfEntry(key, list) new BdfEntry(key, list)
); );
clientHelper.mergeGroupMetadata(txn, groupId, metadata); clientHelper.mergeGroupMetadata(txn, groupId, metadata);
} }
private BdfList encodeForumList(List<Forum> forums) { private BdfList encodeShareableList(List<S> shareables) {
BdfList forumList = new BdfList(); BdfList shareableList = new BdfList();
for (Forum f : forums) for (S f : shareables)
forumList.add(BdfList.of(f.getName(), f.getSalt())); shareableList.add(getSFactory().encode(f));
return forumList; return shareableList;
} }
private List<Forum> parseForumList(BdfList list) throws FormatException { private List<S> parseShareableList(BdfList list) throws FormatException {
List<Forum> forums = new ArrayList<Forum>(list.size()); List<S> shareables = new ArrayList<S>(list.size());
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
BdfList forum = list.getList(i); BdfList shareable = list.getList(i);
forums.add(forumFactory shareables.add(getSFactory().parse(shareable));
.createForum(forum.getString(0), forum.getRaw(1)));
} }
return forums; return shareables;
} }
private void deleteMessage(Transaction txn, MessageId messageId) private void deleteMessage(Transaction txn, MessageId messageId)

View File

@@ -0,0 +1,62 @@
package org.briarproject.sharing;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.system.Clock;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
@Module
public class SharingModule {
public static class EagerSingletons {
@Inject
ForumSharingValidator forumSharingValidator;
@Inject
ForumSharingManager forumSharingManager;
}
@Provides
@Singleton
ForumSharingValidator provideForumSharingValidator(
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
MetadataEncoder metadataEncoder, Clock clock) {
ForumSharingValidator
validator = new ForumSharingValidator(clientHelper,
metadataEncoder, clock);
messageQueueManager.registerMessageValidator(
ForumSharingManagerImpl.CLIENT_ID, validator);
return validator;
}
@Provides
@Singleton
ForumSharingManager provideForumSharingManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
MessageQueueManager messageQueueManager,
ForumManager forumManager,
ForumSharingManagerImpl forumSharingManager) {
lifecycleManager.registerClient(forumSharingManager);
contactManager.registerAddContactHook(forumSharingManager);
contactManager.registerRemoveContactHook(forumSharingManager);
messageQueueManager.registerIncomingMessageHook(
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
forumManager.registerRemoveForumHook(forumSharingManager);
return forumSharingManager;
}
}

View File

@@ -0,0 +1,102 @@
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID;
import static org.briarproject.api.sharing.SharingConstants.STATE;
import static org.briarproject.api.sharing.SharingConstants.STORAGE_ID;
// This class is not thread-safe
public abstract class SharingSessionState {
private final SessionId sessionId;
private final MessageId storageId;
private final GroupId groupId;
private final ContactId contactId;
private final GroupId shareableId;
private int task = -1; // TODO get rid of task, see #376
public SharingSessionState(SessionId sessionId, MessageId storageId,
GroupId groupId, ContactId contactId, GroupId shareableId) {
this.sessionId = sessionId;
this.storageId = storageId;
this.groupId = groupId;
this.contactId = contactId;
this.shareableId = shareableId;
}
public static SharingSessionState fromBdfDictionary(
InviteeSessionStateFactory isFactory,
SharerSessionStateFactory ssFactory, BdfDictionary d)
throws FormatException {
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
GroupId forumId = new GroupId(d.getRaw(SHAREABLE_ID));
int intState = d.getLong(STATE).intValue();
if (d.getBoolean(IS_SHARER)) {
SharerSessionState.State state =
SharerSessionState.State.fromValue(intState);
return ssFactory.build(sessionId, messageId, groupId, state,
contactId, forumId, d);
} else {
InviteeSessionState.State state =
InviteeSessionState.State.fromValue(intState);
return isFactory.build(sessionId, messageId, groupId, state,
contactId, forumId, d);
}
}
public BdfDictionary toBdfDictionary() {
BdfDictionary d = new BdfDictionary();
d.put(SESSION_ID, getSessionId());
d.put(STORAGE_ID, getStorageId());
d.put(GROUP_ID, getGroupId());
d.put(CONTACT_ID, getContactId().getInt());
d.put(SHAREABLE_ID, getShareableId());
return d;
}
public SessionId getSessionId() {
return sessionId;
}
public MessageId getStorageId() {
return storageId;
}
public GroupId getGroupId() {
return groupId;
}
public ContactId getContactId() {
return contactId;
}
public GroupId getShareableId() {
return shareableId;
}
public void setTask(int task) {
this.task = task;
}
public int getTask() {
return task;
}
}

View File

@@ -5,7 +5,7 @@ import org.junit.Test;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
public class ForumSharingManagerImplTest extends BriarTestCase { public class SharingManagerImplTest extends BriarTestCase {
@Test @Test
public void testUnitTestsExist() { public void testUnitTestsExist() {