diff --git a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex index a0724ca55..5e377c294 100644 --- a/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex +++ b/lib/pleroma/web/activity_pub/object_validators/article_note_page_validator.ex @@ -156,7 +156,6 @@ defp fix(data) do |> fix_replies() |> fix_source() |> fix_misskey_content() - |> Transmogrifier.fix_quote_url() |> Transmogrifier.fix_attachments() |> Transmogrifier.fix_emoji() |> Transmogrifier.fix_content_map() diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index b6ee24ee6..eea785a55 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -38,6 +38,7 @@ def fix_object(object, options \\ []) do |> fix_attachments() |> fix_context() |> fix_in_reply_to(options) + |> fix_quote_url(options) |> fix_emoji() |> fix_tag() |> fix_content_map() @@ -167,6 +168,50 @@ def fix_in_reply_to(%{"inReplyTo" => in_reply_to} = object, options) def fix_in_reply_to(object, _options), do: object + def fix_quote_url(object, options \\ []) + + def fix_quote_url(%{"quoteUri" => quote_url} = object, options) + when not is_nil(quote_url) do + depth = (options[:depth] || 0) + 1 + + if Federator.allowed_thread_distance?(depth) do + with {:ok, quoted_object} <- get_obj_helper(quote_url, options), + %Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do + object + |> Map.put("quoteUri", quoted_object.data["id"]) + else + e -> + Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}") + object + end + else + object + end + end + + # Soapbox + def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do + object + |> Map.put("quoteUri", quote_url) + |> fix_quote_url(options) + end + + # Old Fedibird (bug) + # https://github.com/fedibird/mastodon/issues/9 + def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do + object + |> Map.put("quoteUri", quote_url) + |> fix_quote_url(options) + end + + def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do + object + |> Map.put("quoteUri", quote_url) + |> fix_quote_url(options) + end + + def fix_quote_url(object, _), do: object + defp prepare_in_reply_to(in_reply_to) do cond do is_bitstring(in_reply_to) -> @@ -424,6 +469,7 @@ def handle_incoming( |> strip_internal_fields() |> fix_type(fetch_options) |> fix_in_reply_to(fetch_options) + |> fix_quote_url(fetch_options) data = Map.put(data, "object", object) options = Keyword.put(options, :local, false) @@ -886,43 +932,6 @@ defp strip_internal_tags(%{"tag" => tags} = object) do defp strip_internal_tags(object), do: object - def fix_quote_url(object, options \\ []) - - def fix_quote_url(%{"quoteUri" => quote_url} = object, options) - when not is_nil(quote_url) do - with {:ok, quoted_object} <- get_obj_helper(quote_url, options), - %Activity{} <- Activity.get_create_by_object_ap_id(quoted_object.data["id"]) do - Map.put(object, "quoteUri", quoted_object.data["id"]) - else - e -> - Logger.warn("Couldn't fetch #{inspect(quote_url)}, error: #{inspect(e)}") - object - end - end - - # Soapbox - def fix_quote_url(%{"quoteUrl" => quote_url} = object, options) do - object - |> Map.put("quoteUri", quote_url) - |> fix_quote_url(options) - end - - # Old Fedibird (bug) - # https://github.com/fedibird/mastodon/issues/9 - def fix_quote_url(%{"quoteURL" => quote_url} = object, options) do - object - |> Map.put("quoteUri", quote_url) - |> fix_quote_url(options) - end - - def fix_quote_url(%{"_misskey_quote" => quote_url} = object, options) do - object - |> Map.put("quoteUri", quote_url) - |> fix_quote_url(options) - end - - def fix_quote_url(object, _), do: object - def perform(:user_upgrade, user) do # we pass a fake user so that the followers collection is stripped away old_follower_address = User.ap_followers(%User{nickname: user.nickname}) diff --git a/test/fixtures/misskey/recursive_quote.json b/test/fixtures/misskey/recursive_quote.json new file mode 100644 index 000000000..f2f66521a --- /dev/null +++ b/test/fixtures/misskey/recursive_quote.json @@ -0,0 +1,48 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "quoteUrl": "as:quoteUrl", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "featured": "toot:featured", + "discoverable": "toot:discoverable", + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "misskey": "https://misskey-hub.net/ns#", + "_misskey_content": "misskey:_misskey_content", + "_misskey_quote": "misskey:_misskey_quote", + "_misskey_reaction": "misskey:_misskey_reaction", + "_misskey_votes": "misskey:_misskey_votes", + "_misskey_talk": "misskey:_misskey_talk", + "isCat": "misskey:isCat", + "vcard": "http://www.w3.org/2006/vcard/ns#" + } + ], + "id": "https://misskey.io/notes/934gok3482", + "type": "Note", + "attributedTo": "https://misskey.io/users/93492q0ip0", + "summary": null, + "content": "

