aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/juick/ActivityPubManager.java4
-rw-r--r--src/main/java/com/juick/www/api/activity/Profile.java121
-rw-r--r--src/main/java/com/juick/www/api/activity/helpers/ContextDeserializer.java38
-rw-r--r--src/main/java/com/juick/www/api/activity/model/Activity.java9
-rw-r--r--src/main/java/com/juick/www/api/activity/model/Context.java10
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Note.java6
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Tombstone.java24
-rw-r--r--src/test/java/com/juick/server/tests/ServerTests.java32
8 files changed, 151 insertions, 93 deletions
diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java
index 8aa2ef78..cb4d0b54 100644
--- a/src/main/java/com/juick/ActivityPubManager.java
+++ b/src/main/java/com/juick/ActivityPubManager.java
@@ -92,7 +92,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
@Override
public void processFollowEvent(@Nonnull FollowEvent followEvent) {
- String acct = (String)followEvent.getRequest().getObject();
+ String acct = followEvent.getRequest().getObject().getId();
logger.info("received follower request to {}", acct);
User followedUser = socialService.getUserByAccountUri(acct);
if (!followedUser.isAnonymous()) {
@@ -195,7 +195,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
Update update = new Update();
update.setId(objectUri + "#update");
update.setActor(me.getId());
- update.setObject(objectUri);
+ update.setObject(new Context(objectUri));
logger.info("Update to follower {}", follower.getId());
signatureManager.post(me, follower, update);
} catch (NoSuchElementException e) {
diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java
index 72cc849e..751d0cff 100644
--- a/src/main/java/com/juick/www/api/activity/Profile.java
+++ b/src/main/java/com/juick/www/api/activity/Profile.java
@@ -40,6 +40,7 @@ import com.juick.www.api.activity.model.objects.Note;
import com.juick.www.api.activity.model.objects.OrderedCollection;
import com.juick.www.api.activity.model.objects.OrderedCollectionPage;
import com.juick.www.api.activity.model.objects.Person;
+import com.juick.www.api.activity.model.objects.Tombstone;
import com.juick.util.HttpNotFoundException;
import com.juick.www.WebApp;
import com.juick.service.MessagesService;
@@ -310,32 +311,52 @@ public class Profile {
}
}
if (activity instanceof Create) {
- if (activity.getObject() instanceof Map) {
- Map<String, Object> note = (Map<String, Object>) activity.getObject();
- if (note.get("type").equals("Note")) {
- URI noteId = URI.create((String) note.get("id"));
- if (messagesService.replyExists(noteId)) {
- return new ResponseEntity<>(CommandResult.fromString("Reply already exists"),
- HttpStatus.OK);
- } else {
- String inReplyTo = (String) note.get("inReplyTo");
- if (StringUtils.isNotBlank(inReplyTo)) {
- if (inReplyTo.startsWith(baseUri)) {
- String postId = activityPubManager.postId(inReplyTo);
+ if (activity.getObject() instanceof Note) {
+ Note note = (Note) activity.getObject();
+ URI noteId = URI.create((String) note.getId());
+ if (messagesService.replyExists(noteId)) {
+ return new ResponseEntity<>(CommandResult.fromString("Reply already exists"), HttpStatus.OK);
+ } else {
+ String inReplyTo = (String) note.getInReplyTo();
+ if (StringUtils.isNotBlank(inReplyTo)) {
+ if (inReplyTo.startsWith(baseUri)) {
+ String postId = activityPubManager.postId(inReplyTo);
+ User user = new User();
+ user.setUri(URI.create(activity.getActor()));
+ String markdown = remarkConverter.convertFragment((String) note.getContent());
+ String commandBody = note.getAttachment() == null ? markdown
+ : note.getAttachment().stream().map(attachment -> {
+ String attachmentUrl = attachment.getUrl();
+ String attachmentName = attachment.getName();
+ return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
+ }).reduce(markdown,
+ (currentUrl, nextUrl) -> String.format("%s\n%s", currentUrl, nextUrl));
+
+ CommandResult result = commandsManager.processCommand(user,
+ String.format("#%s %s", postId, commandBody), URI.create(StringUtils.EMPTY));
+ logger.info(jsonMapper.writeValueAsString(result));
+ if (result.getNewMessage().isPresent()) {
+ messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
+ return new ResponseEntity<>(result, HttpStatus.OK);
+ } else {
+ return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
+ }
+ } else {
+ Message reply = messagesService.getReplyByUri(inReplyTo);
+ if (reply != null) {
User user = new User();
user.setUri(URI.create(activity.getActor()));
- String markdown = remarkConverter.convertFragment((String) note.get("content"));
- String commandBody = note.get("attachment") == null ? markdown
- : ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
- Map<String, String> attachment = (Map<String, String>) attachmentObj;
- String attachmentUrl = attachment.get("url");
- String attachmentName = attachment.get("name");
+ String markdown = remarkConverter.convertFragment((String) note.getContent());
+ // combine note text with attachment urls
+ String commandBody = note.getAttachment() == null ? markdown
+ : note.getAttachment().stream().map(attachment -> {
+ String attachmentUrl = attachment.getUrl();
+ String attachmentName = attachment.getName();
return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
}).reduce(markdown, (currentUrl, nextUrl) -> String.format("%s\n%s",
currentUrl, nextUrl));
-
CommandResult result = commandsManager.processCommand(user,
- String.format("#%s %s", postId, commandBody),
+ String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody),
URI.create(StringUtils.EMPTY));
logger.info(jsonMapper.writeValueAsString(result));
if (result.getNewMessage().isPresent()) {
@@ -344,34 +365,6 @@ public class Profile {
} else {
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
}
- } else {
- Message reply = messagesService.getReplyByUri(inReplyTo);
- if (reply != null) {
- User user = new User();
- user.setUri(URI.create(activity.getActor()));
- String markdown = remarkConverter.convertFragment((String) note.get("content"));
- // combine note text with attachment urls
- String commandBody = note.get("attachment") == null ? markdown
- : ((List<Object>) note.get("attachment")).stream()
- .map(attachmentObj -> {
- Map<String, String> attachment = (Map<String, String>) attachmentObj;
- String attachmentUrl = attachment.get("url");
- String attachmentName = attachment.get("name");
- return PlainTextFormatter.markdownUrl(attachmentUrl,
- attachmentName);
- }).reduce(markdown, (currentUrl, nextUrl) -> String
- .format("%s\n%s", currentUrl, nextUrl));
- CommandResult result = commandsManager.processCommand(user,
- String.format("#%d/%d %s", reply.getMid(), reply.getRid(), commandBody),
- URI.create(StringUtils.EMPTY));
- logger.info(jsonMapper.writeValueAsString(result));
- if (result.getNewMessage().isPresent()) {
- messagesService.updateReplyUri(result.getNewMessage().get(), noteId);
- return new ResponseEntity<>(result, HttpStatus.OK);
- } else {
- return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
- }
- }
}
}
}
@@ -379,26 +372,23 @@ public class Profile {
}
}
if (activity instanceof Delete) {
- if (activity.getObject() instanceof String) {
- // Delete gone user
- // TODO: check if it is really deleted and remove copy-paste
- if (activity.getActor().equals(activity.getObject())) {
- return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"),
- HttpStatus.ACCEPTED);
- }
+ // Delete gone user
+ // TODO: check if it is really deleted and remove copy-paste
+ if (activity.getActor().equals(activity.getObject().getUrl())) {
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"),
+ HttpStatus.ACCEPTED);
}
- Map<String, Object> tombstone = (Map<String, Object>) activity.getObject();
- if (tombstone.get("type").equals("Tombstone")) {
+
+ if (activity.getObject() instanceof Tombstone) {
+ Tombstone tombstone = (Tombstone) activity.getObject();
URI actor = URI.create(activity.getActor());
- URI reply = URI.create((String) tombstone.get("id"));
+ URI reply = URI.create((String) tombstone.getId());
messagesService.deleteReply(actor, reply);
return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.OK);
}
}
if (activity instanceof Like || activity instanceof Announce) {
- String messageUri = activity.getObject() instanceof String ? (String) activity.getObject()
- : activity.getObject() instanceof Context ? ((Context) activity.getObject()).getId()
- : (String) ((Map) activity.getObject()).get("id");
+ String messageUri = activity.getObject().getId();
applicationEventPublisher.publishEvent(new AnnounceEvent(this, activity.getActor(), messageUri));
return new ResponseEntity<>(CommandResult.fromString("Like/announce request accepted"), HttpStatus.OK);
}
@@ -411,12 +401,9 @@ public class Profile {
return new ResponseEntity<>(CommandResult.fromString("Unknown activity"), HttpStatus.NOT_IMPLEMENTED);
}
if (activity instanceof Delete) {
- if (activity.getObject() instanceof String) {
- // Delete gone user
- if (activity.getActor().equals(activity.getObject())) {
- return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"),
- HttpStatus.ACCEPTED);
- }
+ // Delete gone user
+ if (activity.getActor().equals(activity.getObject().getId())) {
+ return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED);
}
}
return new ResponseEntity<>(CommandResult.fromString("Can not authenticate"), HttpStatus.UNAUTHORIZED);
diff --git a/src/main/java/com/juick/www/api/activity/helpers/ContextDeserializer.java b/src/main/java/com/juick/www/api/activity/helpers/ContextDeserializer.java
new file mode 100644
index 00000000..367269ef
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/helpers/ContextDeserializer.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008-2021, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.helpers;
+
+import com.fasterxml.jackson.core.JsonParser;
+import com.fasterxml.jackson.core.JsonToken;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonDeserializer;
+import com.juick.www.api.activity.model.Context;
+
+import java.io.IOException;
+
+public class ContextDeserializer extends JsonDeserializer<Context> {
+ @Override
+ public Context deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
+ JsonToken jsonToken = p.getCurrentToken();
+ if (jsonToken == JsonToken.VALUE_STRING) {
+ String id = p.getText();
+ return new Context(id);
+ }
+ return p.readValueAs(Context.class);
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/Activity.java b/src/main/java/com/juick/www/api/activity/model/Activity.java
index d2f905af..7cc0b13f 100644
--- a/src/main/java/com/juick/www/api/activity/model/Activity.java
+++ b/src/main/java/com/juick/www/api/activity/model/Activity.java
@@ -19,6 +19,8 @@ package com.juick.www.api.activity.model;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.juick.www.api.activity.helpers.ActivityIdDeserializer;
+import com.juick.www.api.activity.helpers.ContextDeserializer;
+
import org.apache.commons.lang3.StringUtils;
import java.util.UUID;
@@ -27,7 +29,7 @@ public abstract class Activity extends Context {
@JsonDeserialize(using = ActivityIdDeserializer.class)
private String actor;
- private Object object;
+ private Context object;
public String getActor() {
return actor;
@@ -40,11 +42,12 @@ public abstract class Activity extends Context {
}
}
- public Object getObject() {
+ @JsonDeserialize(using = ContextDeserializer.class)
+ public Context getObject() {
return object;
}
- public void setObject(Object object) {
+ public void setObject(Context object) {
this.object = object;
}
}
diff --git a/src/main/java/com/juick/www/api/activity/model/Context.java b/src/main/java/com/juick/www/api/activity/model/Context.java
index d7b23539..edfa89b1 100644
--- a/src/main/java/com/juick/www/api/activity/model/Context.java
+++ b/src/main/java/com/juick/www/api/activity/model/Context.java
@@ -30,7 +30,7 @@ import java.util.List;
import java.util.Map;
@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property="type")
+@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property="type", defaultImpl = Context.class)
@JsonSubTypes({
@JsonSubTypes.Type(value = Create.class, name = "Create"),
@JsonSubTypes.Type(value = Update.class, name = "Update"),
@@ -55,7 +55,7 @@ import java.util.Map;
@JsonSubTypes.Type(value = Person.class, name = "Person"),
@JsonSubTypes.Type(value = Application.class, name = "Application")
})
-public abstract class Context {
+public class Context {
private List<Object> context;
@@ -120,6 +120,12 @@ public abstract class Context {
return response;
}
+ public Context() {}
+
+ public Context(String id) {
+ setId(id);
+ }
+
public String getUrl() {
return url;
}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Note.java b/src/main/java/com/juick/www/api/activity/model/objects/Note.java
index 66470b5c..7889b172 100644
--- a/src/main/java/com/juick/www/api/activity/model/objects/Note.java
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Note.java
@@ -26,7 +26,7 @@ public class Note extends Context {
private String content;
private String attributedTo;
private String inReplyTo;
- private List<Image> attachment;
+ private List<Context> attachment;
private List<String> to;
private List<String> cc;
private boolean sensitive;
@@ -48,11 +48,11 @@ public class Note extends Context {
}
@JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
- public List<Image> getAttachment() {
+ public List<Context> getAttachment() {
return attachment;
}
- public void setAttachment(List<Image> attachment) {
+ public void setAttachment(List<Context> attachment) {
this.attachment = attachment;
}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Tombstone.java b/src/main/java/com/juick/www/api/activity/model/objects/Tombstone.java
new file mode 100644
index 00000000..1a229299
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Tombstone.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2008-2021, Juick
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.juick.www.api.activity.model.Context;
+
+public class Tombstone extends Context {
+
+} \ No newline at end of file
diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java
index e25f0b0d..c11dfb46 100644
--- a/src/test/java/com/juick/server/tests/ServerTests.java
+++ b/src/test/java/com/juick/server/tests/ServerTests.java
@@ -40,6 +40,7 @@ import com.juick.www.api.activity.Profile;
import com.juick.www.api.activity.model.Context;
import com.juick.www.api.activity.model.activities.*;
import com.juick.www.api.activity.model.objects.Application;
+import com.juick.www.api.activity.model.objects.Image;
import com.juick.www.api.activity.model.objects.Note;
import com.juick.www.api.activity.model.objects.Person;
import com.juick.www.api.webfinger.model.Account;
@@ -1778,12 +1779,12 @@ public class ServerTests {
Person person = (Person) jsonMapper.readValue(personJsonStr, Context.class);
String undoJsonStr = IOUtils.toString(new ClassPathResource("undo.json").getURI(), StandardCharsets.UTF_8);
Undo undo = jsonMapper.readValue(undoJsonStr, Undo.class);
- String undoFollower = (String) ((Map) undo.getObject()).get("object");
+ String undoFollower = undo.getObject().getId();
String createJsonStr = IOUtils.toString(new ClassPathResource("create.json").getURI(), StandardCharsets.UTF_8);
Create create = jsonMapper.readValue(createJsonStr, Create.class);
- Map<String, Object> note = (Map<String, Object>) create.getObject();
- Map<String, Object> attachmentObj = (Map<String, Object>) ((List<Object>) note.get("attachment")).get(0);
- String attachment = attachmentObj != null ? (String) attachmentObj.get("url") : StringUtils.EMPTY;
+ Note note = (Note) create.getObject();
+ Context attachmentObj = note.getAttachment().get(0);
+ String attachment = attachmentObj != null ? (String) attachmentObj.getUrl() : StringUtils.EMPTY;
String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete.json").getURI(), StandardCharsets.UTF_8);
Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class);
int mid = messagesService.createMessage(ugnich.getUid(), "YO", "", null);
@@ -1811,7 +1812,7 @@ public class ServerTests {
String undoPleromaStr = IOUtils.toString(new ClassPathResource("undo_pleroma.json").getURI(),
StandardCharsets.UTF_8);
Undo undoPleroma = jsonMapper.readValue(undoPleromaStr, Undo.class);
- String undoPleromaFollower = (String) ((Map) undoPleroma.getObject()).get("object");
+ String undoPleromaFollower = undoPleroma.getObject().getId();
String deletev3JsonStr = IOUtils.toString(new ClassPathResource("delete_v3.json").getURI(),
StandardCharsets.UTF_8);
Delete deleteObject = jsonMapper.readValue(deletev3JsonStr, Delete.class);
@@ -1850,7 +1851,7 @@ public class ServerTests {
Person to = (Person) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get();
Follow follow = new Follow();
follow.setActor("http://localhost:8080/u/freefd");
- follow.setObject("http://localhost:8080/u/ugnich");
+ follow.setObject(new Context("http://localhost:8080/u/ugnich"));
signatureManager.post(from, to, follow);
}
@@ -2073,9 +2074,9 @@ public class ServerTests {
Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class);
ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
- restServiceServer.expect(times(2), requestTo((String) delete.getObject()))
+ restServiceServer.expect(times(2), requestTo(delete.getObject().getId()))
.andRespond(withStatus(HttpStatus.GONE));
- restServiceServer.expect(requestTo((String) delete.getObject())).andRespond(response -> {
+ restServiceServer.expect(requestTo(delete.getObject().getId())).andRespond(response -> {
throw new ResourceAccessException("Connection reset");
});
mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr))
@@ -2094,7 +2095,7 @@ public class ServerTests {
Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class);
ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
- restServiceServer.expect(requestTo((String) delete.getObject())).andRespond(response -> {
+ restServiceServer.expect(requestTo(delete.getObject().getId())).andRespond(response -> {
throw new ResourceAccessException("Connection reset");
});
mockMvc.perform(post("/api/inbox").contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr).header("Signature",
@@ -2117,13 +2118,12 @@ public class ServerTests {
Announce announce = jsonMapper.readValue(activity, Announce.class);
String noteString = IOUtils.toString(noteWithAttachment.getInputStream(), StandardCharsets.UTF_8);
Create create = jsonMapper.readValue(noteString, Create.class);
- Map<String, Object> note = (Map<String, Object>) create.getObject();
- String markdown = remarkConverter.convertFragment((String) note.get("content"));
- String commandBody = note.get("attachment") == null ? markdown
- : ((List<Object>) note.get("attachment")).stream().map(attachmentObj -> {
- Map<String, String> attachment = (Map<String, String>) attachmentObj;
- String attachmentUrl = attachment.get("url");
- String attachmentName = attachment.get("name");
+ Note note = (Note) create.getObject();
+ String markdown = remarkConverter.convertFragment((String) note.getContent());
+ String commandBody = note.getContent() == null ? markdown
+ : note.getAttachment().stream().map(attachment -> {
+ String attachmentUrl = attachment.getUrl();
+ String attachmentName = attachment.getName();
return PlainTextFormatter.markdownUrl(attachmentUrl, attachmentName);
}).reduce(markdown, (current, next) -> String.format("%s\n%s", current, next));
}