diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.java b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.java index 257d94ee0..60dd7feb0 100644 --- a/briar-headless/src/main/java/org/briarproject/briar/headless/Router.java +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/Router.java @@ -3,6 +3,7 @@ package org.briarproject.briar.headless; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.briar.headless.blogs.BlogController; import org.briarproject.briar.headless.forums.ForumController; +import org.briarproject.briar.headless.messaging.MessagingController; import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.concurrent.Immutable; @@ -25,13 +26,17 @@ import static java.lang.Runtime.getRuntime; public class Router { private final BriarService briarService; + private final MessagingController messagingController; private final ForumController forumController; private final BlogController blogController; @Inject - public Router(BriarService briarService, ForumController forumController, + public Router(BriarService briarService, + MessagingController messagingController, + ForumController forumController, BlogController blogController) { this.briarService = briarService; + this.messagingController = messagingController; this.forumController = forumController; this.blogController = blogController; } @@ -51,6 +56,10 @@ public class Router { .start(); app.routes(() -> { + path("/messages/:contactId", () -> { + get(messagingController::list); + post(messagingController::write); + }); path("/forums", () -> { get(forumController::list); post(forumController::create); diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.java b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.java new file mode 100644 index 000000000..7146bc8ce --- /dev/null +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/MessagingController.java @@ -0,0 +1,97 @@ +package org.briarproject.briar.headless.messaging; + +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.contact.ContactId; +import org.briarproject.bramble.api.contact.ContactManager; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.db.NoSuchContactException; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.sync.Group; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.messaging.MessagingManager; +import org.briarproject.briar.api.messaging.PrivateMessage; +import org.briarproject.briar.api.messaging.PrivateMessageFactory; +import org.briarproject.briar.api.messaging.PrivateMessageHeader; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; +import javax.inject.Singleton; + +import io.javalin.Context; + +import static org.briarproject.briar.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH; + +@Immutable +@Singleton +@NotNullByDefault +public class MessagingController { + + private final MessagingManager messagingManager; + private final PrivateMessageFactory privateMessageFactory; + private final ContactManager contactManager; + private final Clock clock; + + @Inject + public MessagingController(MessagingManager messagingManager, + PrivateMessageFactory privateMessageFactory, + ContactManager contactManager, + Clock clock) { + this.messagingManager = messagingManager; + this.privateMessageFactory = privateMessageFactory; + this.contactManager = contactManager; + this.clock = clock; + } + + public Context list(Context ctx) throws DbException { + Contact contact = getContact(ctx); + if (contact == null) return ctx.status(404); + + Collection headers = + messagingManager.getMessageHeaders(contact.getId()); + List messages = new ArrayList<>(headers.size()); + for (PrivateMessageHeader header : headers) { + String body = messagingManager.getMessageBody(header.getId()); + messages.add(new OutputPrivateMessage(header, body)); + } + return ctx.json(messages); + } + + public Context write(Context ctx) throws DbException, FormatException { + Contact contact = getContact(ctx); + if (contact == null) return ctx.status(404); + + String message = ctx.formParam("message"); + if (message == null || message.length() < 1) + return ctx.status(500).result("Expecting Message text"); + if (message.length() > MAX_PRIVATE_MESSAGE_BODY_LENGTH) + return ctx.status(500).result("Message text too large"); + + Group group = messagingManager.getContactGroup(contact); + long now = clock.currentTimeMillis(); + PrivateMessage m = privateMessageFactory + .createPrivateMessage(group.getId(), now, message); + + messagingManager.addLocalMessage(m); + return ctx.json(new OutputPrivateMessage(m, message)); + } + + @Nullable + private Contact getContact(Context ctx) throws DbException { + String contactString = ctx.param("contactId"); + if (contactString == null) return null; + int contactInt = Integer.parseInt(contactString); + ContactId contactId = new ContactId(contactInt); + try { + return contactManager.getContact(contactId); + } catch (NoSuchContactException e) { + return null; + } + } + +} diff --git a/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.java b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.java new file mode 100644 index 000000000..6cf491d0e --- /dev/null +++ b/briar-headless/src/main/java/org/briarproject/briar/headless/messaging/OutputPrivateMessage.java @@ -0,0 +1,43 @@ +package org.briarproject.briar.headless.messaging; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.api.messaging.PrivateMessage; +import org.briarproject.briar.api.messaging.PrivateMessageHeader; + +import javax.annotation.concurrent.Immutable; + +@Immutable +@NotNullByDefault +@SuppressWarnings("WeakerAccess") +public class OutputPrivateMessage { + + public final String body; + public final long timestamp; + public final boolean read, seen, sent, local; + public final byte[] id, groupId; + + OutputPrivateMessage(PrivateMessageHeader header, String body) { + this.body = body; + this.timestamp = header.getTimestamp(); + this.read = header.isRead(); + this.seen = header.isSeen(); + this.sent = header.isSent(); + this.local = header.isLocal(); + this.id = header.getId().getBytes(); + this.groupId = header.getGroupId().getBytes(); + } + + /** + * Only meant for own {@link PrivateMessage}s directly after creation. + */ + OutputPrivateMessage(PrivateMessage m, String body) { + this.body = body; + this.timestamp = m.getMessage().getTimestamp(); + this.read = true; + this.seen = true; + this.sent = false; + this.local = true; + this.id = m.getMessage().getId().getBytes(); + this.groupId = m.getMessage().getGroupId().getBytes(); + } +}