configurable media upload limits Closes #118 See merge request pleroma/pleroma!401tags/v0.9.9
@@ -85,6 +85,9 @@ config :pleroma, :instance, | |||
description: "A Pleroma instance, an alternative fediverse server", | |||
limit: 5000, | |||
upload_limit: 16_000_000, | |||
avatar_upload_limit: 2_000_000, | |||
background_upload_limit: 4_000_000, | |||
banner_upload_limit: 4_000_000, | |||
registrations_open: true, | |||
federating: true, | |||
allow_relay: true, | |||
@@ -4,61 +4,76 @@ defmodule Pleroma.Upload do | |||
@storage_backend Application.get_env(:pleroma, Pleroma.Upload) | |||
|> Keyword.fetch!(:uploader) | |||
def store(%Plug.Upload{} = file, should_dedupe) do | |||
def check_file_size(path, nil), do: true | |||
def check_file_size(path, size_limit) do | |||
{:ok, %{size: size}} = File.stat(path) | |||
size <= size_limit | |||
end | |||
def store(file, should_dedupe, size_limit \\ nil) | |||
def store(%Plug.Upload{} = file, should_dedupe, size_limit) do | |||
content_type = get_content_type(file.path) | |||
uuid = get_uuid(file, should_dedupe) | |||
name = get_name(file, uuid, content_type, should_dedupe) | |||
strip_exif_data(content_type, file.path) | |||
{:ok, url_path} = | |||
@storage_backend.put_file(name, uuid, file.path, content_type, should_dedupe) | |||
%{ | |||
"type" => "Document", | |||
"url" => [ | |||
%{ | |||
"type" => "Link", | |||
"mediaType" => content_type, | |||
"href" => url_path | |||
} | |||
], | |||
"name" => name | |||
} | |||
with uuid <- get_uuid(file, should_dedupe), | |||
name <- get_name(file, uuid, content_type, should_dedupe), | |||
true <- check_file_size(file.path, size_limit) do | |||
strip_exif_data(content_type, file.path) | |||
{:ok, url_path} = | |||
@storage_backend.put_file(name, uuid, file.path, content_type, should_dedupe) | |||
%{ | |||
"type" => "Document", | |||
"url" => [ | |||
%{ | |||
"type" => "Link", | |||
"mediaType" => content_type, | |||
"href" => url_path | |||
} | |||
], | |||
"name" => name | |||
} | |||
else | |||
_e -> nil | |||
end | |||
end | |||
def store(%{"img" => "data:image/" <> image_data}, should_dedupe) do | |||
def store(%{"img" => "data:image/" <> image_data}, should_dedupe, size_limit) do | |||
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data) | |||
data = Base.decode64!(parsed["data"], ignore: :whitespace) | |||
tmp_path = tempfile_for_image(data) | |||
uuid = UUID.generate() | |||
content_type = get_content_type(tmp_path) | |||
strip_exif_data(content_type, tmp_path) | |||
name = | |||
create_name( | |||
String.downcase(Base.encode16(:crypto.hash(:sha256, data))), | |||
parsed["filetype"], | |||
content_type | |||
) | |||
{:ok, url_path} = @storage_backend.put_file(name, uuid, tmp_path, content_type, should_dedupe) | |||
%{ | |||
"type" => "Image", | |||
"url" => [ | |||
%{ | |||
"type" => "Link", | |||
"mediaType" => content_type, | |||
"href" => url_path | |||
} | |||
], | |||
"name" => name | |||
} | |||
with tmp_path <- tempfile_for_image(data), | |||
uuid <- UUID.generate(), | |||
true <- check_file_size(tmp_path, size_limit) do | |||
content_type = get_content_type(tmp_path) | |||
strip_exif_data(content_type, tmp_path) | |||
name = | |||
create_name( | |||
String.downcase(Base.encode16(:crypto.hash(:sha256, data))), | |||
parsed["filetype"], | |||
content_type | |||
) | |||
{:ok, url_path} = | |||
@storage_backend.put_file(name, uuid, tmp_path, content_type, should_dedupe) | |||
%{ | |||
"type" => "Image", | |||
"url" => [ | |||
%{ | |||
"type" => "Link", | |||
"mediaType" => content_type, | |||
"href" => url_path | |||
} | |||
], | |||
"name" => name | |||
} | |||
else | |||
_e -> nil | |||
end | |||
end | |||
@doc """ | |||
@@ -575,9 +575,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do | |||
|> Enum.reverse() | |||
end | |||
def upload(file) do | |||
data = Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media]) | |||
Repo.insert(%Object{data: data}) | |||
def upload(file, size_limit \\ nil) do | |||
with data <- | |||
Upload.store(file, Application.get_env(:pleroma, :instance)[:dedupe_media], size_limit), | |||
false <- is_nil(data) do | |||
Repo.insert(%Object{data: data}) | |||
end | |||
end | |||
def user_data_from_user_object(data) do | |||
@@ -35,6 +35,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
def update_credentials(%{assigns: %{user: user}} = conn, params) do | |||
original_user = user | |||
avatar_upload_limit = | |||
Application.get_env(:pleroma, :instance) | |||
|> Keyword.fetch(:avatar_upload_limit) | |||
banner_upload_limit = | |||
Application.get_env(:pleroma, :instance) | |||
|> Keyword.fetch(:banner_upload_limit) | |||
params = | |||
if bio = params["note"] do | |||
Map.put(params, "bio", bio) | |||
@@ -52,7 +60,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
user = | |||
if avatar = params["avatar"] do | |||
with %Plug.Upload{} <- avatar, | |||
{:ok, object} <- ActivityPub.upload(avatar), | |||
{:ok, object} <- ActivityPub.upload(avatar, avatar_upload_limit), | |||
change = Ecto.Changeset.change(user, %{avatar: object.data}), | |||
{:ok, user} = User.update_and_set_cache(change) do | |||
user | |||
@@ -66,7 +74,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do | |||
user = | |||
if banner = params["header"] do | |||
with %Plug.Upload{} <- banner, | |||
{:ok, object} <- ActivityPub.upload(banner), | |||
{:ok, object} <- ActivityPub.upload(banner, banner_upload_limit), | |||
new_info <- Map.put(user.info, "banner", object.data), | |||
change <- User.info_changeset(user, %{info: new_info}), | |||
{:ok, user} <- User.update_and_set_cache(change) do | |||
@@ -113,6 +113,12 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do | |||
staffAccounts: staff_accounts, | |||
federation: federation_response, | |||
postFormats: Keyword.get(instance, :allowed_post_formats), | |||
uploadLimits: %{ | |||
general: Keyword.get(instance, :upload_limit), | |||
avatar: Keyword.get(instance, :avatar_upload_limit), | |||
banner: Keyword.get(instance, :banner_upload_limit), | |||
background: Keyword.get(instance, :background_upload_limit) | |||
}, | |||
features: features | |||
} | |||
} | |||
@@ -263,7 +263,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
end | |||
def update_avatar(%{assigns: %{user: user}} = conn, params) do | |||
{:ok, object} = ActivityPub.upload(params) | |||
upload_limit = | |||
Application.get_env(:pleroma, :instance) | |||
|> Keyword.fetch(:avatar_upload_limit) | |||
{:ok, object} = ActivityPub.upload(params, upload_limit) | |||
change = Changeset.change(user, %{avatar: object.data}) | |||
{:ok, user} = User.update_and_set_cache(change) | |||
CommonAPI.update(user) | |||
@@ -272,7 +276,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
end | |||
def update_banner(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}), | |||
upload_limit = | |||
Application.get_env(:pleroma, :instance) | |||
|> Keyword.fetch(:banner_upload_limit) | |||
with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, upload_limit), | |||
new_info <- Map.put(user.info, "banner", object.data), | |||
change <- User.info_changeset(user, %{info: new_info}), | |||
{:ok, user} <- User.update_and_set_cache(change) do | |||
@@ -286,7 +294,11 @@ defmodule Pleroma.Web.TwitterAPI.Controller do | |||
end | |||
def update_background(%{assigns: %{user: user}} = conn, params) do | |||
with {:ok, object} <- ActivityPub.upload(params), | |||
upload_limit = | |||
Application.get_env(:pleroma, :instance) | |||
|> Keyword.fetch(:background_upload_limit) | |||
with {:ok, object} <- ActivityPub.upload(params, upload_limit), | |||
new_info <- Map.put(user.info, "background", object.data), | |||
change <- User.info_changeset(user, %{info: new_info}), | |||
{:ok, _user} <- User.update_and_set_cache(change) do | |||