media proxy: good enough wip
authorhref <href@random.sh>
Wed, 22 Nov 2017 18:06:07 +0000 (19:06 +0100)
committerhref <href@random.sh>
Tue, 28 Nov 2017 19:50:34 +0000 (20:50 +0100)
lib/pleroma/web/mastodon_api/views/account_view.ex
lib/pleroma/web/mastodon_api/views/status_view.ex
lib/pleroma/web/media_proxy/controller.ex [new file with mode: 0644]
lib/pleroma/web/media_proxy/media_proxy.ex [new file with mode: 0644]
lib/pleroma/web/router.ex
lib/pleroma/web/twitter_api/representers/object_representer.ex
lib/pleroma/web/twitter_api/views/user_view.ex

index 02f1e60bb889d661883a553c64002c178f8b62b3..1d5918988822400da00be251f1948ca660a77f7e 100644 (file)
@@ -3,6 +3,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   alias Pleroma.User
   alias Pleroma.Web.MastodonAPI.AccountView
   alias Pleroma.Web.CommonAPI.Utils
+  alias Pleroma.Web.MediaProxy
 
   defp image_url(%{"url" => [ %{ "href" => href } | _ ]}), do: href
   defp image_url(_), do: nil
@@ -12,10 +13,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
   end
 
   def render("account.json", %{user: user}) do
-    image = User.avatar_url(user)
+    image = User.avatar_url(user) |> MediaProxy.url()
     user_info = User.user_info(user)
 
-    header = image_url(user.info["banner"]) || "https://placehold.it/700x335"
+    header = (image_url(user.info["banner"]) || "https://placehold.it/700x335") |> MediaProxy.url()
 
     %{
       id: to_string(user.id),
index 5585a560572e582f7bb67483a7e3dd1ba7add741..64f3155975ccf6968a258373fb33a798909451d7 100644 (file)
@@ -3,6 +3,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
   alias Pleroma.Web.MastodonAPI.{AccountView, StatusView}
   alias Pleroma.{User, Activity}
   alias Pleroma.Web.CommonAPI.Utils
+  alias Pleroma.Web.MediaProxy
 
   def render("index.json", opts) do
     render_many(opts.activities, StatusView, "status.json", opts)
@@ -121,9 +122,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
 
     %{
       id: to_string(attachment["id"] || hash_id),
-      url: href,
+      url: MediaProxy.url(href),
       remote_url: href,
-      preview_url: href,
+      preview_url: MediaProxy.url(href),
       text_url: href,
       type: type
     }
diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex
new file mode 100644 (file)
index 0000000..84c6e9c
--- /dev/null
@@ -0,0 +1,49 @@
+defmodule Pleroma.Web.MediaProxy.MediaProxyController do
+  use Pleroma.Web, :controller
+  require Logger
+
+  def remote(conn, %{"sig" => sig, "url" => url}) do
+    {:ok, url} = Pleroma.MediaProxy.decode_url(sig, url)
+    url = url |> URI.encode()
+    case proxy_request(url) do
+      {:ok, content_type, body} ->
+        conn
+        |> put_resp_content_type(content_type)
+        |> set_cache_header(:default)
+        |> send_resp(200, body)
+      other ->
+        conn
+        |> set_cache_header(:error)
+        |> redirect(external: url)
+    end
+  end
+
+  defp proxy_request(link) do
+    instance = )
+    headers = [{"user-agent", "Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{Application.get_env(:pleroma, :instance)[:email]}>"}]
+    options = [:insecure, {:follow_redirect, true}]
+    case :hackney.request(:get, link, headers, "", options) do
+      {:ok, 200, headers, client} ->
+        headers = Enum.into(headers, Map.new)
+        {:ok, body} = :hackney.body(client)
+        {:ok, headers["Content-Type"], body}
+      {:ok, status, _, _} ->
+        Logger.warn "MediaProxy: request failed, status #{status}, link: #{link}"
+        {:error, :bad_status}
+      {:error, error} ->
+        Logger.warn "MediaProxy: request failed, error #{inspect error}, link: #{link}"
+        {:error, error}
+    end
+  end
+
+  @cache_control %{
+    default: "public, max-age=1209600",
+    error:   "public, must-revalidate, max-age=160",
+  }
+
+  defp set_cache_header(conn, true), do: set_cache_header(conn, :default)
+  defp set_cache_header(conn, false), do: set_cache_header(conn, :error)
+  defp set_cache_header(conn, key) when is_atom(key), do: set_cache_header(conn, @cache_control[key])
+  defp set_cache_header(conn, value) when is_binary(value), do: Plug.Conn.put_resp_header(conn, "cache-control", value)
+
+end
diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex
new file mode 100644 (file)
index 0000000..9c1d717
--- /dev/null
@@ -0,0 +1,33 @@
+defmodule Pleroma.Web.MediaProxy do
+  @base64_opts [padding: false]
+  @base64_key Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
+
+  def url(nil), do: nil
+
+  def url(url) do
+    if String.starts_with?(url, Pleroma.Web.base_url) do
+      url
+    else
+      base64 = Base.url_encode64(url, @base64_opts)
+      sig = :crypto.hmac(:sha, @base64_key, base64)
+      sig64 = sig |> Base.url_encode64(@base64_opts)
+      cache_url("#{sig64}/#{base64}")
+    end
+  end
+
+  def decode_url(sig, url) do
+    sig = Base.url_decode64!(sig, @base64_opts)
+    local_sig = :crypto.hmac(:sha, @base64_key, url)
+    if local_sig == sig do
+      {:ok, Base.url_decode64!(url, @base64_opts)}
+    else
+      {:error, :invalid_signature}
+    end
+  end
+
+  defp cache_url(path) do
+    "/proxy/" <> path
+  end
+
+
+end
index 6806e8a75431a4ee9532772e46d1b014867f9e55..799021c2436bb60cb8fa18ea5845247dc9325bc7 100644 (file)
@@ -233,6 +233,14 @@ defmodule Pleroma.Web.Router do
     delete "/auth/sign_out", MastodonAPIController, :logout
   end
 
