@@ -22,14 +22,14 @@ defmodule Pleroma.Emoji.Pack do | |||||
alias Pleroma.Emoji | alias Pleroma.Emoji | ||||
alias Pleroma.Emoji.Pack | alias Pleroma.Emoji.Pack | ||||
alias Pleroma.Utils | |||||
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} | @spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} | ||||
def create(name) do | def create(name) do | ||||
with :ok <- validate_not_empty([name]), | with :ok <- validate_not_empty([name]), | ||||
dir <- Path.join(emoji_path(), name), | dir <- Path.join(emoji_path(), name), | ||||
:ok <- File.mkdir(dir) do | :ok <- File.mkdir(dir) do | ||||
%__MODULE__{pack_file: Path.join(dir, "pack.json")} | |||||
|> save_pack() | |||||
save_pack(%__MODULE__{pack_file: Path.join(dir, "pack.json")}) | |||||
end | end | ||||
end | end | ||||
@@ -94,7 +94,7 @@ defmodule Pleroma.Emoji.Pack do | |||||
def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do | def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do | ||||
with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), | with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), | ||||
[_ | _] = emojies <- unpack_zip_emojies(zip_files), | [_ | _] = emojies <- unpack_zip_emojies(zip_files), | ||||
{:ok, tmp_dir} <- Pleroma.Utils.tmp_dir("emoji") do | |||||
{:ok, tmp_dir} <- Utils.tmp_dir("emoji") do | |||||
try do | try do | ||||
{:ok, _emoji_files} = | {:ok, _emoji_files} = | ||||
:zip.unzip( | :zip.unzip( | ||||
@@ -282,18 +282,21 @@ defmodule Pleroma.Emoji.Pack do | |||||
end | end | ||||
end | end | ||||
@spec load_pack(String.t()) :: {:ok, t()} | {:error, :not_found} | |||||
@spec load_pack(String.t()) :: {:ok, t()} | {:error, :file.posix()} | |||||
def load_pack(name) do | def load_pack(name) do | ||||
pack_file = Path.join([emoji_path(), name, "pack.json"]) | pack_file = Path.join([emoji_path(), name, "pack.json"]) | ||||
if File.exists?(pack_file) do | |||||
with {:ok, _} <- File.stat(pack_file), | |||||
{:ok, pack_data} <- File.read(pack_file) do | |||||
pack = | pack = | ||||
pack_file | |||||
|> File.read!() | |||||
|> from_json() | |||||
|> Map.put(:pack_file, pack_file) | |||||
|> Map.put(:path, Path.dirname(pack_file)) | |||||
|> Map.put(:name, name) | |||||
from_json( | |||||
pack_data, | |||||
%{ | |||||
pack_file: pack_file, | |||||
path: Path.dirname(pack_file), | |||||
name: name | |||||
} | |||||
) | |||||
files_count = | files_count = | ||||
pack.files | pack.files | ||||
@@ -301,8 +304,6 @@ defmodule Pleroma.Emoji.Pack do | |||||
|> length() | |> length() | ||||
{:ok, Map.put(pack, :files_count, files_count)} | {:ok, Map.put(pack, :files_count, files_count)} | ||||
else | |||||
{:error, :not_found} | |||||
end | end | ||||
end | end | ||||
@@ -434,10 +435,17 @@ defmodule Pleroma.Emoji.Pack do | |||||
end | end | ||||
end | end | ||||
defp from_json(json) do | |||||
defp from_json(json, attrs) do | |||||
map = Jason.decode!(json) | map = Jason.decode!(json) | ||||
struct(__MODULE__, %{files: map["files"], pack: map["pack"]}) | |||||
pack_attrs = | |||||
attrs | |||||
|> Map.merge(%{ | |||||
files: map["files"], | |||||
pack: map["pack"] | |||||
}) | |||||
struct(__MODULE__, pack_attrs) | |||||
end | end | ||||
defp validate_shareable_packs_available(uri) do | defp validate_shareable_packs_available(uri) do | ||||
@@ -491,10 +499,10 @@ defmodule Pleroma.Emoji.Pack do | |||||
end | end | ||||
defp create_subdirs(file_path) do | defp create_subdirs(file_path) do | ||||
if String.contains?(file_path, "/") do | |||||
file_path | |||||
|> Path.dirname() | |||||
|> File.mkdir_p!() | |||||
with true <- String.contains?(file_path, "/"), | |||||
path <- Path.dirname(file_path), | |||||
false <- File.exists?(path) do | |||||
File.mkdir_p!(path) | |||||
end | end | ||||
end | end | ||||
@@ -518,10 +526,15 @@ defmodule Pleroma.Emoji.Pack do | |||||
defp get_filename(pack, shortcode) do | defp get_filename(pack, shortcode) do | ||||
with %{^shortcode => filename} when is_binary(filename) <- pack.files, | with %{^shortcode => filename} when is_binary(filename) <- pack.files, | ||||
true <- pack.path |> Path.join(filename) |> File.exists?() do | |||||
file_path <- Path.join(pack.path, filename), | |||||
{:ok, _} <- File.stat(file_path) do | |||||
{:ok, filename} | {:ok, filename} | ||||
else | else | ||||
_ -> {:error, :doesnt_exist} | |||||
{:error, _} = error -> | |||||
error | |||||
_ -> | |||||
{:error, :doesnt_exist} | |||||
end | end | ||||
end | end | ||||
@@ -3,6 +3,14 @@ | |||||
# SPDX-License-Identifier: AGPL-3.0-only | # SPDX-License-Identifier: AGPL-3.0-only | ||||
defmodule Pleroma.Utils do | defmodule Pleroma.Utils do | ||||
@posix_error_codes ~w( | |||||
eacces eagain ebadf ebadmsg ebusy edeadlk edeadlock edquot eexist efault | |||||
efbig eftype eintr einval eio eisdir eloop emfile emlink emultihop | |||||
enametoolong enfile enobufs enodev enolck enolink enoent enomem enospc | |||||
enosr enostr enosys enotblk enotdir enotsup enxio eopnotsupp eoverflow | |||||
eperm epipe erange erofs espipe esrch estale etxtbsy exdev | |||||
)a | |||||
def compile_dir(dir) when is_binary(dir) do | def compile_dir(dir) when is_binary(dir) do | ||||
dir | dir | ||||
|> File.ls!() | |> File.ls!() | ||||
@@ -44,4 +52,12 @@ defmodule Pleroma.Utils do | |||||
error -> error | error -> error | ||||
end | end | ||||
end | end | ||||
@spec posix_error_message(atom()) :: binary() | |||||
def posix_error_message(code) when code in @posix_error_codes do | |||||
error_message = Gettext.dgettext(Pleroma.Web.Gettext, "posix_errors", "#{code}") | |||||
"(POSIX error: #{error_message})" | |||||
end | |||||
def posix_error_message(_), do: "" | |||||
end | end |
@@ -42,7 +42,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||||
|> json(%{error: "pack name, shortcode or filename cannot be empty"}) | |> json(%{error: "pack name, shortcode or filename cannot be empty"}) | ||||
{:error, _} = error -> | {:error, _} = error -> | ||||
handle_error(conn, error, %{pack_name: pack_name}) | |||||
handle_error(conn, error, %{ | |||||
pack_name: pack_name, | |||||
message: "Unexpected error occurred while adding file to pack." | |||||
}) | |||||
end | end | ||||
end | end | ||||
@@ -69,7 +72,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||||
|> json(%{error: "new_shortcode or new_filename cannot be empty"}) | |> json(%{error: "new_shortcode or new_filename cannot be empty"}) | ||||
{:error, _} = error -> | {:error, _} = error -> | ||||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) | |||||
handle_error(conn, error, %{ | |||||
pack_name: pack_name, | |||||
code: shortcode, | |||||
message: "Unexpected error occurred while updating." | |||||
}) | |||||
end | end | ||||
end | end | ||||
@@ -84,7 +91,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||||
|> json(%{error: "pack name or shortcode cannot be empty"}) | |> json(%{error: "pack name or shortcode cannot be empty"}) | ||||
{:error, _} = error -> | {:error, _} = error -> | ||||
handle_error(conn, error, %{pack_name: pack_name, code: shortcode}) | |||||
handle_error(conn, error, %{ | |||||
pack_name: pack_name, | |||||
code: shortcode, | |||||
message: "Unexpected error occurred while deleting emoji file." | |||||
}) | |||||
end | end | ||||
end | end | ||||
@@ -94,18 +105,24 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||||
|> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) | |> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) | ||||
end | end | ||||
defp handle_error(conn, {:error, :not_found}, %{pack_name: pack_name}) do | |||||
defp handle_error(conn, {:error, :enoent}, %{pack_name: pack_name}) do | |||||
conn | conn | ||||
|> put_status(:not_found) | |> put_status(:not_found) | ||||
|> json(%{error: "pack \"#{pack_name}\" is not found"}) | |> json(%{error: "pack \"#{pack_name}\" is not found"}) | ||||
end | end | ||||
defp handle_error(conn, {:error, _}, _) do | |||||
render_error( | |||||
conn, | |||||
:internal_server_error, | |||||
"Unexpected error occurred while adding file to pack." | |||||
) | |||||
defp handle_error(conn, {:error, error}, opts) do | |||||
message = | |||||
[ | |||||
Map.get(opts, :message, "Unexpected error occurred."), | |||||
Pleroma.Utils.posix_error_message(error) | |||||
] | |||||
|> Enum.join(" ") | |||||
|> String.trim() | |||||
conn | |||||
|> put_status(:internal_server_error) | |||||
|> json(%{error: message}) | |||||
end | end | ||||
defp get_filename(%Plug.Upload{filename: filename}), do: filename | defp get_filename(%Plug.Upload{filename: filename}), do: filename | ||||
@@ -71,7 +71,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do | with {:ok, pack} <- Pack.show(name: name, page: page, page_size: page_size) do | ||||
json(conn, pack) | json(conn, pack) | ||||
else | else | ||||
{:error, :not_found} -> | |||||
{:error, :enoent} -> | |||||
conn | conn | ||||
|> put_status(:not_found) | |> put_status(:not_found) | ||||
|> json(%{error: "Pack #{name} does not exist"}) | |> json(%{error: "Pack #{name} does not exist"}) | ||||
@@ -80,6 +80,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
conn | conn | ||||
|> put_status(:bad_request) | |> put_status(:bad_request) | ||||
|> json(%{error: "pack name cannot be empty"}) | |> json(%{error: "pack name cannot be empty"}) | ||||
{:error, error} -> | |||||
error_message = | |||||
add_posix_error( | |||||
"Failed to get the contents of the `#{name}` pack.", | |||||
error | |||||
) | |||||
conn | |||||
|> put_status(:internal_server_error) | |||||
|> json(%{error: error_message}) | |||||
end | end | ||||
end | end | ||||
@@ -95,7 +106,7 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
"Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" | "Pack #{name} cannot be downloaded from this instance, either pack sharing was disabled for this pack or some files are missing" | ||||
}) | }) | ||||
{:error, :not_found} -> | |||||
{:error, :enoent} -> | |||||
conn | conn | ||||
|> put_status(:not_found) | |> put_status(:not_found) | ||||
|> json(%{error: "Pack #{name} does not exist"}) | |> json(%{error: "Pack #{name} does not exist"}) | ||||
@@ -116,10 +127,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
|> put_status(:internal_server_error) | |> put_status(:internal_server_error) | ||||
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) | |> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) | ||||
{:error, e} -> | |||||
{:error, error} -> | |||||
conn | conn | ||||
|> put_status(:internal_server_error) | |> put_status(:internal_server_error) | ||||
|> json(%{error: e}) | |||||
|> json(%{error: error}) | |||||
end | end | ||||
end | end | ||||
@@ -139,12 +150,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
|> put_status(:bad_request) | |> put_status(:bad_request) | ||||
|> json(%{error: "pack name cannot be empty"}) | |> json(%{error: "pack name cannot be empty"}) | ||||
{:error, _} -> | |||||
render_error( | |||||
conn, | |||||
:internal_server_error, | |||||
"Unexpected error occurred while creating pack." | |||||
) | |||||
{:error, error} -> | |||||
error_message = | |||||
add_posix_error( | |||||
"Unexpected error occurred while creating pack.", | |||||
error | |||||
) | |||||
conn | |||||
|> put_status(:internal_server_error) | |||||
|> json(%{error: error_message}) | |||||
end | end | ||||
end | end | ||||
@@ -164,10 +179,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
|> put_status(:bad_request) | |> put_status(:bad_request) | ||||
|> json(%{error: "pack name cannot be empty"}) | |> json(%{error: "pack name cannot be empty"}) | ||||
{:error, _, _} -> | |||||
{:error, error, _} -> | |||||
error_message = add_posix_error("Couldn't delete the pack #{name}", error) | |||||
conn | conn | ||||
|> put_status(:internal_server_error) | |> put_status(:internal_server_error) | ||||
|> json(%{error: "Couldn't delete the pack #{name}"}) | |||||
|> json(%{error: error_message}) | |||||
end | end | ||||
end | end | ||||
@@ -180,12 +197,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
|> put_status(:bad_request) | |> put_status(:bad_request) | ||||
|> json(%{error: "The fallback archive does not have all files specified in pack.json"}) | |> json(%{error: "The fallback archive does not have all files specified in pack.json"}) | ||||
{:error, _} -> | |||||
render_error( | |||||
conn, | |||||
:internal_server_error, | |||||
"Unexpected error occurred while updating pack metadata." | |||||
) | |||||
{:error, error} -> | |||||
error_message = | |||||
add_posix_error( | |||||
"Unexpected error occurred while updating pack metadata.", | |||||
error | |||||
) | |||||
conn | |||||
|> put_status(:internal_server_error) | |||||
|> json(%{error: error_message}) | |||||
end | end | ||||
end | end | ||||
@@ -204,4 +225,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||||
|> json(%{error: "Error accessing emoji pack directory"}) | |> json(%{error: "Error accessing emoji pack directory"}) | ||||
end | end | ||||
end | end | ||||
defp add_posix_error(msg, error) do | |||||
[msg, Pleroma.Utils.posix_error_message(error)] | |||||
|> Enum.join(" ") | |||||
|> String.trim() | |||||
end | |||||
end | end |
@@ -0,0 +1,141 @@ | |||||
## This file is a PO Template file. | |||||
msgid "eperm" | |||||
msgstr "Operation not permitted" | |||||
msgid "eacces" | |||||
msgstr "Permission denied" | |||||
msgid "eagain" | |||||
msgstr "Resource temporarily unavailable" | |||||
msgid "ebadf" | |||||
msgstr "Bad file descriptor" | |||||
msgid "ebadmsg" | |||||
msgstr "Bad message" | |||||
msgid "ebusy" | |||||
msgstr "Device or resource busy" | |||||
msgid "edeadlk" | |||||
msgstr "Resource deadlock avoided" | |||||
msgid "edeadlock" | |||||
msgstr "Resource deadlock avoided" | |||||
msgid "edquot" | |||||
msgstr "Disk quota exceeded" | |||||
msgid "eexist" | |||||
msgstr "File exists" | |||||
msgid "efault" | |||||
msgstr "Bad address" | |||||
msgid "efbig" | |||||
msgstr "File too large" | |||||
msgid "eftype" | |||||
msgstr "Inappropriate file type or format" | |||||
msgid "eintr" | |||||
msgstr "Interrupted system call" | |||||
msgid "einval" | |||||
msgstr "Invalid argument" | |||||
msgid "eio" | |||||
msgstr "Input/output error" | |||||
msgid "eisdir" | |||||
msgstr "Is a directory" | |||||
msgid "eloop" | |||||
msgstr "Too many levels of symbolic links" | |||||
msgid "emfile" | |||||
msgstr "Too many open files" | |||||
msgid "emlink" | |||||
msgstr "Too many links" | |||||
msgid "emultihop" | |||||
msgstr "Multihop attempted" | |||||
msgid "enametoolong" | |||||
msgstr "File name too long" | |||||
msgid "enfile" | |||||
msgstr "Too many open files in system" | |||||
msgid "enobufs" | |||||
msgstr "No buffer space available" | |||||
msgid "enodev" | |||||
msgstr "No such device" | |||||
msgid "enolck" | |||||
msgstr "No locks available" | |||||
msgid "enolink" | |||||
msgstr "Link has been severed" | |||||
msgid "enoent" | |||||
msgstr "No such file or directory" | |||||
msgid "enomem" | |||||
msgstr "Cannot allocate memory" | |||||
msgid "enospc" | |||||
msgstr "No space left on device" | |||||
msgid "enosr" | |||||
msgstr "Out of streams resources" | |||||
msgid "enostr" | |||||
msgstr "Device not a stream" | |||||
msgid "enosys" | |||||
msgstr "Function not implemented" | |||||
msgid "enotblk" | |||||
msgstr "Block device required" | |||||
msgid "enotdir" | |||||
msgstr "Not a directory" | |||||
msgid "enotsup" | |||||
msgstr "Operation not supported" | |||||
msgid "enxio" | |||||
msgstr "No such device or address" | |||||
msgid "eopnotsupp" | |||||
msgstr "Operation not supported" | |||||
msgid "eoverflow" | |||||
msgstr "Value too large for defined data type" | |||||
msgid "epipe" | |||||
msgstr "Broken pipe" | |||||
msgid "erange" | |||||
msgstr "Numerical result out of range" | |||||
msgid "erofs" | |||||
msgstr "Read-only file system" | |||||
msgid "espipe" | |||||
msgstr "Illegal seek" | |||||
msgid "esrch" | |||||
msgstr "No such process" | |||||
msgid "estale" | |||||
msgstr "Stale file handle" | |||||
msgid "etxtbsy" | |||||
msgstr "Text file busy" | |||||
msgid "exdev" | |||||
msgstr "Invalid cross-device link" |
@@ -0,0 +1,149 @@ | |||||
## This file is a PO Template file. | |||||
## | |||||
## `msgid`s here are often extracted from source code. | |||||
## Add new translations manually only if they're dynamic | |||||
## translations that can't be statically extracted. | |||||
## | |||||
## Run `mix gettext.extract` to bring this file up to | |||||
## date. Leave `msgstr`s empty as changing them here as no | |||||
## effect: edit them in PO (`.po`) files instead. | |||||
msgid "eperm" | |||||
msgstr "" | |||||
msgid "eacces" | |||||
msgstr "" | |||||
msgid "eagain" | |||||
msgstr "" | |||||
msgid "ebadf" | |||||
msgstr "" | |||||
msgid "ebadmsg" | |||||
msgstr "" | |||||
msgid "ebusy" | |||||
msgstr "" | |||||
msgid "edeadlk" | |||||
msgstr "" | |||||
msgid "edeadlock" | |||||
msgstr "" | |||||
msgid "edquot" | |||||
msgstr "" | |||||
msgid "eexist" | |||||
msgstr "" | |||||
msgid "efault" | |||||
msgstr "" | |||||
msgid "efbig" | |||||
msgstr "" | |||||
msgid "eftype" | |||||
msgstr "" | |||||
msgid "eintr" | |||||
msgstr "" | |||||
msgid "einval" | |||||
msgstr "" | |||||
msgid "eio" | |||||
msgstr "" | |||||
msgid "eisdir" | |||||
msgstr "" | |||||
msgid "eloop" | |||||
msgstr "" | |||||
msgid "emfile" | |||||
msgstr "" | |||||
msgid "emlink" | |||||
msgstr "" | |||||
msgid "emultihop" | |||||
msgstr "" | |||||
msgid "enametoolong" | |||||
msgstr "" | |||||
msgid "enfile" | |||||
msgstr "" | |||||
msgid "enobufs" | |||||
msgstr "" | |||||
msgid "enodev" | |||||
msgstr "" | |||||
msgid "enolck" | |||||
msgstr "" | |||||
msgid "enolink" | |||||
msgstr "" | |||||
msgid "enoent" | |||||
msgstr "" | |||||
msgid "enomem" | |||||
msgstr "" | |||||
msgid "enospc" | |||||
msgstr "" | |||||
msgid "enosr" | |||||
msgstr "" | |||||
msgid "enostr" | |||||
msgstr "" | |||||
msgid "enosys" | |||||
msgstr "" | |||||
msgid "enotblk" | |||||
msgstr "" | |||||
msgid "enotdir" | |||||
msgstr "" | |||||
msgid "enotsup" | |||||
msgstr "" | |||||
msgid "enxio" | |||||
msgstr "" | |||||
msgid "eopnotsupp" | |||||
msgstr "" | |||||
msgid "eoverflow" | |||||
msgstr "" | |||||
msgid "epipe" | |||||
msgstr "" | |||||
msgid "erange" | |||||
msgstr "" | |||||
msgid "erofs" | |||||
msgstr "" | |||||
msgid "espipe" | |||||
msgstr "" | |||||
msgid "esrch" | |||||
msgstr "" | |||||
msgid "estale" | |||||
msgstr "" | |||||
msgid "etxtbsy" | |||||
msgstr "" | |||||
msgid "exdev" | |||||
msgstr "" |