# Portions of this file are derived from Pleroma: # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only # Upstream: https://git.pleroma.social/pleroma/pleroma/blob/develop/lib/pleroma/uploaders/local.ex defmodule Mobilizon.Web.Upload.Uploader.Local do @moduledoc """ Local uploader for files """ @behaviour Mobilizon.Web.Upload.Uploader alias Mobilizon.Config alias Mobilizon.Web.Upload @impl true def get_file(_) do {:ok, {:static_dir, upload_path()}} end @impl true @spec put_file(Upload.t()) :: :ok | {:ok, {:file, String.t()}} | {:error, :tempfile_no_longer_exists} def put_file(%Upload{path: initial_path, tempfile: tempfile}) do {path, file} = local_path(initial_path) result_file = Path.join(path, file) if File.exists?(result_file) do # If the resulting file already exists, it's because of the Dedupe filter :ok else if File.exists?(tempfile) do File.cp!(tempfile, result_file) {:ok, {:file, initial_path}} else {:error, :tempfile_no_longer_exists} end end end @impl true @spec remove_file(String.t()) :: {:ok, {:file, String.t()}} | {:error, :folder_not_empty} | {:error, :enofile} | {:error, File.posix()} def remove_file(path) do {path, file} = local_path(path) full_path = Path.join(path, file) if File.exists?(full_path) do do_remove_file(path, full_path) else {:error, :enofile} end end @spec do_remove_file(String.t(), String.t()) :: {:ok, {:file, String.t()}} | {:error, :folder_not_empty} | {:error, File.posix()} defp do_remove_file(path, full_path) do case File.rm(full_path) do :ok -> case remove_folder(path) do :ok -> {:ok, {:file, path}} {:error, err} -> {:error, err} end {:error, err} -> {:error, err} end end @spec remove_folder(String.t()) :: :ok | {:error, :folder_not_empty} | {:error, File.posix()} defp remove_folder(path) do with {:subfolder, true} <- {:subfolder, path != upload_path()}, {:empty_folder, {:ok, [] = _files}} <- {:empty_folder, File.ls(path)} do File.rmdir(path) else {:subfolder, _} -> :ok {:empty_folder, _} -> {:error, :folder_not_empty} end end @spec local_path(String.t()) :: {String.t(), String.t()} defp local_path(path) do case Enum.reverse(String.split(path, "/", trim: true)) do [file] -> {upload_path(), file} [file | folders] -> path = Path.join([upload_path()] ++ Enum.reverse(folders)) File.mkdir_p!(path) {path, file} end end @spec upload_path :: String.t() def upload_path do Config.get!([__MODULE__, :uploads]) end end