i quompt u

", + "_misskey_content": "i quompt u", + "source": { + "content": "i quompt u", + "mediaType": "text/x.misskeymarkdown" + }, + "_misskey_quote": "https://misskey.io/notes/934gok3482", + "quoteUrl": "https://misskey.io/notes/934gok3482", + "published": "2022-07-25T15:21:48.208Z", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [], + "inReplyTo": null, + "attachment": [], + "sensitive": false, + "tag": [] +} diff --git a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs index 80290a6e3..8b3982916 100644 --- a/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs +++ b/test/pleroma/web/activity_pub/object_validators/article_note_page_validator_test.exs @@ -143,61 +143,5 @@ test "a misskey MFM status with a _misskey_content field should work and be link } } = ArticleNotePageValidator.cast_and_validate(note) end - - test "a misskey quote should work", _ do - Tesla.Mock.mock(fn %{ - method: :get, - url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/quoted_status.json"), - headers: HttpRequestMock.activitypub_object_headers() - } - end) - - insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"}) - insert(:user, %{ap_id: "https://example.com/users/user"}) - - note = - "test/fixtures/misskey/quote.json" - |> File.read!() - |> Jason.decode!() - - %{ - valid?: true, - changes: %{ - quoteUri: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" - } - } = ArticleNotePageValidator.cast_and_validate(note) - end - - test "a fedibird quote should work", _ do - Tesla.Mock.mock(fn %{ - method: :get, - url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" - } -> - %Tesla.Env{ - status: 200, - body: File.read!("test/fixtures/quoted_status.json"), - headers: HttpRequestMock.activitypub_object_headers() - } - end) - - insert(:user, %{ap_id: "https://fedibird.com/users/akkoma_ap_integration_tester"}) - insert(:user, %{ap_id: "https://example.com/users/user"}) - - note = - "test/fixtures/fedibird/quote.json" - |> File.read!() - |> Jason.decode!() - - %{ - valid?: true, - changes: %{ - quoteUri: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" - } - } = ArticleNotePageValidator.cast_and_validate(note) - end end end diff --git a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs index 1846b2291..24df5ea61 100644 --- a/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs +++ b/test/pleroma/web/activity_pub/transmogrifier/note_handling_test.exs @@ -707,4 +707,81 @@ test "take_emoji_tags/1" do } ] end + + describe "fix_quote_url/1" do + test "a misskey quote should work", _ do + Tesla.Mock.mock(fn %{ + method: :get, + url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/quoted_status.json"), + headers: HttpRequestMock.activitypub_object_headers() + } + end) + + insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"}) + insert(:user, %{ap_id: "https://example.com/users/user"}) + + note = + "test/fixtures/misskey/quote.json" + |> File.read!() + |> Jason.decode!() + + %{"quoteUri" => "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924"} = + Transmogrifier.fix_quote_url(note) + end + + test "a fedibird quote should work", _ do + Tesla.Mock.mock(fn %{ + method: :get, + url: "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/quoted_status.json"), + headers: HttpRequestMock.activitypub_object_headers() + } + end) + + insert(:user, %{ap_id: "https://fedibird.com/users/akkoma_ap_integration_tester"}) + insert(:user, %{ap_id: "https://example.com/users/user"}) + + note = + "test/fixtures/fedibird/quote.json" + |> File.read!() + |> Jason.decode!() + + %{ + "quoteUri" => "https://example.com/objects/43479e20-c0f8-4f49-bf7f-13fab8234924" + } = Transmogrifier.fix_quote_url(note) + end + + test "quote fetching should stop after n levels", _ do + clear_config([:instance, :federation_incoming_replies_max_depth], 1) + + Tesla.Mock.mock(fn %{ + method: :get, + url: "https://misskey.io/notes/934gok3482" + } -> + %Tesla.Env{ + status: 200, + body: File.read!("test/fixtures/misskey/recursive_quote.json"), + headers: HttpRequestMock.activitypub_object_headers() + } + end) + + insert(:user, %{ap_id: "https://misskey.io/users/93492q0ip0"}) + + note = + "test/fixtures/misskey/recursive_quote.json" + |> File.read!() + |> Jason.decode!() + + %{ + "quoteUri" => "https://misskey.io/notes/934gok3482" + } = Transmogrifier.fix_quote_url(note) + end + end end