@@ -22,14 +22,14 @@ defmodule Pleroma.Emoji.Pack do | |||
alias Pleroma.Emoji | |||
alias Pleroma.Emoji.Pack | |||
alias Pleroma.Utils | |||
@spec create(String.t()) :: {:ok, t()} | {:error, File.posix()} | {:error, :empty_values} | |||
def create(name) do | |||
with :ok <- validate_not_empty([name]), | |||
dir <- Path.join(emoji_path(), name), | |||
: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 | |||
@@ -94,7 +94,7 @@ defmodule Pleroma.Emoji.Pack do | |||
def add_file(%Pack{} = pack, _, _, %Plug.Upload{content_type: "application/zip"} = file) do | |||
with {:ok, zip_files} <- :zip.table(to_charlist(file.path)), | |||
[_ | _] = 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 | |||
{:ok, _emoji_files} = | |||
:zip.unzip( | |||
@@ -282,18 +282,21 @@ defmodule Pleroma.Emoji.Pack do | |||
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 | |||
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_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 = | |||
pack.files | |||
@@ -301,8 +304,6 @@ defmodule Pleroma.Emoji.Pack do | |||
|> length() | |||
{:ok, Map.put(pack, :files_count, files_count)} | |||
else | |||
{:error, :not_found} | |||
end | |||
end | |||
@@ -434,10 +435,17 @@ defmodule Pleroma.Emoji.Pack do | |||
end | |||
end | |||
defp from_json(json) do | |||
defp from_json(json, attrs) do | |||
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 | |||
defp validate_shareable_packs_available(uri) do | |||
@@ -491,10 +499,10 @@ defmodule Pleroma.Emoji.Pack do | |||
end | |||
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 | |||
@@ -518,10 +526,15 @@ defmodule Pleroma.Emoji.Pack do | |||
defp get_filename(pack, shortcode) do | |||
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} | |||
else | |||
_ -> {:error, :doesnt_exist} | |||
{:error, _} = error -> | |||
error | |||
_ -> | |||
{:error, :doesnt_exist} | |||
end | |||
end | |||
@@ -3,6 +3,14 @@ | |||
# SPDX-License-Identifier: AGPL-3.0-only | |||
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 | |||
dir | |||
|> File.ls!() | |||
@@ -44,4 +52,12 @@ defmodule Pleroma.Utils do | |||
error -> error | |||
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 |
@@ -42,7 +42,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||
|> json(%{error: "pack name, shortcode or filename cannot be empty"}) | |||
{: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 | |||
@@ -69,7 +72,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||
|> json(%{error: "new_shortcode or new_filename cannot be empty"}) | |||
{: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 | |||
@@ -84,7 +91,11 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||
|> json(%{error: "pack name or shortcode cannot be empty"}) | |||
{: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 | |||
@@ -94,18 +105,24 @@ defmodule Pleroma.Web.PleromaAPI.EmojiFileController do | |||
|> json(%{error: "Emoji \"#{emoji_code}\" does not exist"}) | |||
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 | |||
|> put_status(:not_found) | |||
|> json(%{error: "pack \"#{pack_name}\" is not found"}) | |||
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 | |||
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 | |||
json(conn, pack) | |||
else | |||
{:error, :not_found} -> | |||
{:error, :enoent} -> | |||
conn | |||
|> put_status(:not_found) | |||
|> json(%{error: "Pack #{name} does not exist"}) | |||
@@ -80,6 +80,17 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
conn | |||
|> put_status(:bad_request) | |||
|> 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 | |||
@@ -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" | |||
}) | |||
{:error, :not_found} -> | |||
{:error, :enoent} -> | |||
conn | |||
|> put_status(:not_found) | |||
|> json(%{error: "Pack #{name} does not exist"}) | |||
@@ -116,10 +127,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> put_status(:internal_server_error) | |||
|> json(%{error: "SHA256 for the pack doesn't match the one sent by the server"}) | |||
{:error, e} -> | |||
{:error, error} -> | |||
conn | |||
|> put_status(:internal_server_error) | |||
|> json(%{error: e}) | |||
|> json(%{error: error}) | |||
end | |||
end | |||
@@ -139,12 +150,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> put_status(:bad_request) | |||
|> 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 | |||
@@ -164,10 +179,12 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> put_status(:bad_request) | |||
|> json(%{error: "pack name cannot be empty"}) | |||
{:error, _, _} -> | |||
{:error, error, _} -> | |||
error_message = add_posix_error("Couldn't delete the pack #{name}", error) | |||
conn | |||
|> put_status(:internal_server_error) | |||
|> json(%{error: "Couldn't delete the pack #{name}"}) | |||
|> json(%{error: error_message}) | |||
end | |||
end | |||
@@ -180,12 +197,16 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> put_status(:bad_request) | |||
|> 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 | |||
@@ -204,4 +225,10 @@ defmodule Pleroma.Web.PleromaAPI.EmojiPackController do | |||
|> json(%{error: "Error accessing emoji pack directory"}) | |||
end | |||
end | |||
defp add_posix_error(msg, error) do | |||
[msg, Pleroma.Utils.posix_error_message(error)] | |||
|> Enum.join(" ") | |||
|> String.trim() | |||
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 "" |