diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/pleroma/web/mastodon_api/views/status_view.ex | 32 | ||||
-rw-r--r-- | lib/pleroma/web/plugs/http_security_plug.ex | 3 | ||||
-rw-r--r-- | lib/pleroma/web/rich_media/helpers.ex | 22 | ||||
-rw-r--r-- | lib/pleroma/web/rich_media/parser.ex | 32 | ||||
-rw-r--r-- | lib/pleroma/web/rich_media/parser/card.ex | 75 |
5 files changed, 126 insertions, 38 deletions
diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index bac897a57..5eb09f0a1 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -367,36 +367,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do nil end - def render("card.json", %{rich_media: rich_media, page_url: page_url}) do - page_url_data = URI.parse(page_url) - - page_url_data = - if is_binary(rich_media["url"]) do - URI.merge(page_url_data, URI.parse(rich_media["url"])) - else - page_url_data - end - - page_url = page_url_data |> to_string - - image_url = - if is_binary(rich_media["image"]) do - URI.merge(page_url_data, URI.parse(rich_media["image"])) - |> to_string - end - - %{ - type: "link", - provider_name: page_url_data.host, - provider_url: page_url_data.scheme <> "://" <> page_url_data.host, - url: page_url, - image: image_url |> MediaProxy.url(), - title: rich_media["title"] || "", - description: rich_media["description"] || "", - pleroma: %{ - opengraph: rich_media - } - } + def render("card.json", %{rich_media: rich_media, page_url: _page_url}) do + rich_media end def render("card.json", _), do: nil diff --git a/lib/pleroma/web/plugs/http_security_plug.ex b/lib/pleroma/web/plugs/http_security_plug.ex index 0025b042a..7be45dce8 100644 --- a/lib/pleroma/web/plugs/http_security_plug.ex +++ b/lib/pleroma/web/plugs/http_security_plug.ex @@ -79,7 +79,8 @@ defmodule Pleroma.Web.Plugs.HTTPSecurityPlug do "frame-ancestors 'none'", "style-src 'self' 'unsafe-inline'", "font-src 'self'", - "manifest-src 'self'" + "manifest-src 'self'", + "frame-src 'self' https:" ] @csp_start [Enum.join(static_csp_rules, ";") <> ";"] diff --git a/lib/pleroma/web/rich_media/helpers.ex b/lib/pleroma/web/rich_media/helpers.ex index 566fc8c8a..249730aea 100644 --- a/lib/pleroma/web/rich_media/helpers.ex +++ b/lib/pleroma/web/rich_media/helpers.ex @@ -8,6 +8,7 @@ defmodule Pleroma.Web.RichMedia.Helpers do alias Pleroma.HTML alias Pleroma.Object alias Pleroma.Web.RichMedia.Parser + alias Pleroma.Web.RichMedia.Parser.Card @options [ pool: :media, @@ -15,6 +16,8 @@ defmodule Pleroma.Web.RichMedia.Helpers do recv_timeout: 2_000 ] + @headers [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}] + @spec validate_page_url(URI.t() | binary()) :: :ok | :error defp validate_page_url(page_url) when is_binary(page_url) do validate_tld = Config.get([Pleroma.Formatter, :validate_tld]) @@ -55,12 +58,23 @@ defmodule Pleroma.Web.RichMedia.Helpers do |> hd end + defp strip_card(%Card{} = card) do + card + |> Map.from_struct() + |> Map.new(fn {k, v} -> {Atom.to_string(k), v} end) + end + + defp strip_card(%{} = card) do + Map.new(card, fn {k, v} -> {Atom.to_string(k), v} end) + end + def fetch_data_for_object(object) do with true <- Config.get([:rich_media, :enabled]), {:ok, page_url} <- HTML.extract_first_external_url_from_object(object), :ok <- validate_page_url(page_url), - {:ok, rich_media} <- Parser.parse(page_url) do + {:ok, rich_media} <- Parser.parse(page_url), + rich_media <- strip_card(rich_media) do %{page_url: page_url, rich_media: rich_media} else _ -> %{} @@ -78,8 +92,12 @@ defmodule Pleroma.Web.RichMedia.Helpers do def fetch_data_for_activity(_), do: %{} + def oembed_get(url) do + Pleroma.HTTP.get(url, @headers, @options) + end + def rich_media_get(url) do - headers = [{"user-agent", Pleroma.Application.user_agent() <> "; Bot"}] + headers = @headers head_check = case Pleroma.HTTP.head(url, headers, @options) do diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex index d6b54943b..d7b0cab6f 100644 --- a/lib/pleroma/web/rich_media/parser.ex +++ b/lib/pleroma/web/rich_media/parser.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.RichMedia.Parser do require Logger + alias Pleroma.Web.RichMedia.Parser.Card @cachex Pleroma.Config.get([:cachex, :provider], Cachex) @@ -131,13 +132,34 @@ defmodule Pleroma.Web.RichMedia.Parser do end def parse_url(url) do + case maybe_fetch_oembed(url) do + {:ok, %Card{} = card} -> {:ok, card} + _ -> fetch_document(url) + end + end + + defp maybe_fetch_oembed(url) do + with {:ok, oembed_url} <- OEmbedProviders.oembed_url(url), + {:ok, %Tesla.Env{body: json}} <- + Pleroma.Web.RichMedia.Helpers.oembed_get(oembed_url), + {:ok, data} <- Jason.decode(json), + %Card{} = card <- Card.from_oembed(data, url) do + {:ok, card} + else + {:error, error} -> {:error, error} + error -> {:error, error} + end + end + + defp fetch_document(url) do with {:ok, %Tesla.Env{body: html}} <- Pleroma.Web.RichMedia.Helpers.rich_media_get(url), {:ok, html} <- Floki.parse_document(html) do html |> maybe_parse() |> Map.put("url", url) |> clean_parsed_data() - |> check_parsed_data() + |> Card.from_meta_tags(url) + |> check_card() end end @@ -150,13 +172,13 @@ defmodule Pleroma.Web.RichMedia.Parser do end) end - defp check_parsed_data(%{"title" => title} = data) + defp check_card(%Card{title: title} = card) when is_binary(title) and title != "" do - {:ok, data} + {:ok, card} end - defp check_parsed_data(data) do - {:error, {:invalid_metadata, data}} + defp check_card(card) do + {:error, {:invalid_metadata, card}} end defp clean_parsed_data(data) do diff --git a/lib/pleroma/web/rich_media/parser/card.ex b/lib/pleroma/web/rich_media/parser/card.ex new file mode 100644 index 000000000..5ea719485 --- /dev/null +++ b/lib/pleroma/web/rich_media/parser/card.ex @@ -0,0 +1,75 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.RichMedia.Parser.Card do + @types ["link", "photo", "video", "rich"] + + # https://docs.joinmastodon.org/entities/card/ + defstruct url: nil, + title: nil, + description: "", + type: "link", + author_name: "", + author_url: "", + provider_name: "", + provider_url: "", + html: "", + width: 0, + height: 0, + image: nil, + embed_url: "", + blurhash: nil + + def from_oembed(%{"type" => type, "title" => title} = oembed, url) when type in @types do + %__MODULE__{ + url: url, + title: title, + description: "", + type: type, + author_name: oembed["author_name"], + author_url: oembed["author_url"], + provider_name: oembed["provider_name"], + provider_url: oembed["provider_url"], + html: oembed["html"], + width: oembed["width"], + height: oembed["height"], + image: oembed["thumbnail_url"] |> proxy(), + embed_url: oembed["url"] |> proxy() + } + end + + def from_oembed(_oembed, _url), do: nil + + def from_meta_tags(rich_media, page_url) do + page_url_data = URI.parse(page_url) + + page_url_data = + if is_binary(rich_media["url"]) do + URI.merge(page_url_data, URI.parse(rich_media["url"])) + else + page_url_data + end + + page_url = page_url_data |> to_string + + image_url = + if is_binary(rich_media["image"]) do + URI.merge(page_url_data, URI.parse(rich_media["image"])) + |> to_string + end + + %__MODULE__{ + type: "link", + provider_name: page_url_data.host, + provider_url: page_url_data.scheme <> "://" <> page_url_data.host, + url: page_url, + image: image_url |> proxy(), + title: rich_media["title"] || "", + description: rich_media["description"] || "" + } + end + + defp proxy(url) when is_binary(url), do: Pleroma.Web.MediaProxy.url(url) + defp proxy(_), do: nil +end |