diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 78543b426..50e7e7ccd 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -22,6 +22,7 @@ defmodule Pleroma.User do alias Pleroma.Web.OAuth alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.ActivityPub + alias Pleroma.Web.RelMe require Logger @@ -1232,8 +1233,14 @@ def parse_bio(bio, user) do {String.trim(name, ":"), url} end) + # TODO: get profile URLs other than user.ap_id + profile_urls = [user.ap_id] + bio - |> CommonUtils.format_input("text/plain", mentions_format: :full) + |> CommonUtils.format_input("text/plain", + mentions_format: :full, + rel: &RelMe.maybe_put_rel_me(&1, profile_urls) + ) |> elem(0) |> Formatter.emojify(emoji) end diff --git a/lib/pleroma/web/rel_me.ex b/lib/pleroma/web/rel_me.ex new file mode 100644 index 000000000..a07db966f --- /dev/null +++ b/lib/pleroma/web/rel_me.ex @@ -0,0 +1,51 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RelMe do + @hackney_options [ + pool: :media, + timeout: 2_000, + recv_timeout: 2_000, + max_body: 2_000_000 + ] + + if Mix.env() == :test do + def parse(url) when is_binary(url), do: parse_url(url) + else + def parse(url) when is_binary(url) do + Cachex.fetch!(:rel_me_cache, url, fn _ -> + {:commit, parse_url(url)} + end) + rescue + e -> {:error, "Cachex error: #{inspect(e)}"} + end + end + + def parse(_), do: {:error, "No URL provided"} + + defp parse_url(url) do + {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url, [], adapter: @hackney_options) + + data = + Floki.attribute(html, "link[rel=me]", "href") ++ Floki.attribute(html, "a[rel=me]", "href") + + {:ok, data} + rescue + e -> {:error, "Parsing error: #{inspect(e)}"} + end + + def maybe_put_rel_me("http" <> _ = target_page, profile_urls) when is_list(profile_urls) do + {:ok, rel_me_hrefs} = parse(target_page) + + true = Enum.any?(rel_me_hrefs, fn x -> x in profile_urls end) + + "me" + rescue + _ -> nil + end + + def maybe_put_rel_me(_, _) do + nil + end +end diff --git a/test/fixtures/rel_me_anchor.html b/test/fixtures/rel_me_anchor.html new file mode 100644 index 000000000..5abcce129 --- /dev/null +++ b/test/fixtures/rel_me_anchor.html @@ -0,0 +1,14 @@ + + + + + Blog + + +
+

Lorem ipsum

+

Lorem ipsum dolor sit ameph, …

+ lain’s account +
+ + diff --git a/test/fixtures/rel_me_link.html b/test/fixtures/rel_me_link.html new file mode 100644 index 000000000..b9ff18f6e --- /dev/null +++ b/test/fixtures/rel_me_link.html @@ -0,0 +1,14 @@ + + + + + Blog + + + +
+

Lorem ipsum

+

Lorem ipsum dolor sit ameph, …

+
+ + diff --git a/test/fixtures/rel_me_null.html b/test/fixtures/rel_me_null.html new file mode 100644 index 000000000..57d424b80 --- /dev/null +++ b/test/fixtures/rel_me_null.html @@ -0,0 +1,13 @@ + + + + + Blog + + +
+

Lorem ipsum

+

Lorem ipsum dolor sit ameph, …

+
+ + diff --git a/test/user_test.exs b/test/user_test.exs index 390e3ef13..3a5f3c240 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -1054,6 +1054,22 @@ test "preserves hosts in user links text" do assert expected_text == User.parse_bio(bio, user) end + + test "Adds rel=me on linkbacked urls" do + user = insert(:user, ap_id: "http://social.example.org/users/lain") + + bio = "http://example.org/rel_me/null" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + + bio = "http://example.org/rel_me/link" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + + bio = "http://example.org/rel_me/anchor" + expected_text = "#{bio}" + assert expected_text == User.parse_bio(bio, user) + end end test "bookmarks" do diff --git a/test/web/rel_me_test.exs b/test/web/rel_me_test.exs new file mode 100644 index 000000000..ba8038e69 --- /dev/null +++ b/test/web/rel_me_test.exs @@ -0,0 +1,55 @@ +defmodule Pleroma.Web.RelMeTest do + use ExUnit.Case, async: true + + setup do + Tesla.Mock.mock(fn + %{ + method: :get, + url: "http://example.com/rel_me/anchor" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_anchor.html")} + + %{ + method: :get, + url: "http://example.com/rel_me/link" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_link.html")} + + %{ + method: :get, + url: "http://example.com/rel_me/null" + } -> + %Tesla.Env{status: 200, body: File.read!("test/fixtures/rel_me_null.html")} + end) + + :ok + end + + test "parse/1" do + hrefs = ["https://social.example.org/users/lain"] + + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/null") == {:ok, []} + assert {:error, _} = Pleroma.Web.RelMe.parse("http://example.com/rel_me/error") + + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/link") == {:ok, hrefs} + assert Pleroma.Web.RelMe.parse("http://example.com/rel_me/anchor") == {:ok, hrefs} + end + + test "maybe_put_rel_me/2" do + profile_urls = ["https://social.example.org/users/lain"] + attr = "me" + fallback = nil + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/null", profile_urls) == + fallback + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/error", profile_urls) == + fallback + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/anchor", profile_urls) == + attr + + assert Pleroma.Web.RelMe.maybe_put_rel_me("http://example.com/rel_me/link", profile_urls) == + attr + end +end