mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Factor out generic sharing code from ForumSharingManger
This commit is contained in:
@@ -31,6 +31,7 @@ import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.briarproject.util.StringUtils;
|
||||
@@ -188,7 +189,7 @@ public class ForumManagerTest {
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
@@ -218,7 +219,7 @@ public class ForumManagerTest {
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
@@ -259,7 +260,7 @@ public class ForumManagerTest {
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendForumInvitation(g, contactId1, null);
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
@@ -270,7 +271,7 @@ public class ForumManagerTest {
|
||||
// share a second forum
|
||||
Forum forum1 = forumManager0.addForum("Test Forum1");
|
||||
GroupId g1 = forum1.getId();
|
||||
forumSharingManager0.sendForumInvitation(g1, contactId1, null);
|
||||
forumSharingManager0.sendInvitation(g1, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
forumSharingManager1.respondToInvitation(forum1, c0, true);
|
||||
@@ -426,6 +427,7 @@ public class ForumManagerTest {
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SharingModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -40,6 +41,7 @@ import dagger.Component;
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
@@ -58,6 +60,8 @@ public interface ForumManagerTestComponent {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.junit.After;
|
||||
@@ -61,8 +62,8 @@ import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
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.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -159,7 +160,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -172,13 +173,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getAvailable().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// invitee has one invitation message from sharer
|
||||
List<ForumInvitationMessage> list =
|
||||
new ArrayList<>(forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
// check other things are alright with the forum message
|
||||
ForumInvitationMessage invitation = list.get(0);
|
||||
@@ -188,7 +189,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertEquals("Hi!", invitation.getMessage());
|
||||
// sharer has own invitation message
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// forum can not be shared again
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
@@ -209,7 +210,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -222,15 +223,15 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was not added
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getAvailable().size());
|
||||
assertEquals(0, forumManager1.getForums().size());
|
||||
// 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
|
||||
List<ForumInvitationMessage> list =
|
||||
new ArrayList<>(forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
// check other things are alright with the forum message
|
||||
ForumInvitationMessage invitation = list.get(0);
|
||||
@@ -240,7 +241,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertEquals(null, invitation.getMessage());
|
||||
// sharer has own invitation message
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// forum can be shared again
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
@@ -259,7 +260,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -272,7 +273,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getAvailable().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
@@ -292,7 +293,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
syncToSharer();
|
||||
|
||||
// forum is gone
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getAvailable().size());
|
||||
assertEquals(0, forumManager1.getForums().size());
|
||||
|
||||
// sharer no longer shares forum with invitee
|
||||
@@ -319,7 +320,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -332,7 +333,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getAvailable().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
@@ -378,7 +379,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
@@ -392,7 +393,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// invitee has no forums available
|
||||
assertEquals(0, forumSharingManager1.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager1.getAvailable().size());
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
@@ -407,7 +408,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -428,7 +429,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
// get SessionId from invitation
|
||||
List<ForumInvitationMessage> list = new ArrayList<>(
|
||||
forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
ForumInvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
@@ -475,7 +476,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -491,7 +492,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendForumInvitation(forum0.getId(),
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0,
|
||||
"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
|
||||
assertFalse(listener0.requestReceived);
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
@@ -524,10 +525,10 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendForumInvitation(forum0.getId(),
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0, "I am re-sharing this forum with you.");
|
||||
|
||||
// find out who should be Alice, because of random keys
|
||||
@@ -555,9 +556,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
assertEquals(1, forumSharingManager0
|
||||
.getForumInvitationMessages(contactId1).size());
|
||||
.getInvitationMessages(contactId1).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0).size());
|
||||
.getInvitationMessages(contactId0).size());
|
||||
} else {
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
@@ -568,9 +569,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener1.responseReceived);
|
||||
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getForumInvitationMessages(contactId1).size());
|
||||
.getInvitationMessages(contactId1).size());
|
||||
assertEquals(1, forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0).size());
|
||||
.getInvitationMessages(contactId0).size());
|
||||
}
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
@@ -586,7 +587,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -606,7 +607,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
// remember SessionId from invitation
|
||||
List<ForumInvitationMessage> list = new ArrayList<>(
|
||||
forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
ForumInvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
@@ -687,20 +688,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
|
||||
// second sharer sends invitation for same forum
|
||||
forumSharingManager2
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
// sync second request message
|
||||
deliverMessage(sync2, contactId2, sync1, contactId1,
|
||||
"Sharer2 to Invitee");
|
||||
|
||||
// make sure we have only one forum available
|
||||
Collection<Forum> forums =
|
||||
forumSharingManager1.getAvailableForums();
|
||||
forumSharingManager1.getAvailable();
|
||||
assertEquals(1, forums.size());
|
||||
|
||||
// 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 ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SharingModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -44,6 +45,7 @@ import dagger.Component;
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
@@ -62,6 +64,8 @@ public interface ForumSharingIntegrationTestComponent {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
@@ -352,7 +352,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
||||
|
||||
now = System.currentTimeMillis();
|
||||
Collection<ForumInvitationMessage> invitations =
|
||||
forumSharingManager.getForumInvitationMessages(id);
|
||||
forumSharingManager.getInvitationMessages(id);
|
||||
for (ForumInvitationMessage i : invitations) {
|
||||
messages.add(ConversationItem.from(i));
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ public class ConversationActivity extends BriarActivity
|
||||
.getIntroductionMessages(contactId);
|
||||
Collection<ForumInvitationMessage> invitations =
|
||||
forumSharingManager
|
||||
.getForumInvitationMessages(contactId);
|
||||
.getInvitationMessages(contactId);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading headers took " + duration + " ms");
|
||||
|
||||
@@ -82,7 +82,7 @@ public class AvailableForumsActivity extends BriarActivity
|
||||
try {
|
||||
Collection<ForumContacts> available = new ArrayList<>();
|
||||
long now = System.currentTimeMillis();
|
||||
for (Forum f : forumSharingManager.getAvailableForums()) {
|
||||
for (Forum f : forumSharingManager.getAvailable()) {
|
||||
try {
|
||||
Collection<Contact> c =
|
||||
forumSharingManager.getSharedBy(f.getId());
|
||||
|
||||
@@ -38,7 +38,7 @@ import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
|
||||
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
|
||||
BaseContactListAdapter.OnItemClickListener {
|
||||
|
||||
@@ -177,7 +177,7 @@ public class ForumListFragment extends BaseEventFragment implements
|
||||
try {
|
||||
long now = System.currentTimeMillis();
|
||||
int available =
|
||||
forumSharingManager.getAvailableForums().size();
|
||||
forumSharingManager.getAvailable().size();
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loading available took " + duration + " ms");
|
||||
|
||||
@@ -28,7 +28,7 @@ import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.android.forum.ShareForumActivity.CONTACTS;
|
||||
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 {
|
||||
|
||||
@@ -136,7 +136,7 @@ public class ShareForumMessageFragment extends BaseFragment {
|
||||
public void run() {
|
||||
try {
|
||||
for (ContactId c : contacts) {
|
||||
forumSharingManager.sendForumInvitation(groupId, c,
|
||||
forumSharingManager.sendInvitation(groupId, c,
|
||||
msg);
|
||||
}
|
||||
} catch (DbException e) {
|
||||
|
||||
@@ -4,21 +4,16 @@ import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.introduction.IntroductionRequest;
|
||||
|
||||
public class ForumInvitationReceivedEvent extends Event {
|
||||
public class ForumInvitationReceivedEvent extends InvitationReceivedEvent {
|
||||
|
||||
private final Forum forum;
|
||||
private final ContactId contactId;
|
||||
|
||||
public ForumInvitationReceivedEvent(Forum forum, ContactId contactId) {
|
||||
super(contactId);
|
||||
this.forum = forum;
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
public Forum getForum() {
|
||||
return forum;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,17 @@ package org.briarproject.api.event;
|
||||
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
|
||||
public class ForumInvitationResponseReceivedEvent extends Event {
|
||||
public class ForumInvitationResponseReceivedEvent extends InvitationResponseReceivedEvent {
|
||||
|
||||
private final String forumName;
|
||||
private final ContactId contactId;
|
||||
|
||||
public ForumInvitationResponseReceivedEvent(String forumName,
|
||||
ContactId contactId) {
|
||||
|
||||
super(contactId);
|
||||
this.forumName = forumName;
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
package org.briarproject.api.forum;
|
||||
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
public class Forum {
|
||||
public class Forum implements Shareable {
|
||||
|
||||
private final Group group;
|
||||
private final String name;
|
||||
|
||||
@@ -17,37 +17,8 @@ public interface ForumConstants {
|
||||
int MAX_FORUM_POST_BODY_LENGTH = MAX_MESSAGE_BODY_LENGTH - 1024;
|
||||
|
||||
/* 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_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
|
||||
String KEY_TIMESTAMP = "timestamp";
|
||||
|
||||
@@ -3,46 +3,25 @@ package org.briarproject.api.forum;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.messaging.BaseMessage;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public class ForumInvitationMessage extends BaseMessage {
|
||||
public class ForumInvitationMessage extends InvitationMessage {
|
||||
|
||||
private final SessionId sessionId;
|
||||
private final ContactId contactId;
|
||||
private final String forumName, message;
|
||||
private final boolean available;
|
||||
private final String forumName;
|
||||
|
||||
public ForumInvitationMessage(MessageId id, SessionId sessionId,
|
||||
ContactId contactId, String forumName, 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;
|
||||
super(id, sessionId, contactId, message, available, time, local, sent,
|
||||
seen, read);
|
||||
this.forumName = forumName;
|
||||
this.message = message;
|
||||
this.available = available;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public boolean isAvailable() {
|
||||
return available;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
package org.briarproject.api.forum;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sharing.SharingManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface ForumSharingManager {
|
||||
public interface ForumSharingManager extends SharingManager<Forum, ForumInvitationMessage> {
|
||||
|
||||
/** Returns the unique ID of the forum sharing client. */
|
||||
ClientId getClientId();
|
||||
@@ -18,7 +18,7 @@ public interface ForumSharingManager {
|
||||
* Sends an invitation to share the given forum with the given contact
|
||||
* and sends an optional message along with it.
|
||||
*/
|
||||
void sendForumInvitation(GroupId groupId, ContactId contactId,
|
||||
void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
String message) throws DbException;
|
||||
|
||||
/**
|
||||
@@ -31,11 +31,11 @@ public interface ForumSharingManager {
|
||||
* Returns all forum sharing messages sent by the Contact
|
||||
* identified by contactId.
|
||||
*/
|
||||
Collection<ForumInvitationMessage> getForumInvitationMessages(
|
||||
Collection<ForumInvitationMessage> getInvitationMessages(
|
||||
ContactId contactId) throws DbException;
|
||||
|
||||
/** 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. */
|
||||
Collection<Contact> getSharedBy(GroupId g) throws DbException;
|
||||
|
||||
@@ -3,89 +3,29 @@ package org.briarproject.api.forum;
|
||||
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.sharing.SharingMessage.Invitation;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
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.INVITATION_MSG;
|
||||
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;
|
||||
import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
|
||||
|
||||
public interface ForumSharingMessage {
|
||||
|
||||
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(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 {
|
||||
class ForumInvitation extends Invitation {
|
||||
|
||||
private final String forumName;
|
||||
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) {
|
||||
|
||||
super(groupId, sessionId);
|
||||
super(groupId, sessionId, message);
|
||||
|
||||
this.forumName = forumName;
|
||||
this.forumSalt = forumSalt;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getType() {
|
||||
return SHARE_MSG_TYPE_INVITATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,7 +46,7 @@ public interface ForumSharingMessage {
|
||||
return d;
|
||||
}
|
||||
|
||||
public static Invitation from(GroupId groupId, BdfDictionary d)
|
||||
public static ForumInvitation from(GroupId groupId, BdfDictionary d)
|
||||
throws FormatException {
|
||||
|
||||
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
|
||||
@@ -114,7 +54,7 @@ public interface ForumSharingMessage {
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
String message = d.getOptionalString(INVITATION_MSG);
|
||||
|
||||
return new Invitation(groupId, sessionId, forumName, forumSalt,
|
||||
return new ForumInvitation(groupId, sessionId, forumName, forumSalt,
|
||||
message);
|
||||
}
|
||||
|
||||
@@ -125,42 +65,5 @@ public interface ForumSharingMessage {
|
||||
public byte[] getForumSalt() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
11
briar-api/src/org/briarproject/api/sharing/Shareable.java
Normal file
11
briar-api/src/org/briarproject/api/sharing/Shareable.java
Normal 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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
122
briar-api/src/org/briarproject/api/sharing/SharingMessage.java
Normal file
122
briar-api/src/org/briarproject/api/sharing/SharingMessage.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.plugins.PluginsModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -36,6 +37,8 @@ public interface CoreEagerSingletons {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(SystemModule.EagerSingletons init);
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.reliability.ReliabilityModule;
|
||||
import org.briarproject.reporting.ReportingModule;
|
||||
import org.briarproject.settings.SettingsModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -47,6 +48,7 @@ import dagger.Module;
|
||||
ReliabilityModule.class,
|
||||
ReportingModule.class,
|
||||
SettingsModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
@@ -63,6 +65,7 @@ public class CoreModule {
|
||||
c.inject(new MessagingModule.EagerSingletons());
|
||||
c.inject(new PluginsModule.EagerSingletons());
|
||||
c.inject(new PropertiesModule.EagerSingletons());
|
||||
c.inject(new SharingModule.EagerSingletons());
|
||||
c.inject(new SyncModule.EagerSingletons());
|
||||
c.inject(new SystemModule.EagerSingletons());
|
||||
c.inject(new TransportModule.EagerSingletons());
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
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.data.MetadataEncoder;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.forum.ForumFactory;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
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.ValidationManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
@@ -31,10 +25,6 @@ public class ForumModule {
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
ForumPostValidator forumPostValidator;
|
||||
@Inject
|
||||
ForumSharingValidator forumSharingValidator;
|
||||
@Inject
|
||||
ForumSharingManager forumSharingManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -68,37 +58,4 @@ public class ForumModule {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
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.clients.BdfMessageValidator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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_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.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.TIME;
|
||||
import static org.briarproject.api.forum.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
|
||||
import static org.briarproject.api.sharing.SharingConstants.LOCAL;
|
||||
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.TIME;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TYPE;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
|
||||
class ForumSharingValidator extends BdfMessageValidator {
|
||||
|
||||
@Inject
|
||||
ForumSharingValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
222
briar-core/src/org/briarproject/sharing/InviteeEngine.java
Normal file
222
briar-core/src/org/briarproject/sharing/InviteeEngine.java
Normal 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());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
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.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
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.STATE;
|
||||
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.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
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.STATE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_INVITATION;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class InviteeSessionState extends ForumSharingSessionState {
|
||||
public abstract class InviteeSessionState extends SharingSessionState {
|
||||
|
||||
private State state;
|
||||
|
||||
public InviteeSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
GroupId groupId, State state, ContactId contactId,
|
||||
GroupId shareableId) {
|
||||
|
||||
super(sessionId, storageId, groupId, contactId, forumId, forumName,
|
||||
forumSalt);
|
||||
super(sessionId, storageId, groupId, contactId, shareableId);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
225
briar-core/src/org/briarproject/sharing/SharerEngine.java
Normal file
225
briar-core/src/org/briarproject/sharing/SharerEngine.java
Normal 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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
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.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
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.STATE;
|
||||
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.api.sharing.SharingConstants.IS_SHARER;
|
||||
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.STATE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_INVITATION;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class SharerSessionState extends ForumSharingSessionState {
|
||||
public abstract class SharerSessionState extends SharingSessionState {
|
||||
|
||||
private State state;
|
||||
private String msg = null;
|
||||
|
||||
public SharerSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
GroupId groupId, State state, ContactId contactId,
|
||||
GroupId shareableId) {
|
||||
|
||||
super(sessionId, storageId, groupId, contactId, forumId, forumName,
|
||||
forumSalt);
|
||||
super(sessionId, storageId, groupId, contactId, shareableId);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
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.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
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.event.InvitationReceivedEvent;
|
||||
import org.briarproject.api.event.InvitationResponseReceivedEvent;
|
||||
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.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
@@ -50,83 +50,90 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.clients.ProtocolEngine.StateUpdate;
|
||||
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_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.LOCAL;
|
||||
import static org.briarproject.api.forum.ForumConstants.READ;
|
||||
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARED_WITH_US;
|
||||
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.STATE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_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.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_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.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TIME;
|
||||
import static org.briarproject.api.forum.ForumConstants.TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.forum.ForumManager.RemoveForumHook;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
|
||||
import static org.briarproject.forum.ForumSharingSessionState.fromBdfDictionary;
|
||||
import static org.briarproject.forum.SharerSessionState.Action;
|
||||
import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.LOCAL;
|
||||
import static org.briarproject.api.sharing.SharingConstants.READ;
|
||||
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.SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARED_WITH_US;
|
||||
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.SHARING_SALT_LENGTH;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
|
||||
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_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_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.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TIME;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TYPE;
|
||||
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.sharing.SharingMessage.Invitation;
|
||||
|
||||
class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
implements ForumSharingManager, Client, RemoveForumHook,
|
||||
AddContactHook, RemoveContactHook {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"cd11a5d04dccd9e2931d6fc3df456313"
|
||||
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
|
||||
abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM extends InvitationMessage, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent>
|
||||
extends BdfIncomingMessageHook
|
||||
implements SharingManager<S, IM>, Client, AddContactHook,
|
||||
RemoveContactHook {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingManagerImpl.class.getName());
|
||||
Logger.getLogger(SharingManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ForumManager forumManager;
|
||||
private final MessageQueueManager messageQueueManager;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final SecureRandom random;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final ForumFactory forumFactory;
|
||||
private final Clock clock;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(DatabaseComponent db, ForumManager forumManager,
|
||||
SharingManagerImpl(DatabaseComponent db,
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataParser metadataParser, MetadataEncoder metadataEncoder,
|
||||
SecureRandom random, PrivateGroupFactory privateGroupFactory,
|
||||
ForumFactory forumFactory, Clock clock) {
|
||||
Clock clock) {
|
||||
|
||||
super(clientHelper, metadataParser);
|
||||
this.db = db;
|
||||
this.forumManager = forumManager;
|
||||
this.messageQueueManager = messageQueueManager;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.random = random;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.forumFactory = forumFactory;
|
||||
this.clock = clock;
|
||||
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
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
@@ -183,7 +190,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary d) throws DbException, FormatException {
|
||||
|
||||
BaseMessage msg = BaseMessage.from(m.getGroupId(), d);
|
||||
BaseMessage msg = BaseMessage.from(getIFactory(), m.getGroupId(), d);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
|
||||
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
|
||||
if (stateExists) throw new FormatException();
|
||||
|
||||
// check if forum can be shared
|
||||
Invitation invitation = (Invitation) msg;
|
||||
Forum f = forumFactory.createForum(invitation.getForumName(),
|
||||
invitation.getForumSalt());
|
||||
// check if shareable can be shared
|
||||
I invitation = (I) msg;
|
||||
S f = getSFactory().parse(invitation);
|
||||
ContactId contactId = getContactId(txn, m.getGroupId());
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
if (!canBeShared(txn, f.getId(), contact))
|
||||
checkForRaceCondition(txn, f, contact);
|
||||
|
||||
// initialize state and process invitation
|
||||
InviteeSessionState state =
|
||||
initializeInviteeState(txn, contactId, invitation);
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
IS state = initializeInviteeState(txn, contactId, invitation);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} catch (FormatException e) {
|
||||
@@ -222,48 +228,47 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
} else if (msg.getType() == SHARE_MSG_TYPE_ACCEPT ||
|
||||
msg.getType() == SHARE_MSG_TYPE_DECLINE) {
|
||||
// we are a sharer who just received a response
|
||||
SharerSessionState state = getSessionStateForSharer(txn, sessionId);
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SS state = getSessionStateForSharer(txn, sessionId);
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else if (msg.getType() == SHARE_MSG_TYPE_LEAVE ||
|
||||
msg.getType() == SHARE_MSG_TYPE_ABORT) {
|
||||
// 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) {
|
||||
// we are a sharer and the invitee wants to leave or abort
|
||||
SharerSessionState state = (SharerSessionState) s;
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SS state = (SS) s;
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else {
|
||||
// we are an invitee and the sharer wants to leave or abort
|
||||
InviteeSessionState state = (InviteeSessionState) s;
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
IS state = (IS) s;
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
}
|
||||
} else {
|
||||
// message has passed validator, so that should never happen
|
||||
throw new RuntimeException("Illegal Forum Sharing Message");
|
||||
throw new RuntimeException("Illegal Sharing Message");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForumInvitation(GroupId groupId, ContactId contactId,
|
||||
public void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
String msg) throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// initialize local state for sharer
|
||||
Forum f = forumManager.getForum(txn, groupId);
|
||||
SharerSessionState localState =
|
||||
initializeSharerState(txn, f, contactId);
|
||||
S f = getSFactory().get(txn, groupId);
|
||||
SS localState = initializeSharerState(txn, f, contactId);
|
||||
|
||||
// add invitation message to local state to be available for engine
|
||||
if (!StringUtils.isNullOrEmpty(msg)) {
|
||||
@@ -271,9 +276,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
// 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,
|
||||
engine.onLocalAction(localState, Action.LOCAL_INVITATION));
|
||||
engine.onLocalAction(localState,
|
||||
SharerSessionState.Action.LOCAL_INVITATION));
|
||||
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
@@ -284,14 +292,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(Forum f, Contact c, boolean accept)
|
||||
public void respondToInvitation(S f, Contact c, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// find session state based on forum
|
||||
InviteeSessionState localState =
|
||||
getSessionStateForResponse(txn, f, c);
|
||||
// find session state based on shareable
|
||||
IS localState = getSessionStateForResponse(txn, f, c);
|
||||
|
||||
// define action
|
||||
InviteeSessionState.Action localAction;
|
||||
@@ -302,7 +309,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, null,
|
||||
engine.onLocalAction(localState, localAction));
|
||||
|
||||
@@ -315,8 +323,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ForumInvitationMessage> getForumInvitationMessages(
|
||||
ContactId contactId) throws DbException {
|
||||
public Collection<IM> getInvitationMessages(ContactId contactId)
|
||||
throws DbException {
|
||||
|
||||
// query for all invitations
|
||||
BdfDictionary query = BdfDictionary.of(
|
||||
@@ -328,14 +336,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(contact);
|
||||
|
||||
Collection<ForumInvitationMessage> list =
|
||||
new ArrayList<ForumInvitationMessage>();
|
||||
Collection<IM> list = new ArrayList<IM>();
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, group.getId(), query);
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
try {
|
||||
Invitation msg = Invitation.from(group.getId(), d);
|
||||
I msg = getIFactory().build(group.getId(), d);
|
||||
MessageStatus status =
|
||||
db.getMessageStatus(txn, contactId, m.getKey());
|
||||
long time = d.getLong(TIME);
|
||||
@@ -343,20 +350,17 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
boolean read = d.getBoolean(READ, false);
|
||||
boolean available = false;
|
||||
if (!local) {
|
||||
// figure out whether the forum is still available
|
||||
ForumSharingSessionState s =
|
||||
// figure out whether the shareable is still available
|
||||
SharingSessionState s =
|
||||
getSessionState(txn, msg.getSessionId(), true);
|
||||
if (!(s instanceof InviteeSessionState))
|
||||
continue;
|
||||
available = ((InviteeSessionState) s).getState() ==
|
||||
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
|
||||
}
|
||||
ForumInvitationMessage im =
|
||||
new ForumInvitationMessage(m.getKey(),
|
||||
msg.getSessionId(), contactId,
|
||||
msg.getForumName(), msg.getMessage(),
|
||||
available, time, local, status.isSent(),
|
||||
status.isSeen(), read);
|
||||
IM im = createInvitationMessage(m.getKey(), msg, contactId,
|
||||
available, time, local, status.isSent(),
|
||||
status.isSeen(), read);
|
||||
list.add(im);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -373,40 +377,20 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingForum(Transaction txn, Forum f) throws DbException {
|
||||
public Collection<S> getAvailable() throws DbException {
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
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>();
|
||||
Set<S> available = new HashSet<S>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
// Get any forums we subscribe to
|
||||
// Get any shareables we subscribe to
|
||||
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
|
||||
forumManager.getClientId()));
|
||||
// Get all forums shared by contacts
|
||||
getShareableClientId()));
|
||||
// Get all shareables shared by contacts
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group g = getContactGroup(c);
|
||||
List<Forum> forums =
|
||||
getForumList(txn, g.getId(), SHARED_WITH_US);
|
||||
for (Forum f : forums) {
|
||||
List<S> shareables =
|
||||
getShareableList(txn, g.getId(), SHARED_WITH_US);
|
||||
for (S f : shareables) {
|
||||
if (!subscribed.contains(f.getGroup()))
|
||||
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 {
|
||||
|
||||
GroupId contactGroup = getContactGroup(c).getId();
|
||||
@@ -507,12 +510,14 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (alice) {
|
||||
// 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();
|
||||
} else {
|
||||
// we are Bob, so we need to "take back" our own invitation
|
||||
LOG.info("Invitation race-condition: We are Bob taking back our invitation.");
|
||||
ForumSharingSessionState state =
|
||||
LOG.info(
|
||||
"Invitation race-condition: We are Bob taking back our invitation.");
|
||||
SharingSessionState state =
|
||||
getSessionStateForLeaving(txn, f, c.getId());
|
||||
if (state instanceof SharerSessionState) {
|
||||
//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 {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
@@ -537,15 +542,15 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
// create local message to keep engine state
|
||||
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());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(salt));
|
||||
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,
|
||||
contactId, f.getId(), f.getName(), f.getSalt());
|
||||
contactId, f);
|
||||
|
||||
// save local state to database
|
||||
BdfDictionary d = s.toBdfDictionary();
|
||||
@@ -554,27 +559,24 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
return s;
|
||||
}
|
||||
|
||||
private InviteeSessionState initializeInviteeState(Transaction txn,
|
||||
ContactId contactId, Invitation msg)
|
||||
private IS initializeInviteeState(Transaction txn,
|
||||
ContactId contactId, I msg)
|
||||
throws FormatException, DbException {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(c);
|
||||
String name = msg.getForumName();
|
||||
byte[] salt = msg.getForumSalt();
|
||||
Forum f = forumFactory.createForum(name, salt);
|
||||
S f = getSFactory().parse(msg);
|
||||
|
||||
// create local message to keep engine state
|
||||
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());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(mSalt));
|
||||
|
||||
InviteeSessionState s = new InviteeSessionState(msg.getSessionId(),
|
||||
IS s = getISFactory().build(msg.getSessionId(),
|
||||
m.getId(), group.getId(),
|
||||
InviteeSessionState.State.AWAIT_INVITATION, contactId,
|
||||
f.getId(), f.getName(), f.getSalt());
|
||||
InviteeSessionState.State.AWAIT_INVITATION, contactId, f);
|
||||
|
||||
// save local state to database
|
||||
BdfDictionary d = s.toBdfDictionary();
|
||||
@@ -583,7 +585,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
return s;
|
||||
}
|
||||
|
||||
private ForumSharingSessionState getSessionState(Transaction txn,
|
||||
private SharingSessionState getSessionState(Transaction txn,
|
||||
SessionId sessionId, boolean warn)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -612,11 +614,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
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)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -626,17 +630,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (!d.getBoolean(IS_SHARER)) throw new FormatException();
|
||||
|
||||
return (SharerSessionState) fromBdfDictionary(d);
|
||||
return (SS) SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(), d);
|
||||
}
|
||||
|
||||
private InviteeSessionState getSessionStateForResponse(Transaction txn,
|
||||
Forum f, Contact c) throws DbException, FormatException {
|
||||
private IS getSessionStateForResponse(Transaction txn,
|
||||
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(
|
||||
new BdfEntry(IS_SHARER, false),
|
||||
new BdfEntry(CONTACT_ID, c.getId().getInt()),
|
||||
new BdfEntry(FORUM_ID, f.getId()),
|
||||
new BdfEntry(SHAREABLE_ID, f.getId()),
|
||||
new BdfEntry(STATE,
|
||||
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE
|
||||
.getValue())
|
||||
@@ -647,7 +652,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (map.size() > 1 && LOG.isLoggable(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()) +
|
||||
" in state AWAIT_LOCAL_RESPONSE for contact " +
|
||||
c.getAuthor().getName());
|
||||
@@ -655,35 +660,38 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
if (map.isEmpty()) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning(
|
||||
"No session state found for forum with ID " +
|
||||
"No session state found for shareable with ID " +
|
||||
Arrays.hashCode(f.getId().getBytes()) +
|
||||
" in state AWAIT_LOCAL_RESPONSE");
|
||||
}
|
||||
throw new DbException();
|
||||
}
|
||||
return (InviteeSessionState) fromBdfDictionary(
|
||||
map.values().iterator().next());
|
||||
return (IS) SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(),
|
||||
map.values().iterator().next());
|
||||
}
|
||||
|
||||
private ForumSharingSessionState getSessionStateForLeaving(Transaction txn,
|
||||
Forum f, ContactId c) throws DbException, FormatException {
|
||||
private SharingSessionState getSessionStateForLeaving(Transaction txn,
|
||||
S f, ContactId c) throws DbException, FormatException {
|
||||
|
||||
BdfDictionary query = BdfDictionary.of(
|
||||
new BdfEntry(CONTACT_ID, c.getInt()),
|
||||
new BdfEntry(FORUM_ID, f.getId())
|
||||
new BdfEntry(SHAREABLE_ID, f.getId())
|
||||
);
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId(), query);
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
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) {
|
||||
SharerSessionState state = (SharerSessionState) s;
|
||||
SharerSessionState.State nextState =
|
||||
state.getState().next(Action.LOCAL_LEAVE);
|
||||
state.getState()
|
||||
.next(SharerSessionState.Action.LOCAL_LEAVE);
|
||||
if (nextState != SharerSessionState.State.ERROR) {
|
||||
return state;
|
||||
}
|
||||
@@ -703,11 +711,11 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private void processStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> result)
|
||||
StateUpdate<SharingSessionState, BaseMessage> result, S f)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// perform actions based on new local state
|
||||
performTasks(txn, result.localState);
|
||||
performTasks(txn, result.localState, f);
|
||||
|
||||
// save new local state
|
||||
MessageId storageId = result.localState.getStorageId();
|
||||
@@ -735,31 +743,37 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private void processSharerStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<SharerSessionState, BaseMessage> result)
|
||||
StateUpdate<SS, BaseMessage> result)
|
||||
throws DbException, FormatException {
|
||||
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<ForumSharingSessionState, BaseMessage>(
|
||||
StateUpdate<SharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<SharingSessionState, BaseMessage>(
|
||||
result.deleteMessage, result.deleteState,
|
||||
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,
|
||||
StateUpdate<InviteeSessionState, BaseMessage> result)
|
||||
StateUpdate<IS, BaseMessage> result)
|
||||
throws DbException, FormatException {
|
||||
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<ForumSharingSessionState, BaseMessage>(
|
||||
StateUpdate<SharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<SharingSessionState, BaseMessage>(
|
||||
result.deleteMessage, result.deleteState,
|
||||
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)
|
||||
throws FormatException, DbException {
|
||||
private void performTasks(Transaction txn, SharingSessionState localState,
|
||||
S f) throws FormatException, DbException {
|
||||
|
||||
if (localState.getTask() == -1) return;
|
||||
|
||||
@@ -772,37 +786,26 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
// get contact ID for later
|
||||
ContactId contactId = localState.getContactId();
|
||||
|
||||
// get forum for later
|
||||
String name = localState.getForumName();
|
||||
byte[] salt = localState.getForumSalt();
|
||||
Forum f = forumFactory.createForum(name, salt);
|
||||
|
||||
// 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);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US) {
|
||||
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
else if (task == TASK_ADD_SHARED_FORUM) {
|
||||
} else if (task == TASK_ADD_SHARED_SHAREABLE) {
|
||||
db.addGroup(txn, f.getGroup());
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
||||
}
|
||||
else if (task == TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US) {
|
||||
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
|
||||
addToList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US) {
|
||||
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US) {
|
||||
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_SHARE_FORUM) {
|
||||
} else if (task == TASK_SHARE_SHAREABLE) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
||||
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
addToList(txn, groupId, SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_UNSHARE_FORUM_SHARED_BY_US) {
|
||||
} else if (task == TASK_UNSHARE_SHAREABLE_SHARED_BY_US) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), false);
|
||||
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);
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
@@ -826,7 +829,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private Group getContactGroup(Contact c) {
|
||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
||||
return privateGroupFactory.createPrivateGroup(getClientId(), c);
|
||||
}
|
||||
|
||||
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
@@ -836,90 +839,94 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
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 {
|
||||
|
||||
ForumSharingSessionState state = getSessionStateForLeaving(txn, f, c);
|
||||
SharingSessionState state = getSessionStateForLeaving(txn, f, c);
|
||||
if (state instanceof SharerSessionState) {
|
||||
Action action = Action.LOCAL_LEAVE;
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SharerSessionState.Action action =
|
||||
SharerSessionState.Action.LOCAL_LEAVE;
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, null,
|
||||
engine.onLocalAction((SharerSessionState) state, action));
|
||||
engine.onLocalAction((SS) state, action));
|
||||
} else {
|
||||
InviteeSessionState.Action action =
|
||||
InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, null,
|
||||
engine.onLocalAction((InviteeSessionState) state, action));
|
||||
engine.onLocalAction((IS) state, action));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for (Forum f : list) {
|
||||
if (f.getId().equals(forum)) return true;
|
||||
List<S> list = getShareableList(txn, contactGroup, key);
|
||||
for (S f : list) {
|
||||
if (f.getId().equals(shareable)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
if (forums.contains(f)) return false;
|
||||
forums.add(f);
|
||||
storeForumList(txn, groupId, key, forums);
|
||||
List<S> shareables = getShareableList(txn, groupId, key);
|
||||
if (shareables.contains(f)) return false;
|
||||
shareables.add(f);
|
||||
storeShareableList(txn, groupId, key, shareables);
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
if (forums.remove(f)) {
|
||||
storeForumList(txn, groupId, key, forums);
|
||||
List<S> shareables = getShareableList(txn, groupId, key);
|
||||
if (shareables.remove(f)) {
|
||||
storeShareableList(txn, groupId, key, shareables);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Forum> getForumList(Transaction txn, GroupId groupId,
|
||||
private List<S> getShareableList(Transaction txn, GroupId groupId,
|
||||
String key) throws DbException, FormatException {
|
||||
|
||||
BdfDictionary metadata =
|
||||
clientHelper.getGroupMetadataAsDictionary(txn, groupId);
|
||||
BdfList list = metadata.getList(key);
|
||||
|
||||
return parseForumList(list);
|
||||
return parseShareableList(list);
|
||||
}
|
||||
|
||||
private void storeForumList(Transaction txn, GroupId groupId, String key,
|
||||
List<Forum> forums) throws DbException, FormatException {
|
||||
private void storeShareableList(Transaction txn, GroupId groupId,
|
||||
String key,
|
||||
List<S> shareables) throws DbException, FormatException {
|
||||
|
||||
BdfList list = encodeForumList(forums);
|
||||
BdfList list = encodeShareableList(shareables);
|
||||
BdfDictionary metadata = BdfDictionary.of(
|
||||
new BdfEntry(key, list)
|
||||
);
|
||||
clientHelper.mergeGroupMetadata(txn, groupId, metadata);
|
||||
}
|
||||
|
||||
private BdfList encodeForumList(List<Forum> forums) {
|
||||
BdfList forumList = new BdfList();
|
||||
for (Forum f : forums)
|
||||
forumList.add(BdfList.of(f.getName(), f.getSalt()));
|
||||
return forumList;
|
||||
private BdfList encodeShareableList(List<S> shareables) {
|
||||
BdfList shareableList = new BdfList();
|
||||
for (S f : shareables)
|
||||
shareableList.add(getSFactory().encode(f));
|
||||
return shareableList;
|
||||
}
|
||||
|
||||
private List<Forum> parseForumList(BdfList list) throws FormatException {
|
||||
List<Forum> forums = new ArrayList<Forum>(list.size());
|
||||
private List<S> parseShareableList(BdfList list) throws FormatException {
|
||||
List<S> shareables = new ArrayList<S>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BdfList forum = list.getList(i);
|
||||
forums.add(forumFactory
|
||||
.createForum(forum.getString(0), forum.getRaw(1)));
|
||||
BdfList shareable = list.getList(i);
|
||||
shareables.add(getSFactory().parse(shareable));
|
||||
}
|
||||
return forums;
|
||||
return shareables;
|
||||
}
|
||||
|
||||
private void deleteMessage(Transaction txn, MessageId messageId)
|
||||
62
briar-core/src/org/briarproject/sharing/SharingModule.java
Normal file
62
briar-core/src/org/briarproject/sharing/SharingModule.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
102
briar-core/src/org/briarproject/sharing/SharingSessionState.java
Normal file
102
briar-core/src/org/briarproject/sharing/SharingSessionState.java
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ForumSharingManagerImplTest extends BriarTestCase {
|
||||
public class SharingManagerImplTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testUnitTestsExist() {
|
||||
Reference in New Issue
Block a user