mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 12:19:54 +01:00
Factor out generic sharing code from ForumSharingManger
This commit is contained in:
@@ -10,6 +10,7 @@ import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.plugins.PluginsModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -36,6 +37,8 @@ public interface CoreEagerSingletons {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(SystemModule.EagerSingletons init);
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.reliability.ReliabilityModule;
|
||||
import org.briarproject.reporting.ReportingModule;
|
||||
import org.briarproject.settings.SettingsModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -47,6 +48,7 @@ import dagger.Module;
|
||||
ReliabilityModule.class,
|
||||
ReportingModule.class,
|
||||
SettingsModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
@@ -63,6 +65,7 @@ public class CoreModule {
|
||||
c.inject(new MessagingModule.EagerSingletons());
|
||||
c.inject(new PluginsModule.EagerSingletons());
|
||||
c.inject(new PropertiesModule.EagerSingletons());
|
||||
c.inject(new SharingModule.EagerSingletons());
|
||||
c.inject(new SyncModule.EagerSingletons());
|
||||
c.inject(new SystemModule.EagerSingletons());
|
||||
c.inject(new TransportModule.EagerSingletons());
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.forum.ForumFactory;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.ValidationManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
@@ -31,10 +25,6 @@ public class ForumModule {
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
ForumPostValidator forumPostValidator;
|
||||
@Inject
|
||||
ForumSharingValidator forumSharingValidator;
|
||||
@Inject
|
||||
ForumSharingManager forumSharingManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@@ -68,37 +58,4 @@ public class ForumModule {
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingValidator provideSharingValidator(
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
|
||||
ForumSharingValidator validator = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
messageQueueManager.registerMessageValidator(
|
||||
ForumSharingManagerImpl.CLIENT_ID, validator);
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingManager provideForumSharingManager(
|
||||
LifecycleManager lifecycleManager,
|
||||
ContactManager contactManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
ForumManager forumManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
|
||||
lifecycleManager.registerClient(forumSharingManager);
|
||||
contactManager.registerAddContactHook(forumSharingManager);
|
||||
contactManager.registerRemoveContactHook(forumSharingManager);
|
||||
messageQueueManager.registerIncomingMessageHook(
|
||||
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
|
||||
forumManager.registerRemoveForumHook(forumSharingManager);
|
||||
|
||||
return forumSharingManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
|
||||
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.STATE;
|
||||
import static org.briarproject.api.forum.ForumConstants.STORAGE_ID;
|
||||
|
||||
// This class is not thread-safe
|
||||
public abstract class ForumSharingSessionState {
|
||||
|
||||
private final SessionId sessionId;
|
||||
private final MessageId storageId;
|
||||
private final GroupId groupId;
|
||||
private final ContactId contactId;
|
||||
private final GroupId forumId;
|
||||
private final String forumName;
|
||||
private final byte[] forumSalt;
|
||||
private int task = -1; // TODO get rid of task, see #376
|
||||
|
||||
public ForumSharingSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.storageId = storageId;
|
||||
this.groupId = groupId;
|
||||
this.contactId = contactId;
|
||||
this.forumId = forumId;
|
||||
this.forumName = forumName;
|
||||
this.forumSalt = forumSalt;
|
||||
}
|
||||
|
||||
public static ForumSharingSessionState fromBdfDictionary(BdfDictionary d)
|
||||
throws FormatException{
|
||||
|
||||
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
|
||||
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
|
||||
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
|
||||
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
|
||||
GroupId forumId = new GroupId(d.getRaw(FORUM_ID));
|
||||
String forumName = d.getString(FORUM_NAME);
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
|
||||
int intState = d.getLong(STATE).intValue();
|
||||
if (d.getBoolean(IS_SHARER)) {
|
||||
SharerSessionState.State state =
|
||||
SharerSessionState.State.fromValue(intState);
|
||||
return new SharerSessionState(sessionId, messageId, groupId, state,
|
||||
contactId, forumId, forumName, forumSalt);
|
||||
} else {
|
||||
InviteeSessionState.State state =
|
||||
InviteeSessionState.State.fromValue(intState);
|
||||
return new InviteeSessionState(sessionId, messageId, groupId, state,
|
||||
contactId, forumId, forumName, forumSalt);
|
||||
}
|
||||
}
|
||||
|
||||
public BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, getSessionId());
|
||||
d.put(STORAGE_ID, getStorageId());
|
||||
d.put(GROUP_ID, getGroupId());
|
||||
d.put(CONTACT_ID, getContactId().getInt());
|
||||
d.put(FORUM_ID, getForumId());
|
||||
d.put(FORUM_NAME, getForumName());
|
||||
d.put(FORUM_SALT, getForumSalt());
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public MessageId getStorageId() {
|
||||
return storageId;
|
||||
}
|
||||
|
||||
public GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public GroupId getForumId() {
|
||||
return forumId;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public byte[] getForumSalt() {
|
||||
return forumSalt;
|
||||
}
|
||||
|
||||
public void setTask(int task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public int getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ProtocolEngine;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumFactory;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ABORT;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE;
|
||||
import static org.briarproject.forum.InviteeSessionState.State;
|
||||
import static org.briarproject.forum.InviteeSessionState.State.ERROR;
|
||||
import static org.briarproject.forum.InviteeSessionState.State.FINISHED;
|
||||
import static org.briarproject.forum.InviteeSessionState.State.LEFT;
|
||||
|
||||
public class InviteeEngine
|
||||
implements ProtocolEngine<Action, InviteeSessionState, BaseMessage> {
|
||||
|
||||
private final ForumFactory forumFactory;
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
InviteeEngine(ForumFactory forumFactory) {
|
||||
this.forumFactory = forumFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<InviteeSessionState, BaseMessage> onLocalAction(
|
||||
InviteeSessionState localState, Action action) {
|
||||
|
||||
try {
|
||||
State currentState = localState.getState();
|
||||
State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
if (action == LOCAL_ABORT && currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
}
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Error: Invalid action in state " +
|
||||
currentState.name());
|
||||
}
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
List<BaseMessage> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == LOCAL_ACCEPT || action == LOCAL_DECLINE) {
|
||||
BaseMessage msg;
|
||||
if (action == LOCAL_ACCEPT) {
|
||||
localState.setTask(TASK_ADD_SHARED_FORUM);
|
||||
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
} else {
|
||||
localState.setTask(
|
||||
TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
|
||||
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
}
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else if (action == LOCAL_LEAVE) {
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<InviteeSessionState, BaseMessage>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<InviteeSessionState, BaseMessage> onMessageReceived(
|
||||
InviteeSessionState localState, BaseMessage msg) {
|
||||
|
||||
try {
|
||||
State currentState = localState.getState();
|
||||
Action action = Action.getRemote(msg.getType());
|
||||
State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
logMessageReceived(currentState, nextState, msg.getType(), msg);
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
|
||||
List<BaseMessage> messages = Collections.emptyList();
|
||||
List<Event> events = Collections.emptyList();
|
||||
boolean deleteMsg = false;
|
||||
|
||||
if (currentState == LEFT) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
deleteMsg = true;
|
||||
}
|
||||
// the sharer left the forum she had shared with us
|
||||
else if (action == REMOTE_LEAVE && currentState == FINISHED) {
|
||||
localState.setTask(TASK_UNSHARE_FORUM_SHARED_WITH_US);
|
||||
}
|
||||
else if (currentState == FINISHED) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
// note that LEAVE is possible, but was handled above
|
||||
deleteMsg = true;
|
||||
}
|
||||
// the sharer left the forum before we couldn't even respond
|
||||
else if (action == REMOTE_LEAVE) {
|
||||
localState.setTask(TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
|
||||
}
|
||||
// we have just received our invitation
|
||||
else if (action == REMOTE_INVITATION) {
|
||||
Forum forum = forumFactory
|
||||
.createForum(localState.getForumName(),
|
||||
localState.getForumSalt());
|
||||
localState.setTask(TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US);
|
||||
ContactId contactId = localState.getContactId();
|
||||
Event event = new ForumInvitationReceivedEvent(forum, contactId);
|
||||
events = Collections.singletonList(event);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<InviteeSessionState, BaseMessage>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(State state,
|
||||
InviteeSessionState localState, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "response";
|
||||
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
LOG.info("Sending " + a + " in state " + state.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + localState.getState().name()
|
||||
);
|
||||
}
|
||||
|
||||
private void logMessageReceived(State currentState, State nextState,
|
||||
long type, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String t = "unknown";
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
|
||||
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
|
||||
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<InviteeSessionState, BaseMessage> onMessageDelivered(
|
||||
InviteeSessionState localState, BaseMessage delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private StateUpdate<InviteeSessionState, BaseMessage> abortSession(
|
||||
State currentState, InviteeSessionState localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
localState.setState(ERROR);
|
||||
BaseMessage msg =
|
||||
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
|
||||
localState.getSessionId());
|
||||
List<BaseMessage> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<InviteeSessionState, BaseMessage>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<InviteeSessionState, BaseMessage> noUpdate(
|
||||
InviteeSessionState localState, boolean delete) throws FormatException {
|
||||
|
||||
return new StateUpdate<InviteeSessionState, BaseMessage>(delete, false,
|
||||
localState, Collections.<BaseMessage>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ProtocolEngine;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_SHARE_FORUM;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.SimpleMessage;
|
||||
import static org.briarproject.forum.SharerSessionState.Action;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_ABORT;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE;
|
||||
import static org.briarproject.forum.SharerSessionState.State;
|
||||
import static org.briarproject.forum.SharerSessionState.State.ERROR;
|
||||
import static org.briarproject.forum.SharerSessionState.State.FINISHED;
|
||||
import static org.briarproject.forum.SharerSessionState.State.LEFT;
|
||||
|
||||
public class SharerEngine
|
||||
implements ProtocolEngine<Action, SharerSessionState, BaseMessage> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
@Override
|
||||
public StateUpdate<SharerSessionState, BaseMessage> onLocalAction(
|
||||
SharerSessionState localState, Action action) {
|
||||
|
||||
try {
|
||||
State currentState = localState.getState();
|
||||
State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
if (action == LOCAL_ABORT && currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
}
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Error: Invalid action in state " +
|
||||
currentState.name());
|
||||
}
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
List<BaseMessage> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == LOCAL_INVITATION) {
|
||||
BaseMessage msg = new Invitation(localState.getGroupId(),
|
||||
localState.getSessionId(), localState.getForumName(),
|
||||
localState.getForumSalt(), localState.getMessage());
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, nextState, msg);
|
||||
|
||||
// remember that we offered to share this forum
|
||||
localState.setTask(TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US);
|
||||
}
|
||||
else if (action == LOCAL_LEAVE) {
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, nextState, msg);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<SharerSessionState, BaseMessage>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<SharerSessionState, BaseMessage> onMessageReceived(
|
||||
SharerSessionState localState, BaseMessage msg) {
|
||||
|
||||
try {
|
||||
State currentState = localState.getState();
|
||||
Action action = Action.getRemote(msg.getType());
|
||||
State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
logMessageReceived(currentState, nextState, msg.getType(), msg);
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
List<BaseMessage> messages = Collections.emptyList();
|
||||
List<Event> events = Collections.emptyList();
|
||||
boolean deleteMsg = false;
|
||||
|
||||
if (currentState == LEFT) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
deleteMsg = true;
|
||||
}
|
||||
else if (action == REMOTE_LEAVE) {
|
||||
localState.setTask(TASK_UNSHARE_FORUM_SHARED_BY_US);
|
||||
}
|
||||
else if (currentState == FINISHED) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
// note that LEAVE is possible, but was handled above
|
||||
deleteMsg = true;
|
||||
}
|
||||
// we have sent our invitation and just got a response
|
||||
else if (action == REMOTE_ACCEPT || action == REMOTE_DECLINE) {
|
||||
if (action == REMOTE_ACCEPT) {
|
||||
localState.setTask(TASK_SHARE_FORUM);
|
||||
} else {
|
||||
// this ensures that the forum can be shared again
|
||||
localState.setTask(
|
||||
TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US);
|
||||
}
|
||||
String name = localState.getForumName();
|
||||
ContactId c = localState.getContactId();
|
||||
Event event = new ForumInvitationResponseReceivedEvent(name, c);
|
||||
events = Collections.singletonList(event);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<SharerSessionState, BaseMessage>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(State currentState, State nextState,
|
||||
BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "invitation";
|
||||
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
LOG.info("Sending " + a + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
private void logMessageReceived(State currentState, State nextState,
|
||||
long type, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String t = "unknown";
|
||||
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
|
||||
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
|
||||
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
|
||||
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<SharerSessionState, BaseMessage> onMessageDelivered(
|
||||
SharerSessionState localState, BaseMessage delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private StateUpdate<SharerSessionState, BaseMessage> abortSession(
|
||||
State currentState, SharerSessionState localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
|
||||
localState.setState(ERROR);
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
List<BaseMessage> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<SharerSessionState, BaseMessage>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<SharerSessionState, BaseMessage> noUpdate(
|
||||
SharerSessionState localState, boolean delete)
|
||||
throws FormatException {
|
||||
|
||||
return new StateUpdate<SharerSessionState, BaseMessage>(delete, false,
|
||||
localState, Collections.<BaseMessage>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
|
||||
|
||||
public class ForumInviteeSessionState extends InviteeSessionState {
|
||||
|
||||
private final String forumName;
|
||||
private final byte[] forumSalt;
|
||||
|
||||
public ForumInviteeSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
super(sessionId, storageId, groupId, state, contactId, forumId);
|
||||
|
||||
this.forumName = forumName;
|
||||
this.forumSalt = forumSalt;
|
||||
}
|
||||
|
||||
public BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = super.toBdfDictionary();
|
||||
d.put(FORUM_NAME, getForumName());
|
||||
d.put(FORUM_SALT, getForumSalt());
|
||||
return d;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public byte[] getForumSalt() {
|
||||
return forumSalt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
|
||||
|
||||
public class ForumSharerSessionState extends SharerSessionState {
|
||||
|
||||
private final String forumName;
|
||||
private final byte[] forumSalt;
|
||||
|
||||
public ForumSharerSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
super(sessionId, storageId, groupId, state, contactId, forumId);
|
||||
|
||||
this.forumName = forumName;
|
||||
this.forumSalt = forumSalt;
|
||||
}
|
||||
|
||||
public BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = super.toBdfDictionary();
|
||||
d.put(FORUM_NAME, getForumName());
|
||||
d.put(FORUM_SALT, getForumSalt());
|
||||
return d;
|
||||
}
|
||||
|
||||
public String getForumName() {
|
||||
return forumName;
|
||||
}
|
||||
|
||||
public byte[] getForumSalt() {
|
||||
return forumSalt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.data.MetadataParser;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumFactory;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.forum.ForumSharingMessage.ForumInvitation;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
|
||||
|
||||
class ForumSharingManagerImpl extends
|
||||
SharingManagerImpl<Forum, ForumInvitation, ForumInvitationMessage, ForumInviteeSessionState, ForumSharerSessionState, ForumInvitationReceivedEvent, ForumInvitationResponseReceivedEvent>
|
||||
implements ForumSharingManager, ForumManager.RemoveForumHook {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"cd11a5d04dccd9e2931d6fc3df456313"
|
||||
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
|
||||
|
||||
private final ForumManager forumManager;
|
||||
|
||||
private final SFactory sFactory;
|
||||
private final IFactory iFactory;
|
||||
private final ISFactory isFactory;
|
||||
private final SSFactory ssFactory;
|
||||
private final IRFactory irFactory;
|
||||
private final IRRFactory irrFactory;
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(ClientHelper clientHelper,
|
||||
Clock clock, DatabaseComponent db,
|
||||
ForumFactory forumFactory,
|
||||
ForumManager forumManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
MetadataEncoder metadataEncoder,
|
||||
MetadataParser metadataParser,
|
||||
PrivateGroupFactory privateGroupFactory,
|
||||
SecureRandom random) {
|
||||
super(db, messageQueueManager, clientHelper, metadataParser,
|
||||
metadataEncoder, random, privateGroupFactory, clock);
|
||||
this.forumManager = forumManager;
|
||||
|
||||
sFactory = new SFactory(forumFactory, forumManager);
|
||||
iFactory = new IFactory();
|
||||
isFactory = new ISFactory();
|
||||
ssFactory = new SSFactory();
|
||||
irFactory = new IRFactory(sFactory);
|
||||
irrFactory = new IRRFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientId getShareableClientId() {
|
||||
return forumManager.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ForumInvitationMessage createInvitationMessage(MessageId id,
|
||||
ForumInvitation msg, ContactId contactId, boolean available,
|
||||
long time, boolean local, boolean sent, boolean seen,
|
||||
boolean read) {
|
||||
return new ForumInvitationMessage(id, msg.getSessionId(), contactId,
|
||||
msg.getForumName(), msg.getMessage(), available, time, local,
|
||||
sent, seen, read);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> getSFactory() {
|
||||
return sFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationFactory<ForumInvitation, ForumSharerSessionState> getIFactory() {
|
||||
return iFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InviteeSessionStateFactory<Forum, ForumInviteeSessionState> getISFactory() {
|
||||
return isFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SharerSessionStateFactory<Forum, ForumSharerSessionState> getSSFactory() {
|
||||
return ssFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationReceivedEvent> getIRFactory() {
|
||||
return irFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> getIRRFactory() {
|
||||
return irrFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingForum(Transaction txn, Forum f) throws DbException {
|
||||
removingShareable(txn, f);
|
||||
}
|
||||
|
||||
static class SFactory implements
|
||||
ShareableFactory<Forum, ForumInvitation, ForumInviteeSessionState, ForumSharerSessionState> {
|
||||
|
||||
private final ForumFactory forumFactory;
|
||||
private final ForumManager forumManager;
|
||||
|
||||
SFactory(ForumFactory forumFactory, ForumManager forumManager) {
|
||||
this.forumFactory = forumFactory;
|
||||
this.forumManager = forumManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BdfList encode(Forum f) {
|
||||
return BdfList.of(f.getName(), f.getSalt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum get(Transaction txn, GroupId groupId)
|
||||
throws DbException {
|
||||
return forumManager.getForum(txn, groupId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(BdfList shareable) throws FormatException {
|
||||
return forumFactory
|
||||
.createForum(shareable.getString(0), shareable.getRaw(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(ForumInvitation msg) {
|
||||
return forumFactory
|
||||
.createForum(msg.getForumName(), msg.getForumSalt());
|
||||
}
|
||||
|
||||
public Forum parse(ForumInviteeSessionState state) {
|
||||
return forumFactory
|
||||
.createForum(state.getForumName(), state.getForumSalt());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum parse(ForumSharerSessionState state) {
|
||||
return forumFactory
|
||||
.createForum(state.getForumName(), state.getForumSalt());
|
||||
}
|
||||
}
|
||||
|
||||
static class IFactory implements
|
||||
InvitationFactory<ForumInvitation, ForumSharerSessionState> {
|
||||
@Override
|
||||
public ForumInvitation build(GroupId groupId, BdfDictionary d)
|
||||
throws FormatException {
|
||||
return ForumInvitation.from(groupId, d);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitation build(ForumSharerSessionState localState) {
|
||||
return new ForumInvitation(localState.getGroupId(),
|
||||
localState.getSessionId(), localState.getForumName(),
|
||||
localState.getForumSalt(), localState.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static class ISFactory implements
|
||||
InviteeSessionStateFactory<Forum, ForumInviteeSessionState> {
|
||||
@Override
|
||||
public ForumInviteeSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId,
|
||||
GroupId forumId, BdfDictionary d) throws FormatException {
|
||||
String forumName = d.getString(FORUM_NAME);
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
return new ForumInviteeSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forumId, forumName, forumSalt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInviteeSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId,
|
||||
Forum forum) {
|
||||
return new ForumInviteeSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forum.getId(), forum.getName(),
|
||||
forum.getSalt());
|
||||
}
|
||||
}
|
||||
|
||||
static class SSFactory implements
|
||||
SharerSessionStateFactory<Forum, ForumSharerSessionState> {
|
||||
@Override
|
||||
public ForumSharerSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId,
|
||||
GroupId forumId, BdfDictionary d) throws FormatException {
|
||||
String forumName = d.getString(FORUM_NAME);
|
||||
byte[] forumSalt = d.getRaw(FORUM_SALT);
|
||||
return new ForumSharerSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forumId, forumName, forumSalt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumSharerSessionState build(SessionId sessionId,
|
||||
MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId,
|
||||
Forum forum) {
|
||||
return new ForumSharerSessionState(sessionId, storageId,
|
||||
groupId, state, contactId, forum.getId(), forum.getName(),
|
||||
forum.getSalt());
|
||||
}
|
||||
}
|
||||
|
||||
static class IRFactory implements
|
||||
InvitationReceivedEventFactory<ForumInviteeSessionState, ForumInvitationReceivedEvent> {
|
||||
|
||||
private final SFactory sFactory;
|
||||
|
||||
IRFactory(SFactory sFactory) {
|
||||
this.sFactory = sFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumInvitationReceivedEvent build(
|
||||
ForumInviteeSessionState localState) {
|
||||
Forum forum = sFactory.parse(localState);
|
||||
ContactId contactId = localState.getContactId();
|
||||
return new ForumInvitationReceivedEvent(forum, contactId);
|
||||
}
|
||||
}
|
||||
|
||||
static class IRRFactory implements
|
||||
InvitationResponseReceivedEventFactory<ForumSharerSessionState, ForumInvitationResponseReceivedEvent> {
|
||||
@Override
|
||||
public ForumInvitationResponseReceivedEvent build(
|
||||
ForumSharerSessionState localState) {
|
||||
String name = localState.getForumName();
|
||||
ContactId c = localState.getContactId();
|
||||
return new ForumInvitationResponseReceivedEvent(name, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
@@ -12,25 +12,27 @@ import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfMessageValidator;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_NAME;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.GROUP_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.INVITATION_MSG;
|
||||
import static org.briarproject.api.forum.ForumConstants.LOCAL;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TIME;
|
||||
import static org.briarproject.api.forum.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.INVITATION_MSG;
|
||||
import static org.briarproject.api.sharing.SharingConstants.LOCAL;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TIME;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TYPE;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
|
||||
class ForumSharingValidator extends BdfMessageValidator {
|
||||
|
||||
@Inject
|
||||
ForumSharingValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
@@ -0,0 +1,9 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.sharing.SharingMessage;
|
||||
|
||||
public interface InvitationFactory<I extends SharingMessage.Invitation, SS extends SharerSessionState> extends
|
||||
org.briarproject.api.sharing.InvitationFactory<I> {
|
||||
|
||||
I build(SS localState);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.event.InvitationReceivedEvent;
|
||||
|
||||
public interface InvitationReceivedEventFactory<IS extends InviteeSessionState, IR extends InvitationReceivedEvent> {
|
||||
|
||||
IR build(IS localState);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.event.InvitationResponseReceivedEvent;
|
||||
|
||||
public interface InvitationResponseReceivedEventFactory<SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent> {
|
||||
|
||||
IRR build(SS localState);
|
||||
}
|
||||
222
briar-core/src/org/briarproject/sharing/InviteeEngine.java
Normal file
222
briar-core/src/org/briarproject/sharing/InviteeEngine.java
Normal file
@@ -0,0 +1,222 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ProtocolEngine;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.InvitationReceivedEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHARED_SHAREABLE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
|
||||
|
||||
public class InviteeEngine<IS extends InviteeSessionState, IR extends InvitationReceivedEvent>
|
||||
implements ProtocolEngine<InviteeSessionState.Action, IS, BaseMessage> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(InviteeEngine.class.getName());
|
||||
|
||||
private final InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory;
|
||||
|
||||
InviteeEngine(InvitationReceivedEventFactory<IS, IR> invitationReceivedEventFactory) {
|
||||
this.invitationReceivedEventFactory = invitationReceivedEventFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<IS, BaseMessage> onLocalAction(
|
||||
IS localState, InviteeSessionState.Action action) {
|
||||
|
||||
try {
|
||||
InviteeSessionState.State currentState = localState.getState();
|
||||
InviteeSessionState.State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
if (action == InviteeSessionState.Action.LOCAL_ABORT && currentState != InviteeSessionState.State.ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
}
|
||||
|
||||
if (nextState == InviteeSessionState.State.ERROR) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Error: Invalid action in state " +
|
||||
currentState.name());
|
||||
}
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
List<BaseMessage> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == InviteeSessionState.Action.LOCAL_ACCEPT || action == InviteeSessionState.Action.LOCAL_DECLINE) {
|
||||
BaseMessage msg;
|
||||
if (action == InviteeSessionState.Action.LOCAL_ACCEPT) {
|
||||
localState.setTask(TASK_ADD_SHARED_SHAREABLE);
|
||||
msg = new SimpleMessage(SHARE_MSG_TYPE_ACCEPT,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
} else {
|
||||
localState.setTask(
|
||||
TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
|
||||
msg = new SimpleMessage(SHARE_MSG_TYPE_DECLINE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
}
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else if (action == InviteeSessionState.Action.LOCAL_LEAVE) {
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<IS, BaseMessage>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<IS, BaseMessage> onMessageReceived(
|
||||
IS localState, BaseMessage msg) {
|
||||
|
||||
try {
|
||||
InviteeSessionState.State currentState = localState.getState();
|
||||
InviteeSessionState.Action action = InviteeSessionState.Action.getRemote(msg.getType());
|
||||
InviteeSessionState.State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
logMessageReceived(currentState, nextState, msg.getType(), msg);
|
||||
|
||||
if (nextState == InviteeSessionState.State.ERROR) {
|
||||
if (currentState != InviteeSessionState.State.ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
|
||||
List<BaseMessage> messages = Collections.emptyList();
|
||||
List<Event> events = Collections.emptyList();
|
||||
boolean deleteMsg = false;
|
||||
|
||||
if (currentState == InviteeSessionState.State.LEFT) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
deleteMsg = true;
|
||||
}
|
||||
// the sharer left the forum she had shared with us
|
||||
else if (action == InviteeSessionState.Action.REMOTE_LEAVE && currentState == InviteeSessionState.State.FINISHED) {
|
||||
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_WITH_US);
|
||||
}
|
||||
else if (currentState == InviteeSessionState.State.FINISHED) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
// note that LEAVE is possible, but was handled above
|
||||
deleteMsg = true;
|
||||
}
|
||||
// the sharer left the forum before we couldn't even respond
|
||||
else if (action == InviteeSessionState.Action.REMOTE_LEAVE) {
|
||||
localState.setTask(TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US);
|
||||
}
|
||||
// we have just received our invitation
|
||||
else if (action == InviteeSessionState.Action.REMOTE_INVITATION) {
|
||||
localState.setTask(TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US);
|
||||
Event event = invitationReceivedEventFactory.build(localState);
|
||||
events = Collections.singletonList(event);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<IS, BaseMessage>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(InviteeSessionState.State state,
|
||||
InviteeSessionState localState, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "response";
|
||||
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
LOG.info("Sending " + a + " in state " + state.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + localState.getState().name()
|
||||
);
|
||||
}
|
||||
|
||||
private void logMessageReceived(InviteeSessionState.State currentState, InviteeSessionState.State nextState,
|
||||
long type, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String t = "unknown";
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) t = "INVITE";
|
||||
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
|
||||
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<IS, BaseMessage> onMessageDelivered(
|
||||
IS localState, BaseMessage delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private StateUpdate<IS, BaseMessage> abortSession(
|
||||
InviteeSessionState.State currentState, IS localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
localState.setState(InviteeSessionState.State.ERROR);
|
||||
BaseMessage msg =
|
||||
new SimpleMessage(SHARE_MSG_TYPE_ABORT, localState.getGroupId(),
|
||||
localState.getSessionId());
|
||||
List<BaseMessage> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<IS, BaseMessage>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<IS, BaseMessage> noUpdate(
|
||||
IS localState, boolean delete) throws FormatException {
|
||||
|
||||
return new StateUpdate<IS, BaseMessage>(delete, false,
|
||||
localState, Collections.<BaseMessage>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
@@ -6,28 +6,27 @@ import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.STATE;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_INVITATION;
|
||||
import static org.briarproject.forum.InviteeSessionState.Action.REMOTE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_ACCEPT;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_DECLINE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_INVITATION;
|
||||
import static org.briarproject.sharing.InviteeSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class InviteeSessionState extends ForumSharingSessionState {
|
||||
public abstract class InviteeSessionState extends SharingSessionState {
|
||||
|
||||
private State state;
|
||||
|
||||
public InviteeSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
GroupId groupId, State state, ContactId contactId,
|
||||
GroupId shareableId) {
|
||||
|
||||
super(sessionId, storageId, groupId, contactId, forumId, forumName,
|
||||
forumSalt);
|
||||
super(sessionId, storageId, groupId, contactId, shareableId);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public interface InviteeSessionStateFactory<S extends Shareable, IS extends InviteeSessionState> {
|
||||
|
||||
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId,
|
||||
GroupId shareableId, BdfDictionary d) throws FormatException;
|
||||
|
||||
IS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
InviteeSessionState.State state, ContactId contactId, S shareable);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sharing.SharingMessage;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
interface ShareableFactory<S extends Shareable, I extends SharingMessage.Invitation, IS extends InviteeSessionState, SS extends SharerSessionState> {
|
||||
|
||||
BdfList encode(S sh);
|
||||
|
||||
S get(Transaction txn, GroupId groupId) throws DbException;
|
||||
|
||||
S parse(BdfList shareable) throws FormatException;
|
||||
|
||||
S parse(I msg);
|
||||
|
||||
S parse(IS state);
|
||||
|
||||
S parse(SS state);
|
||||
}
|
||||
225
briar-core/src/org/briarproject/sharing/SharerEngine.java
Normal file
225
briar-core/src/org/briarproject/sharing/SharerEngine.java
Normal file
@@ -0,0 +1,225 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ProtocolEngine;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.InvitationResponseReceivedEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_SHARE_SHAREABLE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.sharing.SharingMessage.Invitation;
|
||||
import static org.briarproject.api.sharing.SharingMessage.SimpleMessage;
|
||||
|
||||
public class SharerEngine<I extends Invitation, SS extends SharerSessionState, IRR extends InvitationResponseReceivedEvent>
|
||||
implements ProtocolEngine<SharerSessionState.Action, SS, BaseMessage> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
private final InvitationFactory<I, SS> invitationFactory;
|
||||
private final InvitationResponseReceivedEventFactory<SS, IRR>
|
||||
invitationResponseReceivedEventFactory;
|
||||
|
||||
SharerEngine(InvitationFactory<I, SS> invitationFactory,
|
||||
InvitationResponseReceivedEventFactory<SS, IRR> invitationResponseReceivedEventFactory) {
|
||||
this.invitationFactory = invitationFactory;
|
||||
this.invitationResponseReceivedEventFactory =
|
||||
invitationResponseReceivedEventFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<SS, BaseMessage> onLocalAction(
|
||||
SS localState, SharerSessionState.Action action) {
|
||||
|
||||
try {
|
||||
SharerSessionState.State currentState = localState.getState();
|
||||
SharerSessionState.State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
if (action == SharerSessionState.Action.LOCAL_ABORT &&
|
||||
currentState != SharerSessionState.State.ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
}
|
||||
|
||||
if (nextState == SharerSessionState.State.ERROR) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Error: Invalid action in state " +
|
||||
currentState.name());
|
||||
}
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
List<BaseMessage> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == SharerSessionState.Action.LOCAL_INVITATION) {
|
||||
BaseMessage msg = invitationFactory.build(localState);
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, nextState, msg);
|
||||
|
||||
// remember that we offered to share this forum
|
||||
localState
|
||||
.setTask(TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US);
|
||||
} else if (action == SharerSessionState.Action.LOCAL_LEAVE) {
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_LEAVE,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, nextState, msg);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<SS, BaseMessage>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<SS, BaseMessage> onMessageReceived(
|
||||
SS localState, BaseMessage msg) {
|
||||
|
||||
try {
|
||||
SharerSessionState.State currentState = localState.getState();
|
||||
SharerSessionState.Action action =
|
||||
SharerSessionState.Action.getRemote(msg.getType());
|
||||
SharerSessionState.State nextState = currentState.next(action);
|
||||
localState.setState(nextState);
|
||||
|
||||
logMessageReceived(currentState, nextState, msg.getType(), msg);
|
||||
|
||||
if (nextState == SharerSessionState.State.ERROR) {
|
||||
if (currentState != SharerSessionState.State.ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
List<BaseMessage> messages = Collections.emptyList();
|
||||
List<Event> events = Collections.emptyList();
|
||||
boolean deleteMsg = false;
|
||||
|
||||
if (currentState == SharerSessionState.State.LEFT) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
deleteMsg = true;
|
||||
} else if (action == SharerSessionState.Action.REMOTE_LEAVE) {
|
||||
localState.setTask(TASK_UNSHARE_SHAREABLE_SHARED_BY_US);
|
||||
} else if (currentState == SharerSessionState.State.FINISHED) {
|
||||
// ignore and delete messages coming in while in that state
|
||||
// note that LEAVE is possible, but was handled above
|
||||
deleteMsg = true;
|
||||
}
|
||||
// we have sent our invitation and just got a response
|
||||
else if (action == SharerSessionState.Action.REMOTE_ACCEPT ||
|
||||
action == SharerSessionState.Action.REMOTE_DECLINE) {
|
||||
if (action == SharerSessionState.Action.REMOTE_ACCEPT) {
|
||||
localState.setTask(TASK_SHARE_SHAREABLE);
|
||||
} else {
|
||||
// this ensures that the forum can be shared again
|
||||
localState.setTask(
|
||||
TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US);
|
||||
}
|
||||
Event event = invitationResponseReceivedEventFactory
|
||||
.build(localState);
|
||||
events = Collections.singletonList(event);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<SS, BaseMessage>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(SharerSessionState.State currentState,
|
||||
SharerSessionState.State nextState,
|
||||
BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "invitation";
|
||||
if (msg.getType() == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
LOG.info("Sending " + a + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
private void logMessageReceived(SharerSessionState.State currentState,
|
||||
SharerSessionState.State nextState,
|
||||
long type, BaseMessage msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String t = "unknown";
|
||||
if (type == SHARE_MSG_TYPE_ACCEPT) t = "ACCEPT";
|
||||
else if (type == SHARE_MSG_TYPE_DECLINE) t = "DECLINE";
|
||||
else if (type == SHARE_MSG_TYPE_LEAVE) t = "LEAVE";
|
||||
else if (type == SHARE_MSG_TYPE_ABORT) t = "ABORT";
|
||||
|
||||
LOG.info("Received " + t + " in state " + currentState.name() +
|
||||
" with session ID " +
|
||||
msg.getSessionId().hashCode() + " in group " +
|
||||
msg.getGroupId().hashCode() + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<SS, BaseMessage> onMessageDelivered(
|
||||
SS localState, BaseMessage delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private StateUpdate<SS, BaseMessage> abortSession(
|
||||
SharerSessionState.State currentState, SS localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
localState.getSessionId().hashCode() +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
|
||||
localState.setState(SharerSessionState.State.ERROR);
|
||||
BaseMessage msg = new SimpleMessage(SHARE_MSG_TYPE_ABORT,
|
||||
localState.getGroupId(), localState.getSessionId());
|
||||
List<BaseMessage> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<SS, BaseMessage>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<SS, BaseMessage> noUpdate(
|
||||
SS localState, boolean delete)
|
||||
throws FormatException {
|
||||
|
||||
return new StateUpdate<SS, BaseMessage>(delete, false,
|
||||
localState, Collections.<BaseMessage>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
@@ -6,30 +6,29 @@ import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.STATE;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_INVITATION;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_ACCEPT;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
import static org.briarproject.forum.SharerSessionState.Action.REMOTE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_INVITATION;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.LOCAL_LEAVE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_ACCEPT;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_DECLINE;
|
||||
import static org.briarproject.sharing.SharerSessionState.Action.REMOTE_LEAVE;
|
||||
|
||||
// This class is not thread-safe
|
||||
public class SharerSessionState extends ForumSharingSessionState {
|
||||
public abstract class SharerSessionState extends SharingSessionState {
|
||||
|
||||
private State state;
|
||||
private String msg = null;
|
||||
|
||||
public SharerSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, State state, ContactId contactId, GroupId forumId,
|
||||
String forumName, byte[] forumSalt) {
|
||||
GroupId groupId, State state, ContactId contactId,
|
||||
GroupId shareableId) {
|
||||
|
||||
super(sessionId, storageId, groupId, contactId, forumId, forumName,
|
||||
forumSalt);
|
||||
super(sessionId, storageId, groupId, contactId, shareableId);
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public interface SharerSessionStateFactory<S extends Shareable, SS extends SharerSessionState> {
|
||||
|
||||
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId,
|
||||
GroupId shareableId, BdfDictionary d) throws FormatException;
|
||||
|
||||
SS build(SessionId sessionId, MessageId storageId, GroupId groupId,
|
||||
SharerSessionState.State state, ContactId contactId, S shareable);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.briarproject.forum;
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.FormatException;
|
||||
@@ -22,12 +22,12 @@ import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumFactory;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.event.InvitationReceivedEvent;
|
||||
import org.briarproject.api.event.InvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sharing.Shareable;
|
||||
import org.briarproject.api.sharing.SharingManager;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
@@ -50,83 +50,90 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static org.briarproject.api.clients.ProtocolEngine.StateUpdate;
|
||||
import static org.briarproject.api.forum.ForumConstants.CONTACT_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.IS_SHARER;
|
||||
import static org.briarproject.api.forum.ForumConstants.LOCAL;
|
||||
import static org.briarproject.api.forum.ForumConstants.READ;
|
||||
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.STATE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_ADD_SHARED_FORUM;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_SHARE_FORUM;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK_UNSHARE_FORUM_SHARED_WITH_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TIME;
|
||||
import static org.briarproject.api.forum.ForumConstants.TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.forum.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.forum.ForumManager.RemoveForumHook;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.forum.ForumSharingMessage.Invitation;
|
||||
import static org.briarproject.forum.ForumSharingSessionState.fromBdfDictionary;
|
||||
import static org.briarproject.forum.SharerSessionState.Action;
|
||||
import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.LOCAL;
|
||||
import static org.briarproject.api.sharing.SharingConstants.READ;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARING_SALT_LENGTH;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_ADD_SHARED_SHAREABLE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_SHARE_SHAREABLE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TASK_UNSHARE_SHAREABLE_SHARED_WITH_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TIME;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TO_BE_SHARED_BY_US;
|
||||
import static org.briarproject.api.sharing.SharingConstants.TYPE;
|
||||
import static org.briarproject.api.sharing.SharingMessage.BaseMessage;
|
||||
import static org.briarproject.api.sharing.SharingMessage.Invitation;
|
||||
|
||||
class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
implements ForumSharingManager, Client, RemoveForumHook,
|
||||
AddContactHook, RemoveContactHook {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"cd11a5d04dccd9e2931d6fc3df456313"
|
||||
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
|
||||
abstract class SharingManagerImpl<S extends Shareable, I extends Invitation, IM extends InvitationMessage, IS extends InviteeSessionState, SS extends SharerSessionState, IR extends InvitationReceivedEvent, IRR extends InvitationResponseReceivedEvent>
|
||||
extends BdfIncomingMessageHook
|
||||
implements SharingManager<S, IM>, Client, AddContactHook,
|
||||
RemoveContactHook {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingManagerImpl.class.getName());
|
||||
Logger.getLogger(SharingManagerImpl.class.getName());
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ForumManager forumManager;
|
||||
private final MessageQueueManager messageQueueManager;
|
||||
private final MetadataEncoder metadataEncoder;
|
||||
private final SecureRandom random;
|
||||
private final PrivateGroupFactory privateGroupFactory;
|
||||
private final ForumFactory forumFactory;
|
||||
private final Clock clock;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(DatabaseComponent db, ForumManager forumManager,
|
||||
SharingManagerImpl(DatabaseComponent db,
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataParser metadataParser, MetadataEncoder metadataEncoder,
|
||||
SecureRandom random, PrivateGroupFactory privateGroupFactory,
|
||||
ForumFactory forumFactory, Clock clock) {
|
||||
Clock clock) {
|
||||
|
||||
super(clientHelper, metadataParser);
|
||||
this.db = db;
|
||||
this.forumManager = forumManager;
|
||||
this.messageQueueManager = messageQueueManager;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.random = random;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.forumFactory = forumFactory;
|
||||
this.clock = clock;
|
||||
localGroup = privateGroupFactory.createLocalGroup(getClientId());
|
||||
}
|
||||
|
||||
public abstract ClientId getClientId();
|
||||
|
||||
protected abstract ClientId getShareableClientId();
|
||||
|
||||
protected abstract IM createInvitationMessage(MessageId id, I msg,
|
||||
ContactId contactId, boolean available, long time, boolean local,
|
||||
boolean sent, boolean seen, boolean read);
|
||||
|
||||
protected abstract ShareableFactory<S, I, IS, SS> getSFactory();
|
||||
|
||||
protected abstract InvitationFactory<I, SS> getIFactory();
|
||||
|
||||
protected abstract InviteeSessionStateFactory<S, IS> getISFactory();
|
||||
|
||||
protected abstract SharerSessionStateFactory<S, SS> getSSFactory();
|
||||
|
||||
protected abstract InvitationReceivedEventFactory<IS, IR> getIRFactory();
|
||||
|
||||
protected abstract InvitationResponseReceivedEventFactory<SS, IRR> getIRRFactory();
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
@@ -183,7 +190,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary d) throws DbException, FormatException {
|
||||
|
||||
BaseMessage msg = BaseMessage.from(m.getGroupId(), d);
|
||||
BaseMessage msg = BaseMessage.from(getIFactory(), m.getGroupId(), d);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
|
||||
if (msg.getType() == SHARE_MSG_TYPE_INVITATION) {
|
||||
@@ -200,19 +207,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
// check if we already have a state with that sessionId
|
||||
if (stateExists) throw new FormatException();
|
||||
|
||||
// check if forum can be shared
|
||||
Invitation invitation = (Invitation) msg;
|
||||
Forum f = forumFactory.createForum(invitation.getForumName(),
|
||||
invitation.getForumSalt());
|
||||
// check if shareable can be shared
|
||||
I invitation = (I) msg;
|
||||
S f = getSFactory().parse(invitation);
|
||||
ContactId contactId = getContactId(txn, m.getGroupId());
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
if (!canBeShared(txn, f.getId(), contact))
|
||||
checkForRaceCondition(txn, f, contact);
|
||||
|
||||
// initialize state and process invitation
|
||||
InviteeSessionState state =
|
||||
initializeInviteeState(txn, contactId, invitation);
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
IS state = initializeInviteeState(txn, contactId, invitation);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} catch (FormatException e) {
|
||||
@@ -222,48 +228,47 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
} else if (msg.getType() == SHARE_MSG_TYPE_ACCEPT ||
|
||||
msg.getType() == SHARE_MSG_TYPE_DECLINE) {
|
||||
// we are a sharer who just received a response
|
||||
SharerSessionState state = getSessionStateForSharer(txn, sessionId);
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SS state = getSessionStateForSharer(txn, sessionId);
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else if (msg.getType() == SHARE_MSG_TYPE_LEAVE ||
|
||||
msg.getType() == SHARE_MSG_TYPE_ABORT) {
|
||||
// we don't know who we are, so figure it out
|
||||
ForumSharingSessionState s = getSessionState(txn, sessionId, true);
|
||||
SharingSessionState s = getSessionState(txn, sessionId, true);
|
||||
if (s instanceof SharerSessionState) {
|
||||
// we are a sharer and the invitee wants to leave or abort
|
||||
SharerSessionState state = (SharerSessionState) s;
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SS state = (SS) s;
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else {
|
||||
// we are an invitee and the sharer wants to leave or abort
|
||||
InviteeSessionState state = (InviteeSessionState) s;
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
IS state = (IS) s;
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
}
|
||||
} else {
|
||||
// message has passed validator, so that should never happen
|
||||
throw new RuntimeException("Illegal Forum Sharing Message");
|
||||
throw new RuntimeException("Illegal Sharing Message");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForumInvitation(GroupId groupId, ContactId contactId,
|
||||
public void sendInvitation(GroupId groupId, ContactId contactId,
|
||||
String msg) throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// initialize local state for sharer
|
||||
Forum f = forumManager.getForum(txn, groupId);
|
||||
SharerSessionState localState =
|
||||
initializeSharerState(txn, f, contactId);
|
||||
S f = getSFactory().get(txn, groupId);
|
||||
SS localState = initializeSharerState(txn, f, contactId);
|
||||
|
||||
// add invitation message to local state to be available for engine
|
||||
if (!StringUtils.isNullOrEmpty(msg)) {
|
||||
@@ -271,9 +276,12 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, null,
|
||||
engine.onLocalAction(localState, Action.LOCAL_INVITATION));
|
||||
engine.onLocalAction(localState,
|
||||
SharerSessionState.Action.LOCAL_INVITATION));
|
||||
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
@@ -284,14 +292,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(Forum f, Contact c, boolean accept)
|
||||
public void respondToInvitation(S f, Contact c, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// find session state based on forum
|
||||
InviteeSessionState localState =
|
||||
getSessionStateForResponse(txn, f, c);
|
||||
// find session state based on shareable
|
||||
IS localState = getSessionStateForResponse(txn, f, c);
|
||||
|
||||
// define action
|
||||
InviteeSessionState.Action localAction;
|
||||
@@ -302,7 +309,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, null,
|
||||
engine.onLocalAction(localState, localAction));
|
||||
|
||||
@@ -315,8 +323,8 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ForumInvitationMessage> getForumInvitationMessages(
|
||||
ContactId contactId) throws DbException {
|
||||
public Collection<IM> getInvitationMessages(ContactId contactId)
|
||||
throws DbException {
|
||||
|
||||
// query for all invitations
|
||||
BdfDictionary query = BdfDictionary.of(
|
||||
@@ -328,14 +336,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(contact);
|
||||
|
||||
Collection<ForumInvitationMessage> list =
|
||||
new ArrayList<ForumInvitationMessage>();
|
||||
Collection<IM> list = new ArrayList<IM>();
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, group.getId(), query);
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
try {
|
||||
Invitation msg = Invitation.from(group.getId(), d);
|
||||
I msg = getIFactory().build(group.getId(), d);
|
||||
MessageStatus status =
|
||||
db.getMessageStatus(txn, contactId, m.getKey());
|
||||
long time = d.getLong(TIME);
|
||||
@@ -343,20 +350,17 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
boolean read = d.getBoolean(READ, false);
|
||||
boolean available = false;
|
||||
if (!local) {
|
||||
// figure out whether the forum is still available
|
||||
ForumSharingSessionState s =
|
||||
// figure out whether the shareable is still available
|
||||
SharingSessionState s =
|
||||
getSessionState(txn, msg.getSessionId(), true);
|
||||
if (!(s instanceof InviteeSessionState))
|
||||
continue;
|
||||
available = ((InviteeSessionState) s).getState() ==
|
||||
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE;
|
||||
}
|
||||
ForumInvitationMessage im =
|
||||
new ForumInvitationMessage(m.getKey(),
|
||||
msg.getSessionId(), contactId,
|
||||
msg.getForumName(), msg.getMessage(),
|
||||
available, time, local, status.isSent(),
|
||||
status.isSeen(), read);
|
||||
IM im = createInvitationMessage(m.getKey(), msg, contactId,
|
||||
available, time, local, status.isSent(),
|
||||
status.isSeen(), read);
|
||||
list.add(im);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
@@ -373,40 +377,20 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingForum(Transaction txn, Forum f) throws DbException {
|
||||
public Collection<S> getAvailable() throws DbException {
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
GroupId g = getContactGroup(c).getId();
|
||||
if (removeFromList(txn, g, TO_BE_SHARED_BY_US, f)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_BY_US, f)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_WITH_US, f)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Forum> getAvailableForums() throws DbException {
|
||||
try {
|
||||
Set<Forum> available = new HashSet<Forum>();
|
||||
Set<S> available = new HashSet<S>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
// Get any forums we subscribe to
|
||||
// Get any shareables we subscribe to
|
||||
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
|
||||
forumManager.getClientId()));
|
||||
// Get all forums shared by contacts
|
||||
getShareableClientId()));
|
||||
// Get all shareables shared by contacts
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
Group g = getContactGroup(c);
|
||||
List<Forum> forums =
|
||||
getForumList(txn, g.getId(), SHARED_WITH_US);
|
||||
for (Forum f : forums) {
|
||||
List<S> shareables =
|
||||
getShareableList(txn, g.getId(), SHARED_WITH_US);
|
||||
for (S f : shareables) {
|
||||
if (!subscribed.contains(f.getGroup()))
|
||||
available.add(f);
|
||||
}
|
||||
@@ -489,7 +473,26 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForRaceCondition(Transaction txn, Forum f, Contact c)
|
||||
protected void removingShareable(Transaction txn, S f) throws DbException {
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
GroupId g = getContactGroup(c).getId();
|
||||
if (removeFromList(txn, g, TO_BE_SHARED_BY_US, f)) {
|
||||
leaveShareable(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_BY_US, f)) {
|
||||
leaveShareable(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_WITH_US, f)) {
|
||||
leaveShareable(txn, c.getId(), f);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForRaceCondition(Transaction txn, S f, Contact c)
|
||||
throws FormatException, DbException {
|
||||
|
||||
GroupId contactGroup = getContactGroup(c).getId();
|
||||
@@ -507,12 +510,14 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (alice) {
|
||||
// our own invitation takes precedence, so just delete Bob's
|
||||
LOG.info("Invitation race-condition: We are Alice deleting Bob's invitation.");
|
||||
LOG.info(
|
||||
"Invitation race-condition: We are Alice deleting Bob's invitation.");
|
||||
throw new FormatException();
|
||||
} else {
|
||||
// we are Bob, so we need to "take back" our own invitation
|
||||
LOG.info("Invitation race-condition: We are Bob taking back our invitation.");
|
||||
ForumSharingSessionState state =
|
||||
LOG.info(
|
||||
"Invitation race-condition: We are Bob taking back our invitation.");
|
||||
SharingSessionState state =
|
||||
getSessionStateForLeaving(txn, f, c.getId());
|
||||
if (state instanceof SharerSessionState) {
|
||||
//SharerEngine engine = new SharerEngine();
|
||||
@@ -529,7 +534,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
}
|
||||
|
||||
private SharerSessionState initializeSharerState(Transaction txn, Forum f,
|
||||
private SS initializeSharerState(Transaction txn, S f,
|
||||
ContactId contactId) throws FormatException, DbException {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
@@ -537,15 +542,15 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
Bytes salt = new Bytes(new byte[FORUM_SALT_LENGTH]);
|
||||
Bytes salt = new Bytes(new byte[SHARING_SALT_LENGTH]);
|
||||
random.nextBytes(salt.getBytes());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(salt));
|
||||
SessionId sessionId = new SessionId(m.getId().getBytes());
|
||||
|
||||
SharerSessionState s = new SharerSessionState(sessionId, sessionId,
|
||||
SS s = getSSFactory().build(sessionId, sessionId,
|
||||
group.getId(), SharerSessionState.State.PREPARE_INVITATION,
|
||||
contactId, f.getId(), f.getName(), f.getSalt());
|
||||
contactId, f);
|
||||
|
||||
// save local state to database
|
||||
BdfDictionary d = s.toBdfDictionary();
|
||||
@@ -554,27 +559,24 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
return s;
|
||||
}
|
||||
|
||||
private InviteeSessionState initializeInviteeState(Transaction txn,
|
||||
ContactId contactId, Invitation msg)
|
||||
private IS initializeInviteeState(Transaction txn,
|
||||
ContactId contactId, I msg)
|
||||
throws FormatException, DbException {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(c);
|
||||
String name = msg.getForumName();
|
||||
byte[] salt = msg.getForumSalt();
|
||||
Forum f = forumFactory.createForum(name, salt);
|
||||
S f = getSFactory().parse(msg);
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
Bytes mSalt = new Bytes(new byte[FORUM_SALT_LENGTH]);
|
||||
Bytes mSalt = new Bytes(new byte[SHARING_SALT_LENGTH]);
|
||||
random.nextBytes(mSalt.getBytes());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(mSalt));
|
||||
|
||||
InviteeSessionState s = new InviteeSessionState(msg.getSessionId(),
|
||||
IS s = getISFactory().build(msg.getSessionId(),
|
||||
m.getId(), group.getId(),
|
||||
InviteeSessionState.State.AWAIT_INVITATION, contactId,
|
||||
f.getId(), f.getName(), f.getSalt());
|
||||
InviteeSessionState.State.AWAIT_INVITATION, contactId, f);
|
||||
|
||||
// save local state to database
|
||||
BdfDictionary d = s.toBdfDictionary();
|
||||
@@ -583,7 +585,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
return s;
|
||||
}
|
||||
|
||||
private ForumSharingSessionState getSessionState(Transaction txn,
|
||||
private SharingSessionState getSessionState(Transaction txn,
|
||||
SessionId sessionId, boolean warn)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -612,11 +614,13 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
return fromBdfDictionary(map.values().iterator().next());
|
||||
return SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(),
|
||||
map.values().iterator().next());
|
||||
}
|
||||
}
|
||||
|
||||
private SharerSessionState getSessionStateForSharer(Transaction txn,
|
||||
private SS getSessionStateForSharer(Transaction txn,
|
||||
SessionId sessionId)
|
||||
throws DbException, FormatException {
|
||||
|
||||
@@ -626,17 +630,18 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (!d.getBoolean(IS_SHARER)) throw new FormatException();
|
||||
|
||||
return (SharerSessionState) fromBdfDictionary(d);
|
||||
return (SS) SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(), d);
|
||||
}
|
||||
|
||||
private InviteeSessionState getSessionStateForResponse(Transaction txn,
|
||||
Forum f, Contact c) throws DbException, FormatException {
|
||||
private IS getSessionStateForResponse(Transaction txn,
|
||||
S f, Contact c) throws DbException, FormatException {
|
||||
|
||||
// query for invitee states for that forum in state await response
|
||||
// query for invitee states for that shareable in state await response
|
||||
BdfDictionary query = BdfDictionary.of(
|
||||
new BdfEntry(IS_SHARER, false),
|
||||
new BdfEntry(CONTACT_ID, c.getId().getInt()),
|
||||
new BdfEntry(FORUM_ID, f.getId()),
|
||||
new BdfEntry(SHAREABLE_ID, f.getId()),
|
||||
new BdfEntry(STATE,
|
||||
InviteeSessionState.State.AWAIT_LOCAL_RESPONSE
|
||||
.getValue())
|
||||
@@ -647,7 +652,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
|
||||
if (map.size() > 1 && LOG.isLoggable(WARNING)) {
|
||||
LOG.warning(
|
||||
"More than one session state found for forum with ID " +
|
||||
"More than one session state found for shareable with ID " +
|
||||
Arrays.hashCode(f.getId().getBytes()) +
|
||||
" in state AWAIT_LOCAL_RESPONSE for contact " +
|
||||
c.getAuthor().getName());
|
||||
@@ -655,35 +660,38 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
if (map.isEmpty()) {
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning(
|
||||
"No session state found for forum with ID " +
|
||||
"No session state found for shareable with ID " +
|
||||
Arrays.hashCode(f.getId().getBytes()) +
|
||||
" in state AWAIT_LOCAL_RESPONSE");
|
||||
}
|
||||
throw new DbException();
|
||||
}
|
||||
return (InviteeSessionState) fromBdfDictionary(
|
||||
map.values().iterator().next());
|
||||
return (IS) SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(),
|
||||
map.values().iterator().next());
|
||||
}
|
||||
|
||||
private ForumSharingSessionState getSessionStateForLeaving(Transaction txn,
|
||||
Forum f, ContactId c) throws DbException, FormatException {
|
||||
private SharingSessionState getSessionStateForLeaving(Transaction txn,
|
||||
S f, ContactId c) throws DbException, FormatException {
|
||||
|
||||
BdfDictionary query = BdfDictionary.of(
|
||||
new BdfEntry(CONTACT_ID, c.getInt()),
|
||||
new BdfEntry(FORUM_ID, f.getId())
|
||||
new BdfEntry(SHAREABLE_ID, f.getId())
|
||||
);
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId(), query);
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
try {
|
||||
ForumSharingSessionState s = fromBdfDictionary(d);
|
||||
SharingSessionState s = SharingSessionState
|
||||
.fromBdfDictionary(getISFactory(), getSSFactory(), d);
|
||||
|
||||
// check that a forum get be left in current session
|
||||
// check that a shareable get be left in current session
|
||||
if (s instanceof SharerSessionState) {
|
||||
SharerSessionState state = (SharerSessionState) s;
|
||||
SharerSessionState.State nextState =
|
||||
state.getState().next(Action.LOCAL_LEAVE);
|
||||
state.getState()
|
||||
.next(SharerSessionState.Action.LOCAL_LEAVE);
|
||||
if (nextState != SharerSessionState.State.ERROR) {
|
||||
return state;
|
||||
}
|
||||
@@ -703,11 +711,11 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private void processStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> result)
|
||||
StateUpdate<SharingSessionState, BaseMessage> result, S f)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// perform actions based on new local state
|
||||
performTasks(txn, result.localState);
|
||||
performTasks(txn, result.localState, f);
|
||||
|
||||
// save new local state
|
||||
MessageId storageId = result.localState.getStorageId();
|
||||
@@ -735,31 +743,37 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private void processSharerStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<SharerSessionState, BaseMessage> result)
|
||||
StateUpdate<SS, BaseMessage> result)
|
||||
throws DbException, FormatException {
|
||||
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<ForumSharingSessionState, BaseMessage>(
|
||||
StateUpdate<SharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<SharingSessionState, BaseMessage>(
|
||||
result.deleteMessage, result.deleteState,
|
||||
result.localState, result.toSend, result.toBroadcast);
|
||||
|
||||
processStateUpdate(txn, messageId, r);
|
||||
// get shareable for later
|
||||
S f = getSFactory().parse(result.localState);
|
||||
|
||||
processStateUpdate(txn, messageId, r, f);
|
||||
}
|
||||
|
||||
private void processInviteeStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<InviteeSessionState, BaseMessage> result)
|
||||
StateUpdate<IS, BaseMessage> result)
|
||||
throws DbException, FormatException {
|
||||
|
||||
StateUpdate<ForumSharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<ForumSharingSessionState, BaseMessage>(
|
||||
StateUpdate<SharingSessionState, BaseMessage> r =
|
||||
new StateUpdate<SharingSessionState, BaseMessage>(
|
||||
result.deleteMessage, result.deleteState,
|
||||
result.localState, result.toSend, result.toBroadcast);
|
||||
|
||||
processStateUpdate(txn, messageId, r);
|
||||
// get shareable for later
|
||||
S f = getSFactory().parse(result.localState);
|
||||
|
||||
processStateUpdate(txn, messageId, r, f);
|
||||
}
|
||||
|
||||
private void performTasks(Transaction txn, ForumSharingSessionState localState)
|
||||
throws FormatException, DbException {
|
||||
private void performTasks(Transaction txn, SharingSessionState localState,
|
||||
S f) throws FormatException, DbException {
|
||||
|
||||
if (localState.getTask() == -1) return;
|
||||
|
||||
@@ -772,37 +786,26 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
// get contact ID for later
|
||||
ContactId contactId = localState.getContactId();
|
||||
|
||||
// get forum for later
|
||||
String name = localState.getForumName();
|
||||
byte[] salt = localState.getForumSalt();
|
||||
Forum f = forumFactory.createForum(name, salt);
|
||||
|
||||
// perform tasks
|
||||
if (task == TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US) {
|
||||
if (task == TASK_ADD_SHAREABLE_TO_LIST_SHARED_WITH_US) {
|
||||
addToList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US) {
|
||||
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_SHARED_WITH_US) {
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
else if (task == TASK_ADD_SHARED_FORUM) {
|
||||
} else if (task == TASK_ADD_SHARED_SHAREABLE) {
|
||||
db.addGroup(txn, f.getGroup());
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
||||
}
|
||||
else if (task == TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US) {
|
||||
} else if (task == TASK_ADD_SHAREABLE_TO_LIST_TO_BE_SHARED_BY_US) {
|
||||
addToList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US) {
|
||||
} else if (task == TASK_REMOVE_SHAREABLE_FROM_LIST_TO_BE_SHARED_BY_US) {
|
||||
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_SHARE_FORUM) {
|
||||
} else if (task == TASK_SHARE_SHAREABLE) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), true);
|
||||
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
addToList(txn, groupId, SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_UNSHARE_FORUM_SHARED_BY_US) {
|
||||
} else if (task == TASK_UNSHARE_SHAREABLE_SHARED_BY_US) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), false);
|
||||
removeFromList(txn, groupId, SHARED_BY_US, f);
|
||||
} else if (task == TASK_UNSHARE_FORUM_SHARED_WITH_US) {
|
||||
} else if (task == TASK_UNSHARE_SHAREABLE_SHARED_WITH_US) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), false);
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
@@ -826,7 +829,7 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
}
|
||||
|
||||
private Group getContactGroup(Contact c) {
|
||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
||||
return privateGroupFactory.createPrivateGroup(getClientId(), c);
|
||||
}
|
||||
|
||||
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
@@ -836,90 +839,94 @@ class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
return new ContactId(meta.getLong(CONTACT_ID).intValue());
|
||||
}
|
||||
|
||||
private void leaveForum(Transaction txn, ContactId c, Forum f)
|
||||
private void leaveShareable(Transaction txn, ContactId c, S f)
|
||||
throws DbException, FormatException {
|
||||
|
||||
ForumSharingSessionState state = getSessionStateForLeaving(txn, f, c);
|
||||
SharingSessionState state = getSessionStateForLeaving(txn, f, c);
|
||||
if (state instanceof SharerSessionState) {
|
||||
Action action = Action.LOCAL_LEAVE;
|
||||
SharerEngine engine = new SharerEngine();
|
||||
SharerSessionState.Action action =
|
||||
SharerSessionState.Action.LOCAL_LEAVE;
|
||||
SharerEngine<I, SS, IRR> engine =
|
||||
new SharerEngine<I, SS, IRR>(getIFactory(),
|
||||
getIRRFactory());
|
||||
processSharerStateUpdate(txn, null,
|
||||
engine.onLocalAction((SharerSessionState) state, action));
|
||||
engine.onLocalAction((SS) state, action));
|
||||
} else {
|
||||
InviteeSessionState.Action action =
|
||||
InviteeSessionState.Action.LOCAL_LEAVE;
|
||||
InviteeEngine engine = new InviteeEngine(forumFactory);
|
||||
InviteeEngine<IS, IR> engine =
|
||||
new InviteeEngine<IS, IR>(getIRFactory());
|
||||
processInviteeStateUpdate(txn, null,
|
||||
engine.onLocalAction((InviteeSessionState) state, action));
|
||||
engine.onLocalAction((IS) state, action));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean listContains(Transaction txn, GroupId contactGroup,
|
||||
GroupId forum, String key) throws DbException, FormatException {
|
||||
GroupId shareable, String key) throws DbException, FormatException {
|
||||
|
||||
List<Forum> list = getForumList(txn, contactGroup, key);
|
||||
for (Forum f : list) {
|
||||
if (f.getId().equals(forum)) return true;
|
||||
List<S> list = getShareableList(txn, contactGroup, key);
|
||||
for (S f : list) {
|
||||
if (f.getId().equals(shareable)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean addToList(Transaction txn, GroupId groupId, String key,
|
||||
Forum f) throws DbException, FormatException {
|
||||
S f) throws DbException, FormatException {
|
||||
|
||||
List<Forum> forums = getForumList(txn, groupId, key);
|
||||
if (forums.contains(f)) return false;
|
||||
forums.add(f);
|
||||
storeForumList(txn, groupId, key, forums);
|
||||
List<S> shareables = getShareableList(txn, groupId, key);
|
||||
if (shareables.contains(f)) return false;
|
||||
shareables.add(f);
|
||||
storeShareableList(txn, groupId, key, shareables);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeFromList(Transaction txn, GroupId groupId, String key,
|
||||
Forum f) throws DbException, FormatException {
|
||||
S f) throws DbException, FormatException {
|
||||
|
||||
List<Forum> forums = getForumList(txn, groupId, key);
|
||||
if (forums.remove(f)) {
|
||||
storeForumList(txn, groupId, key, forums);
|
||||
List<S> shareables = getShareableList(txn, groupId, key);
|
||||
if (shareables.remove(f)) {
|
||||
storeShareableList(txn, groupId, key, shareables);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Forum> getForumList(Transaction txn, GroupId groupId,
|
||||
private List<S> getShareableList(Transaction txn, GroupId groupId,
|
||||
String key) throws DbException, FormatException {
|
||||
|
||||
BdfDictionary metadata =
|
||||
clientHelper.getGroupMetadataAsDictionary(txn, groupId);
|
||||
BdfList list = metadata.getList(key);
|
||||
|
||||
return parseForumList(list);
|
||||
return parseShareableList(list);
|
||||
}
|
||||
|
||||
private void storeForumList(Transaction txn, GroupId groupId, String key,
|
||||
List<Forum> forums) throws DbException, FormatException {
|
||||
private void storeShareableList(Transaction txn, GroupId groupId,
|
||||
String key,
|
||||
List<S> shareables) throws DbException, FormatException {
|
||||
|
||||
BdfList list = encodeForumList(forums);
|
||||
BdfList list = encodeShareableList(shareables);
|
||||
BdfDictionary metadata = BdfDictionary.of(
|
||||
new BdfEntry(key, list)
|
||||
);
|
||||
clientHelper.mergeGroupMetadata(txn, groupId, metadata);
|
||||
}
|
||||
|
||||
private BdfList encodeForumList(List<Forum> forums) {
|
||||
BdfList forumList = new BdfList();
|
||||
for (Forum f : forums)
|
||||
forumList.add(BdfList.of(f.getName(), f.getSalt()));
|
||||
return forumList;
|
||||
private BdfList encodeShareableList(List<S> shareables) {
|
||||
BdfList shareableList = new BdfList();
|
||||
for (S f : shareables)
|
||||
shareableList.add(getSFactory().encode(f));
|
||||
return shareableList;
|
||||
}
|
||||
|
||||
private List<Forum> parseForumList(BdfList list) throws FormatException {
|
||||
List<Forum> forums = new ArrayList<Forum>(list.size());
|
||||
private List<S> parseShareableList(BdfList list) throws FormatException {
|
||||
List<S> shareables = new ArrayList<S>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BdfList forum = list.getList(i);
|
||||
forums.add(forumFactory
|
||||
.createForum(forum.getString(0), forum.getRaw(1)));
|
||||
BdfList shareable = list.getList(i);
|
||||
shareables.add(getSFactory().parse(shareable));
|
||||
}
|
||||
return forums;
|
||||
return shareables;
|
||||
}
|
||||
|
||||
private void deleteMessage(Transaction txn, MessageId messageId)
|
||||
62
briar-core/src/org/briarproject/sharing/SharingModule.java
Normal file
62
briar-core/src/org/briarproject/sharing/SharingModule.java
Normal file
@@ -0,0 +1,62 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class SharingModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
ForumSharingValidator forumSharingValidator;
|
||||
@Inject
|
||||
ForumSharingManager forumSharingManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingValidator provideForumSharingValidator(
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
|
||||
ForumSharingValidator
|
||||
validator = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
messageQueueManager.registerMessageValidator(
|
||||
ForumSharingManagerImpl.CLIENT_ID, validator);
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingManager provideForumSharingManager(
|
||||
LifecycleManager lifecycleManager,
|
||||
ContactManager contactManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
ForumManager forumManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
|
||||
lifecycleManager.registerClient(forumSharingManager);
|
||||
contactManager.registerAddContactHook(forumSharingManager);
|
||||
contactManager.registerRemoveContactHook(forumSharingManager);
|
||||
messageQueueManager.registerIncomingMessageHook(
|
||||
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
|
||||
forumManager.registerRemoveForumHook(forumSharingManager);
|
||||
|
||||
return forumSharingManager;
|
||||
}
|
||||
|
||||
}
|
||||
102
briar-core/src/org/briarproject/sharing/SharingSessionState.java
Normal file
102
briar-core/src/org/briarproject/sharing/SharingSessionState.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package org.briarproject.sharing;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
import static org.briarproject.api.sharing.SharingConstants.CONTACT_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.GROUP_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.IS_SHARER;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SESSION_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHAREABLE_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STATE;
|
||||
import static org.briarproject.api.sharing.SharingConstants.STORAGE_ID;
|
||||
|
||||
// This class is not thread-safe
|
||||
public abstract class SharingSessionState {
|
||||
|
||||
private final SessionId sessionId;
|
||||
private final MessageId storageId;
|
||||
private final GroupId groupId;
|
||||
private final ContactId contactId;
|
||||
private final GroupId shareableId;
|
||||
private int task = -1; // TODO get rid of task, see #376
|
||||
|
||||
public SharingSessionState(SessionId sessionId, MessageId storageId,
|
||||
GroupId groupId, ContactId contactId, GroupId shareableId) {
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.storageId = storageId;
|
||||
this.groupId = groupId;
|
||||
this.contactId = contactId;
|
||||
this.shareableId = shareableId;
|
||||
}
|
||||
|
||||
public static SharingSessionState fromBdfDictionary(
|
||||
InviteeSessionStateFactory isFactory,
|
||||
SharerSessionStateFactory ssFactory, BdfDictionary d)
|
||||
throws FormatException {
|
||||
|
||||
SessionId sessionId = new SessionId(d.getRaw(SESSION_ID));
|
||||
MessageId messageId = new MessageId(d.getRaw(STORAGE_ID));
|
||||
GroupId groupId = new GroupId(d.getRaw(GROUP_ID));
|
||||
ContactId contactId = new ContactId(d.getLong(CONTACT_ID).intValue());
|
||||
GroupId forumId = new GroupId(d.getRaw(SHAREABLE_ID));
|
||||
|
||||
int intState = d.getLong(STATE).intValue();
|
||||
if (d.getBoolean(IS_SHARER)) {
|
||||
SharerSessionState.State state =
|
||||
SharerSessionState.State.fromValue(intState);
|
||||
return ssFactory.build(sessionId, messageId, groupId, state,
|
||||
contactId, forumId, d);
|
||||
} else {
|
||||
InviteeSessionState.State state =
|
||||
InviteeSessionState.State.fromValue(intState);
|
||||
return isFactory.build(sessionId, messageId, groupId, state,
|
||||
contactId, forumId, d);
|
||||
}
|
||||
}
|
||||
|
||||
public BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, getSessionId());
|
||||
d.put(STORAGE_ID, getStorageId());
|
||||
d.put(GROUP_ID, getGroupId());
|
||||
d.put(CONTACT_ID, getContactId().getInt());
|
||||
d.put(SHAREABLE_ID, getShareableId());
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
public SessionId getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public MessageId getStorageId() {
|
||||
return storageId;
|
||||
}
|
||||
|
||||
public GroupId getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
public ContactId getContactId() {
|
||||
return contactId;
|
||||
}
|
||||
|
||||
public GroupId getShareableId() {
|
||||
return shareableId;
|
||||
}
|
||||
|
||||
public void setTask(int task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public int getTask() {
|
||||
return task;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user