mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 04:39:54 +01:00
Removed forums for beta release.
This commit is contained in:
@@ -3,7 +3,6 @@ package org.briarproject;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.db.DatabaseExecutorModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.introduction.IntroductionModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
@@ -21,8 +20,6 @@ public interface CoreEagerSingletons {
|
||||
|
||||
void inject(DatabaseExecutorModule.EagerSingletons init);
|
||||
|
||||
void inject(ForumModule.EagerSingletons init);
|
||||
|
||||
void inject(IntroductionModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseExecutorModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.introduction.IntroductionModule;
|
||||
import org.briarproject.invitation.InvitationModule;
|
||||
@@ -33,7 +32,6 @@ import dagger.Module;
|
||||
DatabaseModule.class,
|
||||
DatabaseExecutorModule.class,
|
||||
EventModule.class,
|
||||
ForumModule.class,
|
||||
IdentityModule.class,
|
||||
IntroductionModule.class,
|
||||
InvitationModule.class,
|
||||
@@ -55,7 +53,6 @@ public class CoreModule {
|
||||
c.inject(new ContactModule.EagerSingletons());
|
||||
c.inject(new CryptoModule.EagerSingletons());
|
||||
c.inject(new DatabaseExecutorModule.EagerSingletons());
|
||||
c.inject(new ForumModule.EagerSingletons());
|
||||
c.inject(new LifecycleModule.EagerSingletons());
|
||||
c.inject(new MessagingModule.EagerSingletons());
|
||||
c.inject(new PluginsModule.EagerSingletons());
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostHeader;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_NAME_LENGTH;
|
||||
import static org.briarproject.api.identity.Author.Status.ANONYMOUS;
|
||||
import static org.briarproject.api.identity.Author.Status.UNKNOWN;
|
||||
import static org.briarproject.api.identity.Author.Status.VERIFIED;
|
||||
|
||||
class ForumManagerImpl implements ForumManager {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"859a7be50dca035b64bd6902fb797097"
|
||||
+ "795af837abbf8c16d750b3c2ccc186ea"));
|
||||
|
||||
private final DatabaseComponent db;
|
||||
private final ClientHelper clientHelper;
|
||||
private final GroupFactory groupFactory;
|
||||
private final SecureRandom random;
|
||||
private final List<RemoveForumHook> removeHooks;
|
||||
|
||||
@Inject
|
||||
ForumManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
|
||||
GroupFactory groupFactory, SecureRandom random) {
|
||||
|
||||
this.db = db;
|
||||
this.clientHelper = clientHelper;
|
||||
this.groupFactory = groupFactory;
|
||||
this.random = random;
|
||||
removeHooks = new CopyOnWriteArrayList<RemoveForumHook>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum createForum(String name) {
|
||||
int length = StringUtils.toUtf8(name).length;
|
||||
if (length == 0) throw new IllegalArgumentException();
|
||||
if (length > MAX_FORUM_NAME_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
byte[] salt = new byte[FORUM_SALT_LENGTH];
|
||||
random.nextBytes(salt);
|
||||
return createForum(name, salt);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum createForum(String name, byte[] salt) {
|
||||
try {
|
||||
BdfList forum = BdfList.of(name, salt);
|
||||
byte[] descriptor = clientHelper.toByteArray(forum);
|
||||
Group g = groupFactory.createGroup(getClientId(), descriptor);
|
||||
return new Forum(g, name, salt);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addForum(Forum f) throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
db.addGroup(txn, f.getGroup());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeForum(Forum f) throws DbException {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
for (RemoveForumHook hook : removeHooks)
|
||||
hook.removingForum(txn, f);
|
||||
db.removeGroup(txn, f.getGroup());
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addLocalPost(ForumPost p) throws DbException {
|
||||
try {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put("timestamp", p.getMessage().getTimestamp());
|
||||
if (p.getParent() != null) meta.put("parent", p.getParent());
|
||||
if (p.getAuthor() != null) {
|
||||
Author a = p.getAuthor();
|
||||
BdfDictionary authorMeta = new BdfDictionary();
|
||||
authorMeta.put("id", a.getId());
|
||||
authorMeta.put("name", a.getName());
|
||||
authorMeta.put("publicKey", a.getPublicKey());
|
||||
meta.put("author", authorMeta);
|
||||
}
|
||||
meta.put("contentType", p.getContentType());
|
||||
meta.put("local", true);
|
||||
meta.put("read", true);
|
||||
clientHelper.addLocalMessage(p.getMessage(), CLIENT_ID, meta, true);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum getForum(GroupId g) throws DbException {
|
||||
Forum forum;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
forum = getForum(txn, g);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return forum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Forum getForum(Transaction txn, GroupId g) throws DbException {
|
||||
try {
|
||||
Group group = db.getGroup(txn, g);
|
||||
return parseForum(group);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Forum> getForums() throws DbException {
|
||||
try {
|
||||
Collection<Group> groups;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
groups = db.getGroups(txn, CLIENT_ID);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
List<Forum> forums = new ArrayList<Forum>();
|
||||
for (Group g : groups) forums.add(parseForum(g));
|
||||
return Collections.unmodifiableList(forums);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getPostBody(MessageId m) throws DbException {
|
||||
try {
|
||||
// Parent ID, author, content type, forum post body, signature
|
||||
BdfList message = clientHelper.getMessageAsList(m);
|
||||
return message.getRaw(3);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ForumPostHeader> getPostHeaders(GroupId g)
|
||||
throws DbException {
|
||||
Set<AuthorId> localAuthorIds = new HashSet<AuthorId>();
|
||||
Set<AuthorId> contactAuthorIds = new HashSet<AuthorId>();
|
||||
Map<MessageId, BdfDictionary> metadata;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
// Load the IDs of the user's identities
|
||||
for (LocalAuthor a : db.getLocalAuthors(txn))
|
||||
localAuthorIds.add(a.getId());
|
||||
// Load the IDs of contacts' identities
|
||||
for (Contact c : db.getContacts(txn))
|
||||
contactAuthorIds.add(c.getAuthor().getId());
|
||||
// Load the metadata
|
||||
metadata = clientHelper.getMessageMetadataAsDictionary(txn, g);
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
// Parse the metadata
|
||||
Collection<ForumPostHeader> headers = new ArrayList<ForumPostHeader>();
|
||||
for (Entry<MessageId, BdfDictionary> entry : metadata.entrySet()) {
|
||||
try {
|
||||
BdfDictionary meta = entry.getValue();
|
||||
long timestamp = meta.getLong("timestamp");
|
||||
Author author = null;
|
||||
Author.Status authorStatus = ANONYMOUS;
|
||||
BdfDictionary d1 = meta.getDictionary("author", null);
|
||||
if (d1 != null) {
|
||||
AuthorId authorId = new AuthorId(d1.getRaw("id"));
|
||||
String name = d1.getString("name");
|
||||
byte[] publicKey = d1.getRaw("publicKey");
|
||||
author = new Author(authorId, name, publicKey);
|
||||
if (localAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else if (contactAuthorIds.contains(authorId))
|
||||
authorStatus = VERIFIED;
|
||||
else authorStatus = UNKNOWN;
|
||||
}
|
||||
String contentType = meta.getString("contentType");
|
||||
boolean read = meta.getBoolean("read");
|
||||
headers.add(new ForumPostHeader(entry.getKey(), timestamp,
|
||||
author, authorStatus, contentType, read));
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadFlag(MessageId m, boolean read) throws DbException {
|
||||
try {
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put("read", read);
|
||||
clientHelper.mergeMessageMetadata(m, meta);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRemoveForumHook(RemoveForumHook hook) {
|
||||
removeHooks.add(hook);
|
||||
}
|
||||
|
||||
private Forum parseForum(Group g) throws FormatException {
|
||||
byte[] descriptor = g.getDescriptor();
|
||||
// Name, salt
|
||||
BdfList forum = clientHelper.toList(descriptor, 0, descriptor.length);
|
||||
return new Forum(g, forum.getString(0), forum.getRaw(1));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
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.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.ValidationManager;
|
||||
import org.briarproject.api.system.Clock;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class ForumModule {
|
||||
|
||||
public static class EagerSingletons {
|
||||
@Inject
|
||||
ForumPostValidator forumPostValidator;
|
||||
@Inject
|
||||
ForumSharingValidator forumSharingValidator;
|
||||
@Inject
|
||||
ForumSharingManager forumSharingManager;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumManager provideForumManager(DatabaseComponent db,
|
||||
ClientHelper clientHelper,
|
||||
GroupFactory groupFactory, SecureRandom random) {
|
||||
return new ForumManagerImpl(db, clientHelper, groupFactory, random);
|
||||
}
|
||||
|
||||
@Provides
|
||||
ForumPostFactory provideForumPostFactory(CryptoComponent crypto,
|
||||
ClientHelper clientHelper) {
|
||||
return new ForumPostFactoryImpl(crypto, clientHelper);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumPostValidator provideForumPostValidator(
|
||||
ValidationManager validationManager, CryptoComponent crypto,
|
||||
AuthorFactory authorFactory, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
ForumPostValidator validator = new ForumPostValidator(crypto,
|
||||
authorFactory, clientHelper, metadataEncoder, clock);
|
||||
validationManager.registerMessageValidator(
|
||||
ForumManagerImpl.CLIENT_ID, validator);
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingValidator provideSharingValidator(
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
|
||||
ForumSharingValidator validator = new ForumSharingValidator(clientHelper,
|
||||
metadataEncoder, clock);
|
||||
messageQueueManager.registerMessageValidator(
|
||||
ForumSharingManagerImpl.CLIENT_ID, validator);
|
||||
|
||||
return validator;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
ForumSharingManager provideForumSharingManager(
|
||||
LifecycleManager lifecycleManager,
|
||||
ContactManager contactManager,
|
||||
MessageQueueManager messageQueueManager,
|
||||
ForumManager forumManager,
|
||||
ForumSharingManagerImpl forumSharingManager) {
|
||||
|
||||
lifecycleManager.registerClient(forumSharingManager);
|
||||
contactManager.registerAddContactHook(forumSharingManager);
|
||||
contactManager.registerRemoveContactHook(forumSharingManager);
|
||||
messageQueueManager.registerIncomingMessageHook(
|
||||
ForumSharingManagerImpl.CLIENT_ID, forumSharingManager);
|
||||
forumManager.registerRemoveForumHook(forumSharingManager);
|
||||
|
||||
return forumSharingManager;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.PrivateKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||
|
||||
class ForumPostFactoryImpl implements ForumPostFactory {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final ClientHelper clientHelper;
|
||||
|
||||
@Inject
|
||||
ForumPostFactoryImpl(CryptoComponent crypto, ClientHelper clientHelper) {
|
||||
this.crypto = crypto;
|
||||
this.clientHelper = clientHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumPost createAnonymousPost(GroupId groupId, long timestamp,
|
||||
MessageId parent, String contentType, byte[] body)
|
||||
throws FormatException {
|
||||
// Validate the arguments
|
||||
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (body.length > MAX_FORUM_POST_BODY_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
// Serialise the message
|
||||
BdfList message = BdfList.of(parent, null, contentType, body, null);
|
||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||
return new ForumPost(m, parent, null, contentType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForumPost createPseudonymousPost(GroupId groupId, long timestamp,
|
||||
MessageId parent, Author author, String contentType, byte[] body,
|
||||
PrivateKey privateKey) throws FormatException,
|
||||
GeneralSecurityException {
|
||||
// Validate the arguments
|
||||
if (StringUtils.toUtf8(contentType).length > MAX_CONTENT_TYPE_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
if (body.length > MAX_FORUM_POST_BODY_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
// Serialise the data to be signed
|
||||
BdfList authorList = BdfList.of(author.getName(),
|
||||
author.getPublicKey());
|
||||
BdfList signed = BdfList.of(groupId, timestamp, parent, authorList,
|
||||
contentType, body);
|
||||
// Generate the signature
|
||||
Signature signature = crypto.getSignature();
|
||||
signature.initSign(privateKey);
|
||||
signature.update(clientHelper.toByteArray(signed));
|
||||
byte[] sig = signature.sign();
|
||||
// Serialise the signed message
|
||||
BdfList message = BdfList.of(parent, authorList, contentType, body,
|
||||
sig);
|
||||
Message m = clientHelper.createMessage(groupId, timestamp, message);
|
||||
return new ForumPost(m, parent, author, contentType);
|
||||
}
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyParser;
|
||||
import org.briarproject.api.crypto.PublicKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfMessageValidator;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||
|
||||
class ForumPostValidator extends BdfMessageValidator {
|
||||
|
||||
private final CryptoComponent crypto;
|
||||
private final AuthorFactory authorFactory;
|
||||
|
||||
ForumPostValidator(CryptoComponent crypto, AuthorFactory authorFactory,
|
||||
ClientHelper clientHelper, MetadataEncoder metadataEncoder,
|
||||
Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
this.crypto = crypto;
|
||||
this.authorFactory = authorFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BdfDictionary validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
// Parent ID, author, content type, forum post body, signature
|
||||
checkSize(body, 5);
|
||||
// Parent ID is optional
|
||||
byte[] parent = body.getOptionalRaw(0);
|
||||
checkLength(parent, UniqueId.LENGTH);
|
||||
// Author is optional
|
||||
Author author = null;
|
||||
BdfList authorList = body.getOptionalList(1);
|
||||
if (authorList != null) {
|
||||
// Name, public key
|
||||
checkSize(authorList, 2);
|
||||
String name = authorList.getString(0);
|
||||
checkLength(name, 1, MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] publicKey = authorList.getRaw(1);
|
||||
checkLength(publicKey, 0, MAX_PUBLIC_KEY_LENGTH);
|
||||
author = authorFactory.createAuthor(name, publicKey);
|
||||
}
|
||||
// Content type
|
||||
String contentType = body.getString(2);
|
||||
checkLength(contentType, 0, MAX_CONTENT_TYPE_LENGTH);
|
||||
// Forum post body
|
||||
byte[] forumPostBody = body.getRaw(3);
|
||||
checkLength(forumPostBody, 0, MAX_FORUM_POST_BODY_LENGTH);
|
||||
// Signature is optional
|
||||
byte[] sig = body.getOptionalRaw(4);
|
||||
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
|
||||
// If there's an author there must be a signature and vice versa
|
||||
if (author != null && sig == null) {
|
||||
LOG.info("Author without signature");
|
||||
return null;
|
||||
}
|
||||
if (author == null && sig != null) {
|
||||
LOG.info("Signature without author");
|
||||
return null;
|
||||
}
|
||||
// Verify the signature, if any
|
||||
if (author != null) {
|
||||
try {
|
||||
// Parse the public key
|
||||
KeyParser keyParser = crypto.getSignatureKeyParser();
|
||||
PublicKey key = keyParser.parsePublicKey(author.getPublicKey());
|
||||
// Serialise the data to be signed
|
||||
BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
|
||||
authorList, contentType, forumPostBody);
|
||||
// Verify the signature
|
||||
Signature signature = crypto.getSignature();
|
||||
signature.initVerify(key);
|
||||
signature.update(clientHelper.toByteArray(signed));
|
||||
if (!signature.verify(sig)) {
|
||||
LOG.info("Invalid signature");
|
||||
return null;
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
LOG.info("Invalid public key");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// Return the metadata
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put("timestamp", m.getTimestamp());
|
||||
if (parent != null) meta.put("parent", parent);
|
||||
if (author != null) {
|
||||
BdfDictionary authorMeta = new BdfDictionary();
|
||||
authorMeta.put("id", author.getId());
|
||||
authorMeta.put("name", author.getName());
|
||||
authorMeta.put("publicKey", author.getPublicKey());
|
||||
meta.put("author", authorMeta);
|
||||
}
|
||||
meta.put("contentType", contentType);
|
||||
meta.put("read", false);
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
@@ -1,878 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.Client;
|
||||
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.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager.AddContactHook;
|
||||
import org.briarproject.api.contact.ContactManager.RemoveContactHook;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfEntry;
|
||||
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.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.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.forum.InviteeAction;
|
||||
import org.briarproject.api.forum.InviteeProtocolState;
|
||||
import org.briarproject.api.forum.SharerAction;
|
||||
import org.briarproject.api.forum.SharerProtocolState;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.MessageStatus;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfIncomingMessageHook;
|
||||
import org.briarproject.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
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_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.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.STORAGE_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK;
|
||||
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_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_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.InviteeProtocolState.AWAIT_INVITATION;
|
||||
import static org.briarproject.api.forum.InviteeProtocolState.AWAIT_LOCAL_RESPONSE;
|
||||
import static org.briarproject.api.forum.SharerProtocolState.PREPARE_INVITATION;
|
||||
|
||||
class ForumSharingManagerImpl extends BdfIncomingMessageHook
|
||||
implements ForumSharingManager, Client, RemoveForumHook,
|
||||
AddContactHook, RemoveContactHook {
|
||||
|
||||
static final ClientId CLIENT_ID = new ClientId(StringUtils.fromHexString(
|
||||
"cd11a5d04dccd9e2931d6fc3df456313"
|
||||
+ "63bb3e9d9d0e9405fccdb051f41f5449"));
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingManagerImpl.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 Clock clock;
|
||||
private final Group localGroup;
|
||||
|
||||
@Inject
|
||||
ForumSharingManagerImpl(DatabaseComponent db, ForumManager forumManager,
|
||||
MessageQueueManager messageQueueManager, ClientHelper clientHelper,
|
||||
MetadataParser metadataParser, MetadataEncoder metadataEncoder,
|
||||
SecureRandom random, PrivateGroupFactory privateGroupFactory,
|
||||
Clock clock) {
|
||||
|
||||
super(clientHelper, metadataParser);
|
||||
this.db = db;
|
||||
this.forumManager = forumManager;
|
||||
this.messageQueueManager = messageQueueManager;
|
||||
this.metadataEncoder = metadataEncoder;
|
||||
this.random = random;
|
||||
this.privateGroupFactory = privateGroupFactory;
|
||||
this.clock = clock;
|
||||
localGroup = privateGroupFactory.createLocalGroup(getClientId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createLocalState(Transaction txn) throws DbException {
|
||||
db.addGroup(txn, localGroup);
|
||||
// Ensure we've set things up for any pre-existing contacts
|
||||
for (Contact c : db.getContacts(txn)) addingContact(txn, c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addingContact(Transaction txn, Contact c) throws DbException {
|
||||
try {
|
||||
// Create a group to share with the contact
|
||||
Group g = getContactGroup(c);
|
||||
// Return if we've already set things up for this contact
|
||||
if (db.containsGroup(txn, g.getId())) return;
|
||||
// Store the group and share it with the contact
|
||||
db.addGroup(txn, g);
|
||||
db.setVisibleToContact(txn, c.getId(), g.getId(), true);
|
||||
// Attach the contact ID to the group
|
||||
BdfDictionary meta = new BdfDictionary();
|
||||
meta.put(CONTACT_ID, c.getId().getInt());
|
||||
meta.put(TO_BE_SHARED_BY_US, new BdfList());
|
||||
meta.put(SHARED_BY_US, new BdfList());
|
||||
meta.put(SHARED_WITH_US, new BdfList());
|
||||
clientHelper.mergeGroupMetadata(txn, g.getId(), meta);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingContact(Transaction txn, Contact c) throws DbException {
|
||||
// clean up session states with that contact from localGroup
|
||||
Long id = (long) c.getId().getInt();
|
||||
try {
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> entry : map.entrySet()) {
|
||||
BdfDictionary d = entry.getValue();
|
||||
if (id.equals(d.getLong(CONTACT_ID))) {
|
||||
deleteMessage(txn, entry.getKey());
|
||||
}
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
|
||||
// remove the contact group (all messages will be removed with it)
|
||||
db.removeGroup(txn, getContactGroup(c));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void incomingMessage(Transaction txn, Message m, BdfList body,
|
||||
BdfDictionary msg) throws DbException, FormatException {
|
||||
|
||||
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
|
||||
long type = msg.getLong(TYPE);
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) {
|
||||
// we are an invitee who just received a new invitation
|
||||
boolean stateExists = true;
|
||||
try {
|
||||
// check if we have a session with that ID already
|
||||
getSessionState(txn, sessionId, false);
|
||||
} catch (FormatException e) {
|
||||
// this is what we would expect under normal circumstances
|
||||
stateExists = false;
|
||||
}
|
||||
try {
|
||||
// check if we already have a state with that sessionId
|
||||
if (stateExists) throw new FormatException();
|
||||
|
||||
// check if forum can be shared
|
||||
Forum f = forumManager.createForum(msg.getString(FORUM_NAME),
|
||||
msg.getRaw(FORUM_SALT));
|
||||
ContactId contactId = getContactId(txn, m.getGroupId());
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
if (!canBeShared(txn, f.getId(), contact))
|
||||
throw new FormatException();
|
||||
|
||||
// initialize state and process invitation
|
||||
BdfDictionary state =
|
||||
initializeInviteeState(txn, contactId, msg);
|
||||
InviteeEngine engine = new InviteeEngine();
|
||||
processStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
deleteMessage(txn, m.getId());
|
||||
}
|
||||
} else if (type == SHARE_MSG_TYPE_ACCEPT ||
|
||||
type == SHARE_MSG_TYPE_DECLINE) {
|
||||
// we are a sharer who just received a response
|
||||
BdfDictionary state = getSessionState(txn, sessionId, true);
|
||||
SharerEngine engine = new SharerEngine();
|
||||
processStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else if (type == SHARE_MSG_TYPE_LEAVE ||
|
||||
type == SHARE_MSG_TYPE_ABORT) {
|
||||
// we don't know who we are, so figure it out
|
||||
BdfDictionary state = getSessionState(txn, sessionId, true);
|
||||
if (state.getBoolean(IS_SHARER)) {
|
||||
// we are a sharer and the invitee wants to leave or abort
|
||||
SharerEngine engine = new SharerEngine();
|
||||
processStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
} else {
|
||||
// we are an invitee and the sharer wants to leave or abort
|
||||
InviteeEngine engine = new InviteeEngine();
|
||||
processStateUpdate(txn, m.getId(),
|
||||
engine.onMessageReceived(state, msg));
|
||||
}
|
||||
} else {
|
||||
// message has passed validator, so that should never happen
|
||||
throw new RuntimeException("Illegal Forum Sharing Message");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientId getClientId() {
|
||||
return CLIENT_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendForumInvitation(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);
|
||||
BdfDictionary localState = initializeSharerState(txn, f, contactId);
|
||||
|
||||
// define action
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
localAction.put(TYPE, SHARE_MSG_TYPE_INVITATION);
|
||||
if (!StringUtils.isNullOrEmpty(msg)) {
|
||||
localAction.put(INVITATION_MSG, msg);
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
SharerEngine engine = new SharerEngine();
|
||||
processStateUpdate(txn, null,
|
||||
engine.onLocalAction(localState, localAction));
|
||||
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respondToInvitation(Forum f, boolean accept)
|
||||
throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
// find session state based on forum
|
||||
BdfDictionary localState = getSessionStateForResponse(txn, f);
|
||||
|
||||
// define action
|
||||
BdfDictionary localAction = new BdfDictionary();
|
||||
if (accept) {
|
||||
localAction.put(TYPE, SHARE_MSG_TYPE_ACCEPT);
|
||||
} else {
|
||||
localAction.put(TYPE, SHARE_MSG_TYPE_DECLINE);
|
||||
}
|
||||
|
||||
// start engine and process its state update
|
||||
InviteeEngine engine = new InviteeEngine();
|
||||
processStateUpdate(txn, null,
|
||||
engine.onLocalAction(localState, localAction));
|
||||
|
||||
txn.setComplete();
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ForumInvitationMessage> getForumInvitationMessages(
|
||||
ContactId contactId) throws DbException {
|
||||
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
Contact contact = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(contact);
|
||||
|
||||
Collection<ForumInvitationMessage> list =
|
||||
new ArrayList<ForumInvitationMessage>();
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, group.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary msg = m.getValue();
|
||||
try {
|
||||
if (msg.getLong(TYPE) != SHARE_MSG_TYPE_INVITATION)
|
||||
continue;
|
||||
|
||||
MessageStatus status =
|
||||
db.getMessageStatus(txn, contactId, m.getKey());
|
||||
SessionId sessionId = new SessionId(msg.getRaw(SESSION_ID));
|
||||
String name = msg.getString(FORUM_NAME);
|
||||
String message = msg.getOptionalString(INVITATION_MSG);
|
||||
long time = msg.getLong(TIME);
|
||||
boolean local = msg.getBoolean(LOCAL);
|
||||
boolean read = msg.getBoolean(READ, false);
|
||||
boolean available = false;
|
||||
if (!local) {
|
||||
// figure out whether the forum is still available
|
||||
BdfDictionary sessionState =
|
||||
getSessionState(txn, sessionId, true);
|
||||
InviteeProtocolState state = InviteeProtocolState
|
||||
.fromValue(
|
||||
sessionState.getLong(STATE).intValue());
|
||||
available = state == AWAIT_LOCAL_RESPONSE;
|
||||
}
|
||||
ForumInvitationMessage im =
|
||||
new ForumInvitationMessage(m.getKey(), sessionId,
|
||||
contactId, name, message, available, time,
|
||||
local, status.isSent(), status.isSeen(),
|
||||
read);
|
||||
list.add(im);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
return list;
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removingForum(Transaction txn, Forum 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)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_BY_US, f)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
if (removeFromList(txn, g, SHARED_WITH_US, f)) {
|
||||
leaveForum(txn, c.getId(), f);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Forum> getAvailableForums() throws DbException {
|
||||
try {
|
||||
Set<Forum> available = new HashSet<Forum>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
// Get any forums we subscribe to
|
||||
Set<Group> subscribed = new HashSet<Group>(db.getGroups(txn,
|
||||
forumManager.getClientId()));
|
||||
// Get all forums 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) {
|
||||
if (!subscribed.contains(f.getGroup()))
|
||||
available.add(f);
|
||||
}
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableSet(available);
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Contact> getSharedBy(GroupId g) throws DbException {
|
||||
try {
|
||||
List<Contact> subscribers = new ArrayList<Contact>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
GroupId contactGroup = getContactGroup(c).getId();
|
||||
if (listContains(txn, contactGroup, g, SHARED_WITH_US))
|
||||
subscribers.add(c);
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableList(subscribers);
|
||||
} catch (IOException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ContactId> getSharedWith(GroupId g) throws DbException {
|
||||
try {
|
||||
List<ContactId> shared = new ArrayList<ContactId>();
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
for (Contact c : db.getContacts(txn)) {
|
||||
GroupId contactGroup = getContactGroup(c).getId();
|
||||
if (listContains(txn, contactGroup, g, SHARED_BY_US))
|
||||
shared.add(c.getId());
|
||||
}
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return Collections.unmodifiableList(shared);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeShared(GroupId g, Contact c) throws DbException {
|
||||
boolean canBeShared;
|
||||
Transaction txn = db.startTransaction(true);
|
||||
try {
|
||||
canBeShared = canBeShared(txn, g, c);
|
||||
txn.setComplete();
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
return canBeShared;
|
||||
}
|
||||
|
||||
private boolean canBeShared(Transaction txn, GroupId g, Contact c)
|
||||
throws DbException {
|
||||
|
||||
try {
|
||||
GroupId contactGroup = getContactGroup(c).getId();
|
||||
return !listContains(txn, contactGroup, g, SHARED_BY_US) &&
|
||||
!listContains(txn, contactGroup, g, SHARED_WITH_US) &&
|
||||
!listContains(txn, contactGroup, g, TO_BE_SHARED_BY_US);
|
||||
} catch (FormatException e) {
|
||||
throw new DbException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private BdfDictionary initializeSharerState(Transaction txn, Forum f,
|
||||
ContactId contactId) throws FormatException, DbException {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(c);
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
Bytes salt = new Bytes(new byte[FORUM_SALT_LENGTH]);
|
||||
random.nextBytes(salt.getBytes());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(salt));
|
||||
MessageId sessionId = m.getId();
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, sessionId);
|
||||
d.put(STORAGE_ID, sessionId);
|
||||
d.put(GROUP_ID, group.getId());
|
||||
d.put(IS_SHARER, true);
|
||||
d.put(STATE, PREPARE_INVITATION.getValue());
|
||||
d.put(CONTACT_ID, contactId.getInt());
|
||||
d.put(FORUM_ID, f.getId());
|
||||
d.put(FORUM_NAME, f.getName());
|
||||
d.put(FORUM_SALT, f.getSalt());
|
||||
|
||||
// save local state to database
|
||||
clientHelper.addLocalMessage(txn, m, getClientId(), d, false);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
private BdfDictionary initializeInviteeState(Transaction txn,
|
||||
ContactId contactId, BdfDictionary msg)
|
||||
throws FormatException, DbException {
|
||||
|
||||
Contact c = db.getContact(txn, contactId);
|
||||
Group group = getContactGroup(c);
|
||||
String name = msg.getString(FORUM_NAME);
|
||||
byte[] salt = msg.getRaw(FORUM_SALT);
|
||||
Forum f = forumManager.createForum(name, salt);
|
||||
|
||||
// create local message to keep engine state
|
||||
long now = clock.currentTimeMillis();
|
||||
Bytes mSalt = new Bytes(new byte[FORUM_SALT_LENGTH]);
|
||||
random.nextBytes(mSalt.getBytes());
|
||||
Message m = clientHelper.createMessage(localGroup.getId(), now,
|
||||
BdfList.of(mSalt));
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
d.put(SESSION_ID, msg.getRaw(SESSION_ID));
|
||||
d.put(STORAGE_ID, m.getId());
|
||||
d.put(GROUP_ID, group.getId());
|
||||
d.put(IS_SHARER, false);
|
||||
d.put(STATE, AWAIT_INVITATION.getValue());
|
||||
d.put(CONTACT_ID, contactId.getInt());
|
||||
d.put(FORUM_ID, f.getId());
|
||||
d.put(FORUM_NAME, name);
|
||||
d.put(FORUM_SALT, salt);
|
||||
|
||||
// save local state to database
|
||||
clientHelper.addLocalMessage(txn, m, getClientId(), d, false);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
private BdfDictionary getSessionState(Transaction txn, SessionId sessionId,
|
||||
boolean warn) throws DbException, FormatException {
|
||||
|
||||
try {
|
||||
// we should be able to get the sharer state directly from sessionId
|
||||
return clientHelper.getMessageMetadataAsDictionary(txn, sessionId);
|
||||
} catch (NoSuchMessageException e) {
|
||||
// State not found directly, so iterate over all states
|
||||
// to find state for invitee
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary state = m.getValue();
|
||||
if (Arrays.equals(state.getRaw(SESSION_ID),
|
||||
sessionId.getBytes())) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
if (warn && LOG.isLoggable(WARNING)) {
|
||||
LOG.warning(
|
||||
"No session state found for message with session ID " +
|
||||
Arrays.hashCode(sessionId.getBytes()));
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
|
||||
private BdfDictionary getSessionStateForResponse(Transaction txn, Forum f)
|
||||
throws DbException, FormatException {
|
||||
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
try {
|
||||
InviteeProtocolState state = InviteeProtocolState
|
||||
.fromValue(d.getLong(STATE).intValue());
|
||||
if (state == AWAIT_LOCAL_RESPONSE) {
|
||||
byte[] id = d.getRaw(FORUM_ID);
|
||||
if (Arrays.equals(f.getId().getBytes(), id)) {
|
||||
// Note that there should always be only one session
|
||||
// in this state for the same forum
|
||||
return d;
|
||||
}
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
throw new DbException();
|
||||
}
|
||||
|
||||
private BdfDictionary getSessionStateForLeaving(Transaction txn, Forum f,
|
||||
ContactId c) throws DbException, FormatException {
|
||||
|
||||
Map<MessageId, BdfDictionary> map = clientHelper
|
||||
.getMessageMetadataAsDictionary(txn, localGroup.getId());
|
||||
for (Map.Entry<MessageId, BdfDictionary> m : map.entrySet()) {
|
||||
BdfDictionary d = m.getValue();
|
||||
try {
|
||||
// check that this session is with the right contact
|
||||
if (c.getInt() != d.getLong(CONTACT_ID)) continue;
|
||||
// check that a forum get be left in current session
|
||||
int intState = d.getLong(STATE).intValue();
|
||||
if (d.getBoolean(IS_SHARER)) {
|
||||
SharerProtocolState state =
|
||||
SharerProtocolState.fromValue(intState);
|
||||
if (state.next(SharerAction.LOCAL_LEAVE) ==
|
||||
SharerProtocolState.ERROR) continue;
|
||||
} else {
|
||||
InviteeProtocolState state = InviteeProtocolState
|
||||
.fromValue(intState);
|
||||
if (state.next(InviteeAction.LOCAL_LEAVE) ==
|
||||
InviteeProtocolState.ERROR) continue;
|
||||
}
|
||||
// check that this state actually concerns this forum
|
||||
String name = d.getString(FORUM_NAME);
|
||||
byte[] salt = d.getRaw(FORUM_SALT);
|
||||
if (name.equals(f.getName()) &&
|
||||
Arrays.equals(salt, f.getSalt())) {
|
||||
// TODO what happens when there is more than one invitation?
|
||||
return d;
|
||||
}
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
throw new FormatException();
|
||||
}
|
||||
|
||||
private void processStateUpdate(Transaction txn, MessageId messageId,
|
||||
StateUpdate<BdfDictionary, BdfDictionary> result)
|
||||
throws DbException, FormatException {
|
||||
|
||||
// perform actions based on new local state
|
||||
performTasks(txn, result.localState);
|
||||
|
||||
// save new local state
|
||||
MessageId storageId =
|
||||
new MessageId(result.localState.getRaw(STORAGE_ID));
|
||||
clientHelper.mergeMessageMetadata(txn, storageId, result.localState);
|
||||
|
||||
// send messages
|
||||
for (BdfDictionary d : result.toSend) {
|
||||
sendMessage(txn, d);
|
||||
}
|
||||
|
||||
// broadcast events
|
||||
for (Event event : result.toBroadcast) {
|
||||
txn.attach(event);
|
||||
}
|
||||
|
||||
// delete message
|
||||
if (result.deleteMessage && messageId != null) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Deleting message with id " + messageId.hashCode());
|
||||
}
|
||||
db.deleteMessage(txn, messageId);
|
||||
db.deleteMessageMetadata(txn, messageId);
|
||||
}
|
||||
}
|
||||
|
||||
private void performTasks(Transaction txn, BdfDictionary localState)
|
||||
throws FormatException, DbException {
|
||||
|
||||
if (!localState.containsKey(TASK)) return;
|
||||
|
||||
// remember task and remove it from localState
|
||||
long task = localState.getLong(TASK);
|
||||
localState.put(TASK, BdfDictionary.NULL_VALUE);
|
||||
|
||||
// get group ID for later
|
||||
GroupId groupId = new GroupId(localState.getRaw(GROUP_ID));
|
||||
// get contact ID for later
|
||||
ContactId contactId =
|
||||
new ContactId(localState.getLong(CONTACT_ID).intValue());
|
||||
|
||||
// get forum for later
|
||||
String name = localState.getString(FORUM_NAME);
|
||||
byte[] salt = localState.getRaw(FORUM_SALT);
|
||||
Forum f = forumManager.createForum(name, salt);
|
||||
|
||||
// perform tasks
|
||||
if (task == TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US) {
|
||||
addToList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US) {
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
else if (task == TASK_ADD_SHARED_FORUM) {
|
||||
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) {
|
||||
addToList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US) {
|
||||
removeFromList(txn, groupId, TO_BE_SHARED_BY_US, f);
|
||||
}
|
||||
else if (task == TASK_SHARE_FORUM) {
|
||||
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) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), false);
|
||||
removeFromList(txn, groupId, SHARED_BY_US, f);
|
||||
} else if (task == TASK_UNSHARE_FORUM_SHARED_WITH_US) {
|
||||
db.setVisibleToContact(txn, contactId, f.getId(), false);
|
||||
removeFromList(txn, groupId, SHARED_WITH_US, f);
|
||||
}
|
||||
}
|
||||
|
||||
private void sendMessage(Transaction txn, BdfDictionary m)
|
||||
throws FormatException, DbException {
|
||||
|
||||
BdfList list = encodeMessage(m);
|
||||
byte[] body = clientHelper.toByteArray(list);
|
||||
GroupId groupId = new GroupId(m.getRaw(GROUP_ID));
|
||||
Group group = db.getGroup(txn, groupId);
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
|
||||
// add message itself as metadata
|
||||
m.put(LOCAL, true);
|
||||
m.put(TIME, timestamp);
|
||||
Metadata meta = metadataEncoder.encode(m);
|
||||
|
||||
messageQueueManager
|
||||
.sendMessage(txn, group, timestamp, body, meta);
|
||||
}
|
||||
|
||||
private BdfList encodeMessage(BdfDictionary m) throws FormatException {
|
||||
long type = m.getLong(TYPE);
|
||||
|
||||
BdfList list;
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) {
|
||||
list = BdfList.of(type,
|
||||
m.getRaw(SESSION_ID),
|
||||
m.getString(FORUM_NAME),
|
||||
m.getRaw(FORUM_SALT)
|
||||
);
|
||||
String msg = m.getOptionalString(INVITATION_MSG);
|
||||
if (msg != null) list.add(msg);
|
||||
} else if (type == SHARE_MSG_TYPE_ACCEPT) {
|
||||
list = BdfList.of(type, m.getRaw(SESSION_ID));
|
||||
} else if (type == SHARE_MSG_TYPE_DECLINE) {
|
||||
list = BdfList.of(type, m.getRaw(SESSION_ID));
|
||||
} else if (type == SHARE_MSG_TYPE_LEAVE) {
|
||||
list = BdfList.of(type, m.getRaw(SESSION_ID));
|
||||
} else if (type == SHARE_MSG_TYPE_ABORT) {
|
||||
list = BdfList.of(type, m.getRaw(SESSION_ID));
|
||||
} else {
|
||||
throw new FormatException();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private Group getContactGroup(Contact c) {
|
||||
return privateGroupFactory.createPrivateGroup(CLIENT_ID, c);
|
||||
}
|
||||
|
||||
private ContactId getContactId(Transaction txn, GroupId contactGroupId)
|
||||
throws DbException, FormatException {
|
||||
BdfDictionary meta = clientHelper.getGroupMetadataAsDictionary(txn,
|
||||
contactGroupId);
|
||||
return new ContactId(meta.getLong(CONTACT_ID).intValue());
|
||||
}
|
||||
|
||||
private void leaveForum(Transaction txn, ContactId c, Forum f)
|
||||
throws DbException, FormatException {
|
||||
|
||||
BdfDictionary state = getSessionStateForLeaving(txn, f, c);
|
||||
BdfDictionary action = new BdfDictionary();
|
||||
action.put(TYPE, SHARE_MSG_TYPE_LEAVE);
|
||||
if (state.getBoolean(IS_SHARER)) {
|
||||
SharerEngine engine = new SharerEngine();
|
||||
processStateUpdate(txn, null,
|
||||
engine.onLocalAction(state, action));
|
||||
} else {
|
||||
InviteeEngine engine = new InviteeEngine();
|
||||
processStateUpdate(txn, null,
|
||||
engine.onLocalAction(state, action));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean listContains(Transaction txn, GroupId contactGroup,
|
||||
GroupId forum, String key) throws DbException, FormatException {
|
||||
|
||||
List<Forum> list = getForumList(txn, contactGroup, key);
|
||||
for (Forum f : list) {
|
||||
if (f.getId().equals(forum)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean addToList(Transaction txn, GroupId groupId, String key,
|
||||
Forum 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);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean removeFromList(Transaction txn, GroupId groupId, String key,
|
||||
Forum f) throws DbException, FormatException {
|
||||
|
||||
List<Forum> forums = getForumList(txn, groupId, key);
|
||||
if (forums.remove(f)) {
|
||||
storeForumList(txn, groupId, key, forums);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<Forum> getForumList(Transaction txn, GroupId groupId,
|
||||
String key) throws DbException, FormatException {
|
||||
|
||||
BdfDictionary metadata =
|
||||
clientHelper.getGroupMetadataAsDictionary(txn, groupId);
|
||||
BdfList list = metadata.getList(key);
|
||||
|
||||
return parseForumList(list);
|
||||
}
|
||||
|
||||
private void storeForumList(Transaction txn, GroupId groupId, String key,
|
||||
List<Forum> forums) throws DbException, FormatException {
|
||||
|
||||
BdfList list = encodeForumList(forums);
|
||||
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 List<Forum> parseForumList(BdfList list) throws FormatException {
|
||||
List<Forum> forums = new ArrayList<Forum>(list.size());
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
BdfList forum = list.getList(i);
|
||||
forums.add(forumManager
|
||||
.createForum(forum.getString(0), forum.getRaw(1)));
|
||||
}
|
||||
return forums;
|
||||
}
|
||||
|
||||
private void deleteMessage(Transaction txn, MessageId messageId)
|
||||
throws DbException {
|
||||
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Deleting message with ID: " + messageId.hashCode());
|
||||
|
||||
db.deleteMessage(txn, messageId);
|
||||
db.deleteMessageMetadata(txn, messageId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.briarproject.forum;
|
||||
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.data.MetadataEncoder;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.clients.BdfMessageValidator;
|
||||
|
||||
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.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
||||
|
||||
class ForumSharingValidator extends BdfMessageValidator {
|
||||
|
||||
ForumSharingValidator(ClientHelper clientHelper,
|
||||
MetadataEncoder metadataEncoder, Clock clock) {
|
||||
super(clientHelper, metadataEncoder, clock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BdfDictionary validateMessage(Message m, Group g,
|
||||
BdfList body) throws FormatException {
|
||||
|
||||
BdfDictionary d = new BdfDictionary();
|
||||
long type = body.getLong(0);
|
||||
byte[] id = body.getRaw(1);
|
||||
checkLength(id, SessionId.LENGTH);
|
||||
|
||||
if (type == SHARE_MSG_TYPE_INVITATION) {
|
||||
checkSize(body, 4, 5);
|
||||
|
||||
String name = body.getString(2);
|
||||
checkLength(name, 1, MAX_FORUM_NAME_LENGTH);
|
||||
|
||||
byte[] salt = body.getRaw(3);
|
||||
checkLength(salt, FORUM_SALT_LENGTH);
|
||||
|
||||
d.put(FORUM_NAME, name);
|
||||
d.put(FORUM_SALT, salt);
|
||||
|
||||
if (body.size() > 4) {
|
||||
String msg = body.getString(4);
|
||||
checkLength(msg, 0, MAX_MESSAGE_BODY_LENGTH);
|
||||
d.put(INVITATION_MSG, msg);
|
||||
}
|
||||
} else {
|
||||
checkSize(body, 2);
|
||||
if (type != SHARE_MSG_TYPE_ACCEPT &&
|
||||
type != SHARE_MSG_TYPE_DECLINE &&
|
||||
type != SHARE_MSG_TYPE_LEAVE &&
|
||||
type != SHARE_MSG_TYPE_ABORT) {
|
||||
throw new FormatException();
|
||||
}
|
||||
}
|
||||
// Return the metadata
|
||||
d.put(TYPE, type);
|
||||
d.put(SESSION_ID, id);
|
||||
d.put(GROUP_ID, m.getGroupId());
|
||||
d.put(LOCAL, false);
|
||||
d.put(TIME, m.getTimestamp());
|
||||
return d;
|
||||
}
|
||||
}
|
||||
@@ -1,265 +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.data.BdfDictionary;
|
||||
import org.briarproject.api.data.BdfEntry;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.InviteeAction;
|
||||
import org.briarproject.api.forum.InviteeProtocolState;
|
||||
|
||||
import java.util.Arrays;
|
||||
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.CONTACT_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.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.STATE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK;
|
||||
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.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.forum.InviteeAction.LOCAL_ABORT;
|
||||
import static org.briarproject.api.forum.InviteeAction.LOCAL_ACCEPT;
|
||||
import static org.briarproject.api.forum.InviteeAction.LOCAL_DECLINE;
|
||||
import static org.briarproject.api.forum.InviteeAction.LOCAL_LEAVE;
|
||||
import static org.briarproject.api.forum.InviteeAction.REMOTE_INVITATION;
|
||||
import static org.briarproject.api.forum.InviteeAction.REMOTE_LEAVE;
|
||||
import static org.briarproject.api.forum.InviteeProtocolState.ERROR;
|
||||
import static org.briarproject.api.forum.InviteeProtocolState.FINISHED;
|
||||
import static org.briarproject.api.forum.InviteeProtocolState.LEFT;
|
||||
|
||||
public class InviteeEngine
|
||||
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
|
||||
BdfDictionary localState, BdfDictionary localAction) {
|
||||
|
||||
try {
|
||||
InviteeProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
long type = localAction.getLong(TYPE);
|
||||
InviteeAction action = InviteeAction.getLocal(type);
|
||||
InviteeProtocolState nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
|
||||
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<BdfDictionary> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == LOCAL_ACCEPT || action == LOCAL_DECLINE) {
|
||||
BdfDictionary msg = BdfDictionary.of(
|
||||
new BdfEntry(SESSION_ID, localState.getRaw(SESSION_ID)),
|
||||
new BdfEntry(GROUP_ID, localState.getRaw(GROUP_ID))
|
||||
);
|
||||
if (action == LOCAL_ACCEPT) {
|
||||
localState.put(TASK, TASK_ADD_SHARED_FORUM);
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_ACCEPT);
|
||||
} else {
|
||||
localState.put(TASK,
|
||||
TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_DECLINE);
|
||||
}
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else if (action == LOCAL_LEAVE) {
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_LEAVE);
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
|
||||
try {
|
||||
InviteeProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
long type = msg.getLong(TYPE);
|
||||
InviteeAction action = InviteeAction.getRemote(type);
|
||||
InviteeProtocolState nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
|
||||
logMessageReceived(currentState, nextState, type, msg);
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
|
||||
List<BdfDictionary> 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.put(TASK, 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.put(TASK, TASK_REMOVE_FORUM_FROM_LIST_SHARED_WITH_US);
|
||||
}
|
||||
// we have just received our invitation
|
||||
else if (action == REMOTE_INVITATION) {
|
||||
localState.put(TASK, TASK_ADD_FORUM_TO_LIST_SHARED_WITH_US);
|
||||
// TODO how to get the proper group here?
|
||||
Forum forum = new Forum(null, localState.getString(FORUM_NAME),
|
||||
localState.getRaw(FORUM_SALT));
|
||||
ContactId contactId = new ContactId(
|
||||
localState.getLong(CONTACT_ID).intValue());
|
||||
Event event = new ForumInvitationReceivedEvent(forum, contactId);
|
||||
events = Collections.singletonList(event);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(InviteeProtocolState state,
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "response";
|
||||
if (msg.getLong(TYPE, -1L) == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
try {
|
||||
LOG.info("Sending " + a + " in state " + state.name() +
|
||||
" with session ID " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " +
|
||||
getState(localState.getLong(STATE)).name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logMessageReceived(InviteeProtocolState currentState,
|
||||
InviteeProtocolState nextState, long type, BdfDictionary msg) {
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
try {
|
||||
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 " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
|
||||
BdfDictionary localState, BdfDictionary delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private InviteeProtocolState getState(Long state) {
|
||||
return InviteeProtocolState.fromValue(state.intValue());
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
|
||||
InviteeProtocolState currentState, BdfDictionary localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
|
||||
localState.put(STATE, ERROR.getValue());
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_ABORT);
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
List<BdfDictionary> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
|
||||
BdfDictionary localState, boolean delete) throws FormatException {
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
|
||||
localState, Collections.<BdfDictionary>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
}
|
||||
@@ -1,264 +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.data.BdfDictionary;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.forum.SharerAction;
|
||||
import org.briarproject.api.forum.SharerProtocolState;
|
||||
|
||||
import java.util.Arrays;
|
||||
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.CONTACT_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.INVITATION_MSG;
|
||||
import static org.briarproject.api.forum.ForumConstants.SESSION_ID;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ABORT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_ACCEPT;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_DECLINE;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_LEAVE;
|
||||
import static org.briarproject.api.forum.ForumConstants.STATE;
|
||||
import static org.briarproject.api.forum.ForumConstants.TASK;
|
||||
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.ForumConstants.TYPE;
|
||||
import static org.briarproject.api.forum.SharerAction.LOCAL_ABORT;
|
||||
import static org.briarproject.api.forum.SharerAction.LOCAL_INVITATION;
|
||||
import static org.briarproject.api.forum.SharerAction.LOCAL_LEAVE;
|
||||
import static org.briarproject.api.forum.SharerAction.REMOTE_ACCEPT;
|
||||
import static org.briarproject.api.forum.SharerAction.REMOTE_DECLINE;
|
||||
import static org.briarproject.api.forum.SharerAction.REMOTE_LEAVE;
|
||||
import static org.briarproject.api.forum.SharerProtocolState.ERROR;
|
||||
import static org.briarproject.api.forum.SharerProtocolState.FINISHED;
|
||||
import static org.briarproject.api.forum.SharerProtocolState.LEFT;
|
||||
|
||||
public class SharerEngine
|
||||
implements ProtocolEngine<BdfDictionary, BdfDictionary, BdfDictionary> {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SharerEngine.class.getName());
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onLocalAction(
|
||||
BdfDictionary localState, BdfDictionary localAction) {
|
||||
|
||||
try {
|
||||
SharerProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
long type = localAction.getLong(TYPE);
|
||||
SharerAction action = SharerAction.getLocal(type);
|
||||
SharerProtocolState nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
|
||||
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<BdfDictionary> messages;
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
if (action == LOCAL_INVITATION) {
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_INVITATION);
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
msg.put(FORUM_NAME, localState.getString(FORUM_NAME));
|
||||
msg.put(FORUM_SALT, localState.getRaw(FORUM_SALT));
|
||||
if (localAction.containsKey(INVITATION_MSG)) {
|
||||
msg.put(INVITATION_MSG,
|
||||
localAction.getString(INVITATION_MSG));
|
||||
}
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
|
||||
// remember that we offered to share this forum
|
||||
localState.put(TASK, TASK_ADD_FORUM_TO_LIST_TO_BE_SHARED_BY_US);
|
||||
}
|
||||
else if (action == LOCAL_LEAVE) {
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_LEAVE);
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
messages = Collections.singletonList(msg);
|
||||
logLocalAction(currentState, localState, msg);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown Local Action");
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageReceived(
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
|
||||
try {
|
||||
SharerProtocolState currentState =
|
||||
getState(localState.getLong(STATE));
|
||||
long type = msg.getLong(TYPE);
|
||||
SharerAction action = SharerAction.getRemote(type);
|
||||
SharerProtocolState nextState = currentState.next(action);
|
||||
localState.put(STATE, nextState.getValue());
|
||||
|
||||
logMessageReceived(currentState, nextState, type, msg);
|
||||
|
||||
if (nextState == ERROR) {
|
||||
if (currentState != ERROR) {
|
||||
return abortSession(currentState, localState);
|
||||
} else {
|
||||
return noUpdate(localState, true);
|
||||
}
|
||||
}
|
||||
List<BdfDictionary> 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.put(TASK, 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.put(TASK, TASK_SHARE_FORUM);
|
||||
} else {
|
||||
// this ensures that the forum can be shared again
|
||||
localState.put(TASK,
|
||||
TASK_REMOVE_FORUM_FROM_LIST_TO_BE_SHARED_BY_US);
|
||||
}
|
||||
String name = localState.getString(FORUM_NAME);
|
||||
ContactId c = new ContactId(
|
||||
localState.getLong(CONTACT_ID).intValue());
|
||||
Event event = new ForumInvitationResponseReceivedEvent(name, c);
|
||||
events = Collections.singletonList(event);
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Bad state");
|
||||
}
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(deleteMsg,
|
||||
false, localState, messages, events);
|
||||
} catch (FormatException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logLocalAction(SharerProtocolState state,
|
||||
BdfDictionary localState, BdfDictionary msg) {
|
||||
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
String a = "invitation";
|
||||
if (msg.getLong(TYPE, -1L) == SHARE_MSG_TYPE_LEAVE) a = "leave";
|
||||
|
||||
try {
|
||||
LOG.info("Sending " + a + " in state " + state.name() +
|
||||
" with session ID " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " +
|
||||
getState(localState.getLong(STATE)).name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void logMessageReceived(SharerProtocolState currentState,
|
||||
SharerProtocolState nextState, long type, BdfDictionary msg) {
|
||||
if (!LOG.isLoggable(INFO)) return;
|
||||
|
||||
try {
|
||||
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 " +
|
||||
Arrays.hashCode(msg.getRaw(SESSION_ID)) + " in group " +
|
||||
Arrays.hashCode(msg.getRaw(GROUP_ID)) + ". " +
|
||||
"Moving on to state " + nextState.name()
|
||||
);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public StateUpdate<BdfDictionary, BdfDictionary> onMessageDelivered(
|
||||
BdfDictionary localState, BdfDictionary delivered) {
|
||||
try {
|
||||
return noUpdate(localState, false);
|
||||
} catch (FormatException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SharerProtocolState getState(Long state) {
|
||||
return SharerProtocolState.fromValue(state.intValue());
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> abortSession(
|
||||
SharerProtocolState currentState, BdfDictionary localState)
|
||||
throws FormatException {
|
||||
|
||||
if (LOG.isLoggable(WARNING)) {
|
||||
LOG.warning("Aborting protocol session " +
|
||||
Arrays.hashCode(localState.getRaw(SESSION_ID)) +
|
||||
" in state " + currentState.name());
|
||||
}
|
||||
|
||||
localState.put(STATE, ERROR.getValue());
|
||||
BdfDictionary msg = new BdfDictionary();
|
||||
msg.put(TYPE, SHARE_MSG_TYPE_ABORT);
|
||||
msg.put(SESSION_ID, localState.getRaw(SESSION_ID));
|
||||
msg.put(GROUP_ID, localState.getRaw(GROUP_ID));
|
||||
List<BdfDictionary> messages = Collections.singletonList(msg);
|
||||
|
||||
List<Event> events = Collections.emptyList();
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(false, false,
|
||||
localState, messages, events);
|
||||
}
|
||||
|
||||
private StateUpdate<BdfDictionary, BdfDictionary> noUpdate(
|
||||
BdfDictionary localState, boolean delete) throws FormatException {
|
||||
|
||||
return new StateUpdate<BdfDictionary, BdfDictionary>(delete, false,
|
||||
localState, Collections.<BdfDictionary>emptyList(),
|
||||
Collections.<Event>emptyList());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user