+  pipeline :remote_media do
+    plug :accepts, ["html"]
+  end
+  scope "/proxy/", Pleroma.Web.MediaProxy do
+    pipe_through :remote_media
+    get "/:sig/:url", MediaProxyController, :remote
+  end
+
   scope "/", Fallback do
     get "/*path", RedirectController, :redirector
   end
index c39b607605421449f49d4015be75920e49bcd7c9..69eaeb36c3d9c60aeb790e5547de70ef558df155 100644 (file)
@@ -6,7 +6,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do
     data = object.data
     url = List.first(data["url"])
     %{
-      url: url["href"],
+      url: url["href"] |> Pleroma.Web.MediaProxy.url(),
       mimetype: url["mediaType"],
       id: data["uuid"],
       oembed: false
index 3dc18eff8f87936f0d1bc9e157adcea8d92f3a69..eeef1c0374d74ae87c7890b658316ec19cdbc7b4 100644 (file)
@@ -2,6 +2,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
   use Pleroma.Web, :view
   alias Pleroma.User
   alias Pleroma.Web.CommonAPI.Utils
+  alias Pleroma.Web.MediaProxy
 
   def render("show.json", %{user: user = %User{}} = assigns) do
     render_one(user, Pleroma.Web.TwitterAPI.UserView, "user.json", assigns)
@@ -12,7 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
   end
 
   def render("user.json", %{user: user = %User{}} = assigns) do
-    image = User.avatar_url(user)
+    image = User.avatar_url(user) |> MediaProxy.url()
     {following, follows_you, statusnet_blocking} = if assigns[:for] do
       {
         User.following?(assigns[:for], user),
@@ -44,8 +45,8 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
       "screen_name" => user.nickname,
       "statuses_count" => user_info[:note_count],
       "statusnet_profile_url" => user.ap_id,
-      "cover_photo" => image_url(user.info["banner"]),
-      "background_image" => image_url(user.info["background"])
+      "cover_photo" => image_url(user.info["banner"]) |> MediaProxy.url(),
+      "background_image" => image_url(user.info["background"]) |> MediaProxy.url(),
     }
   end