X-Git-Url: http://git.squeep.com/?a=blobdiff_plain;ds=sidebyside;f=lib%2Fpleroma%2Fweb%2Fmedia_proxy%2Fmedia_proxy_controller.ex;h=d465ce8d10e530b036bbf789e986ffdc3bf7bcab;hb=2cfe2dc81b82f0ab30d82763f2874010966acae6;hp=8403850ff1ca9b7c2f9ee9c56ffe93bab3f666a5;hpb=9570a5be405c4cf4f07907eab4ced21e07fa20c9;p=akkoma
diff --git a/lib/pleroma/web/media_proxy/media_proxy_controller.ex b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
index 8403850ff..d465ce8d1 100644
--- a/lib/pleroma/web/media_proxy/media_proxy_controller.ex
+++ b/lib/pleroma/web/media_proxy/media_proxy_controller.ex
@@ -1,22 +1,26 @@
# Pleroma: A lightweight social networking server
-# Copyright © 2017-2019 Pleroma Authors
+# Copyright © 2017-2020 Pleroma Authors
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.MediaProxy.MediaProxyController do
use Pleroma.Web, :controller
+
+ alias Pleroma.Config
+ alias Pleroma.Helpers.MediaHelper
alias Pleroma.ReverseProxy
alias Pleroma.Web.MediaProxy
- @default_proxy_opts [max_body_length: 25 * 1_048_576, http: [follow_redirect: true]]
-
- def remote(conn, %{"sig" => sig64, "url" => url64} = params) do
- with config <- Pleroma.Config.get([:media_proxy], []),
- true <- Keyword.get(config, :enabled, false),
+ def remote(conn, %{"sig" => sig64, "url" => url64}) do
+ with {_, true} <- {:enabled, MediaProxy.enabled?()},
{:ok, url} <- MediaProxy.decode_url(sig64, url64),
- :ok <- filename_matches(params, conn.request_path, url) do
- ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
+ {_, false} <- {:in_banned_urls, MediaProxy.in_banned_urls(url)},
+ :ok <- MediaProxy.verify_request_path_and_url(conn, url) do
+ ReverseProxy.call(conn, url, media_proxy_opts())
else
- false ->
+ {:enabled, false} ->
+ send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
+
+ {:in_banned_urls, true} ->
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
{:error, :invalid_signature} ->
@@ -27,20 +31,110 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
end
end
- def filename_matches(%{"filename" => _} = _, path, url) do
- filename = MediaProxy.filename(url)
+ def preview(conn, %{"sig" => sig64, "url" => url64}) do
+ with {_, true} <- {:enabled, MediaProxy.preview_enabled?()},
+ {:ok, url} <- MediaProxy.decode_url(sig64, url64),
+ :ok <- MediaProxy.verify_request_path_and_url(conn, url) do
+ handle_preview(conn, url)
+ else
+ {:enabled, false} ->
+ send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
+
+ {:error, :invalid_signature} ->
+ send_resp(conn, 403, Plug.Conn.Status.reason_phrase(403))
+
+ {:wrong_filename, filename} ->
+ redirect(conn, external: MediaProxy.build_preview_url(sig64, url64, filename))
+ end
+ end
- if filename && does_not_match(path, filename) do
- {:wrong_filename, filename}
+ defp handle_preview(conn, url) do
+ with {:ok, %{status: status} = head_response} when status in 200..299 <-
+ Tesla.head(url, opts: [adapter: [timeout: preview_head_request_timeout()]]) do
+ content_type = Tesla.get_header(head_response, "content-type")
+ handle_preview(content_type, conn, url)
else
- :ok
+ {_, %{status: status}} ->
+ send_resp(conn, :failed_dependency, "Can't fetch HTTP headers (HTTP #{status}).")
+
+ {:error, :recv_response_timeout} ->
+ send_resp(conn, :failed_dependency, "HEAD request timeout.")
+
+ _ ->
+ send_resp(conn, :failed_dependency, "Can't fetch HTTP headers.")
end
end
- def filename_matches(_, _, _), do: :ok
+ defp handle_preview("image/gif" = _content_type, conn, url) do
+ mediaproxy_url = url |> MediaProxy.url()
+
+ redirect(conn, external: mediaproxy_url)
+ end
+
+ defp handle_preview("image/" <> _ = _content_type, conn, url) do
+ handle_image_preview(conn, url)
+ end
+
+ defp handle_preview("video/" <> _ = _content_type, conn, url) do
+ mediaproxy_url = url |> MediaProxy.url()
+
+ redirect(conn, external: mediaproxy_url)
+ end
+
+ defp handle_preview(content_type, conn, _url) do
+ send_resp(conn, :unprocessable_entity, "Unsupported content type: #{content_type}.")
+ end
+
+ defp handle_image_preview(%{params: params} = conn, url) do
+ quality = Config.get!([:media_preview_proxy, :image_quality])
+
+ with {thumbnail_max_width, thumbnail_max_height} <- thumbnail_max_dimensions(params),
+ {:ok, thumbnail_binary} <-
+ MediaHelper.image_resize(
+ url,
+ %{max_width: thumbnail_max_width, max_height: thumbnail_max_height, quality: quality}
+ ) do
+ conn
+ |> put_resp_header("content-type", "image/jpeg")
+ |> put_resp_header("content-disposition", "inline; filename=\"preview.jpg\"")
+ |> send_resp(200, thumbnail_binary)
+ else
+ _ ->
+ send_resp(conn, :failed_dependency, "Can't handle preview.")
+ end
+ end
+
+ defp thumbnail_max_dimensions(params) do
+ config = Config.get([:media_preview_proxy], [])
+
+ thumbnail_max_width =
+ if w = params["thumbnail_max_width"] do
+ String.to_integer(w)
+ else
+ Keyword.fetch!(config, :thumbnail_max_width)
+ end
+
+ thumbnail_max_height =
+ if h = params["thumbnail_max_height"] do
+ String.to_integer(h)
+ else
+ Keyword.fetch!(config, :thumbnail_max_height)
+ end
+
+ {thumbnail_max_width, thumbnail_max_height}
+ end
+
+ defp preview_head_request_timeout do
+ Keyword.get(media_preview_proxy_opts(), :head_request_max_read_duration) ||
+ Keyword.get(media_proxy_opts(), :max_read_duration) ||
+ ReverseProxy.max_read_duration_default()
+ end
+
+ defp media_proxy_opts do
+ Config.get([:media_proxy, :proxy_opts], [])
+ end
- defp does_not_match(path, filename) do
- basename = Path.basename(path)
- basename != filename and URI.decode(basename) != filename and URI.encode(basename) != filename
+ defp media_preview_proxy_opts do
+ Config.get([:media_preview_proxy, :proxy_opts], [])
end
end