Factor out generic sharing code from ForumSharingManger

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

View File

@@ -31,6 +31,7 @@ import org.briarproject.crypto.CryptoModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.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());
}

View File

@@ -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);

View File

@@ -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());
}

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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");

View File

@@ -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());

View File

@@ -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 {

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

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

View File

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

View File

@@ -1,9 +1,10 @@
package org.briarproject.api.forum;
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;

View File

@@ -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";

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,6 +10,7 @@ import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.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);

View File

@@ -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());

View File

@@ -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;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum;
package org.briarproject.sharing;
import org.briarproject.api.FormatException;
import org.briarproject.api.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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum;
package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.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;
}

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum;
package org.briarproject.sharing;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.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;
}

View File

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

View File

@@ -1,4 +1,4 @@
package org.briarproject.forum;
package org.briarproject.sharing;
import org.briarproject.api.Bytes;
import org.briarproject.api.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)

View File

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

View File

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

View File

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