From cd316d7269a6cac1e9edb732b202343001b82399 Mon Sep 17 00:00:00 2001 From: Ilja Date: Mon, 14 Feb 2022 13:14:25 +0100 Subject: Use EXIF data of image to prefill image description During attachment upload Pleroma returns a "description" field. Pleroma-fe has an MR to use that to pre-fill the image description field, * This MR allows Pleroma to read the EXIF data during upload and return the description to the FE * If a description is already present (e.g. because a previous module added it), it will use that * Otherwise it will read from the EXIF data. First it will check -ImageDescription, if that's empty, it will check -iptc:Caption-Abstract * If no description is found, it will simply return nil, just like before * When people set up a new instance, they will be asked if they want to read metadata and this module will be activated if so This was taken from an MR i did on Pleroma and isn't finished yet. --- docs/configuration/cheatsheet.md | 6 ++ .../optional/media_graphics_packages.md | 15 +-- lib/mix/tasks/pleroma/instance.ex | 26 +++++ lib/pleroma/application_requirements.ex | 1 + lib/pleroma/upload.ex | 27 ++++-- lib/pleroma/upload/filter/exiftool_read_data.ex | 47 +++++++++ .../portrait_of_owls_caption-abstract_tmp.jpg | Bin 0 -> 958147 bytes ...s_imagedescription_and_caption-abstract_tmp.jpg | Bin 0 -> 958193 bytes .../portrait_of_owls_no_description_tmp.jpg | Bin 0 -> 958065 bytes test/mix/tasks/pleroma/instance_test.exs | 7 +- .../upload/filter/exiftool_read_data_test.exs | 106 +++++++++++++++++++++ 11 files changed, 220 insertions(+), 15 deletions(-) create mode 100644 lib/pleroma/upload/filter/exiftool_read_data.ex create mode 100644 test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg create mode 100644 test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg create mode 100644 test/fixtures/portrait_of_owls_no_description_tmp.jpg create mode 100644 test/pleroma/upload/filter/exiftool_read_data_test.exs diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 1e74d40e6..f1624b651 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -633,6 +633,12 @@ This filter only strips the GPS and location metadata with Exiftool leaving colo No specific configuration. +#### Pleroma.Upload.Filter.ExiftoolReadData + +This filter only reads metadata with Exiftool so clients can prefill the media description field. + +No specific configuration. + #### Pleroma.Upload.Filter.Mogrify * `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"implode", "1"}]`. diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md index cb3d71188..47e2ef3fb 100644 --- a/docs/installation/optional/media_graphics_packages.md +++ b/docs/installation/optional/media_graphics_packages.md @@ -1,9 +1,9 @@ # Optional software packages needed for specific functionality For specific Pleroma functionality (which is disabled by default) some or all of the below packages are required: - * `ImageMagic` - * `ffmpeg` - * `exiftool` + * `ImageMagic` + * `ffmpeg` + * `exiftool` Please refer to documentation in `docs/installation` on how to install them on specific OS. @@ -14,19 +14,20 @@ Note: the packages are not required with the current default settings of Pleroma `ImageMagick` is a set of tools to create, edit, compose, or convert bitmap images. It is required for the following Pleroma features: - * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) - * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) + * `Pleroma.Upload.Filters.Mogrify`, `Pleroma.Upload.Filters.Mogrifun` upload filters (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * Media preview proxy for still images (related config: `media_preview_proxy/enabled` in `config/config.exs`) ## `ffmpeg` `ffmpeg` is software to record, convert and stream audio and video. It is required for the following Pleroma features: - * Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`) + * Media preview proxy for videos (related config: `media_preview_proxy/enabled` in `config/config.exs`) ## `exiftool` `exiftool` is media files metadata reader/writer. It is required for the following Pleroma features: - * `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * `Pleroma.Upload.Filters.ExiftoolReadData` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index f292fc762..d206e1622 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -35,6 +35,7 @@ defmodule Mix.Tasks.Pleroma.Instance do listen_ip: :string, listen_port: :string, strip_uploads: :string, + read_uploads_data: :string, anonymize_uploads: :string, dedupe_uploads: :string ], @@ -178,6 +179,23 @@ defmodule Mix.Tasks.Pleroma.Instance do strip_uploads_default ) === "y" + {read_uploads_data_message, read_uploads_data_default} = + if Pleroma.Utils.command_available?("exiftool") do + {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)", + "y"} + else + {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as not installed, please install it if you answer yes. (y/n)", + "n"} + end + + read_uploads_data = + get_option( + options, + :read_uploads_data, + read_uploads_data_message, + read_uploads_data_default + ) === "y" + anonymize_uploads = get_option( options, @@ -230,6 +248,7 @@ defmodule Mix.Tasks.Pleroma.Instance do upload_filters: upload_filters(%{ strip: strip_uploads, + read_data: read_uploads_data, anonymize: anonymize_uploads, dedupe: dedupe_uploads }) @@ -303,6 +322,13 @@ defmodule Mix.Tasks.Pleroma.Instance do [] end + enabled_filters = + if filters.read_data do + enabled_filters ++ [Pleroma.Upload.Filter.ExiftoolReadData] + else + enabled_filters + end + enabled_filters = if filters.anonymize do enabled_filters ++ [Pleroma.Upload.Filter.AnonymizeFilename] diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index 06d388694..ea1ee71c0 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -165,6 +165,7 @@ defmodule Pleroma.ApplicationRequirements do defp check_system_commands!(:ok) do filter_commands_statuses = [ check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"), + check_filter(Pleroma.Upload.Filter.ExiftoolReadData, "exiftool"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"), diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index 242813dcd..db2909276 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -60,12 +60,23 @@ defmodule Pleroma.Upload do width: integer(), height: integer(), blurhash: String.t(), + description: String.t(), path: String.t() } - defstruct [:id, :name, :tempfile, :content_type, :width, :height, :blurhash, :path] - - defp get_description(opts, upload) do - case {opts[:description], Pleroma.Config.get([Pleroma.Upload, :default_description])} do + defstruct [ + :id, + :name, + :tempfile, + :content_type, + :width, + :height, + :blurhash, + :description, + :path + ] + + defp get_description(upload) do + case {upload.description, Pleroma.Config.get([Pleroma.Upload, :default_description])} do {description, _} when is_binary(description) -> description {_, :filename} -> upload.name {_, str} when is_binary(str) -> str @@ -81,7 +92,7 @@ defmodule Pleroma.Upload do with {:ok, upload} <- prepare_upload(upload, opts), upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), - description = get_description(opts, upload), + description = get_description(upload), {_, true} <- {:description_limit, String.length(description) <= Pleroma.Config.get([:instance, :description_limit])}, @@ -152,7 +163,8 @@ defmodule Pleroma.Upload do id: UUID.generate(), name: file.filename, tempfile: file.path, - content_type: file.content_type + content_type: file.content_type, + description: opts.description }} end end @@ -172,7 +184,8 @@ defmodule Pleroma.Upload do id: UUID.generate(), name: hash <> "." <> ext, tempfile: tmp_path, - content_type: content_type + content_type: content_type, + description: opts.description }} end end diff --git a/lib/pleroma/upload/filter/exiftool_read_data.ex b/lib/pleroma/upload/filter/exiftool_read_data.ex new file mode 100644 index 000000000..c8bedfbf8 --- /dev/null +++ b/lib/pleroma/upload/filter/exiftool_read_data.ex @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.ExiftoolReadData do + @moduledoc """ + Gets the description from the related EXIF tags and provides them in the response if no description is provided yet. + It will first check ImageDescription, when that's too long or empty, it will check iptc:Caption-Abstract. + When the description is too long (see `:instance, :description_limit`), an empty string is returned. + """ + @behaviour Pleroma.Upload.Filter + + @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} + + def filter(%Pleroma.Upload{description: description}) + when is_binary(description), + do: {:ok, :noop} + + def filter(%Pleroma.Upload{tempfile: file} = upload), + do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))} + + def filter(_, _), do: {:ok, :noop} + + defp read_description_from_exif_data(file) do + nil + |> read_when_empty(file, "-ImageDescription") + |> read_when_empty(file, "-iptc:Caption-Abstract") + end + + defp read_when_empty(current_description, _, _) when is_binary(current_description), + do: current_description + + defp read_when_empty(_, file, tag) do + try do + {tag_content, 0} = + System.cmd("exiftool", ["-b", "-s3", tag, file], stderr_to_stdout: true, parallelism: true) + + if tag_content != "" and + String.length(tag_content) <= + Pleroma.Config.get([:instance, :description_limit]), + do: tag_content, + else: nil + rescue + _ in ErlangError -> nil + end + end +end diff --git a/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg b/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg new file mode 100644 index 000000000..f5fe63999 Binary files /dev/null and b/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg differ diff --git a/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg b/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg new file mode 100644 index 000000000..0b8b85754 Binary files /dev/null and b/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg differ diff --git a/test/fixtures/portrait_of_owls_no_description_tmp.jpg b/test/fixtures/portrait_of_owls_no_description_tmp.jpg new file mode 100644 index 000000000..d63d40af8 Binary files /dev/null and b/test/fixtures/portrait_of_owls_no_description_tmp.jpg differ diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs index 249689ec6..e72aab701 100644 --- a/test/mix/tasks/pleroma/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -69,6 +69,8 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do "./test/../test/instance/static/", "--strip-uploads", "y", + "--read-uploads-data", + "y", "--dedupe-uploads", "n", "--anonymize-uploads", @@ -91,7 +93,10 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do assert generated_config =~ "password: \"dbpass\"" assert generated_config =~ "configurable_from_database: true" assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" - assert generated_config =~ "filters: [Pleroma.Upload.Filter.Exiftool]" + + assert generated_config =~ + "filters: [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.ExiftoolReadData]" + assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) end diff --git a/test/pleroma/upload/filter/exiftool_read_data_test.exs b/test/pleroma/upload/filter/exiftool_read_data_test.exs new file mode 100644 index 000000000..0861d293a --- /dev/null +++ b/test/pleroma/upload/filter/exiftool_read_data_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.ExiftoolReadDataTest do + use Pleroma.DataCase, async: true + alias Pleroma.Upload.Filter + + @uploads %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg"), + description: nil + } + + test "keeps description when not empty" do + uploads = %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname( + "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" + ), + description: "Eight different owls" + } + + assert Filter.ExiftoolReadData.filter(uploads) == + {:ok, :noop} + end + + test "otherwise returns ImageDescription when present" do + uploads_after = %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname( + "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" + ), + description: "Pictures of eight different owls" + } + + assert Filter.ExiftoolReadData.filter(@uploads) == + {:ok, :filtered, uploads_after} + end + + test "otherwise returns iptc:Caption-Abstract when present" do + upload = %Pleroma.Upload{ + name: "portrait_of_owls_caption-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), + description: nil + } + + upload_after = %Pleroma.Upload{ + name: "portrait_of_owls_caption-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), + description: "Pictures of eight different owls - iptc" + } + + assert Filter.ExiftoolReadData.filter(upload) == + {:ok, :filtered, upload_after} + end + + test "otherwise returns nil" do + uploads = %Pleroma.Upload{ + name: "portrait_of_owls_no_description-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_no_description.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_no_description_tmp.jpg"), + description: nil + } + + assert Filter.ExiftoolReadData.filter(uploads) == + {:ok, :filtered, uploads} + end + + test "Return nil when image description from EXIF data exceeds the maximum length" do + clear_config([:instance, :description_limit], 5) + + assert Filter.ExiftoolReadData.filter(@uploads) == + {:ok, :filtered, @uploads} + end + + test "Return nil when image description from EXIF data can't be read" do + uploads = %Pleroma.Upload{ + name: "non-existant.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/non-existant.jpg"), + tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg"), + description: nil + } + + assert Filter.ExiftoolReadData.filter(uploads) == + {:ok, :filtered, uploads} + end +end -- cgit v1.2.3 From 551721e41a0bd98bb840baca48415a781cc463a7 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Feb 2022 12:59:42 +0100 Subject: Rename the new module --- docs/configuration/cheatsheet.md | 4 +- .../optional/media_graphics_packages.md | 2 +- lib/mix/tasks/pleroma/instance.ex | 18 ++-- lib/pleroma/application_requirements.ex | 2 +- lib/pleroma/upload/filter/exiftool.ex | 31 ------ lib/pleroma/upload/filter/exiftool/exiftool.ex | 31 ++++++ .../upload/filter/exiftool/read_description.ex | 47 +++++++++ lib/pleroma/upload/filter/exiftool_read_data.ex | 47 --------- test/mix/tasks/pleroma/instance_test.exs | 4 +- .../upload/filter/exiftool/exiftool_test.exs | 42 ++++++++ .../filter/exiftool/read_description_test.exs | 106 +++++++++++++++++++++ .../upload/filter/exiftool_read_data_test.exs | 106 --------------------- test/pleroma/upload/filter/exiftool_test.exs | 42 -------- 13 files changed, 241 insertions(+), 241 deletions(-) delete mode 100644 lib/pleroma/upload/filter/exiftool.ex create mode 100644 lib/pleroma/upload/filter/exiftool/exiftool.ex create mode 100644 lib/pleroma/upload/filter/exiftool/read_description.ex delete mode 100644 lib/pleroma/upload/filter/exiftool_read_data.ex create mode 100644 test/pleroma/upload/filter/exiftool/exiftool_test.exs create mode 100644 test/pleroma/upload/filter/exiftool/read_description_test.exs delete mode 100644 test/pleroma/upload/filter/exiftool_read_data_test.exs delete mode 100644 test/pleroma/upload/filter/exiftool_test.exs diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index f1624b651..985a22a1d 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -633,9 +633,9 @@ This filter only strips the GPS and location metadata with Exiftool leaving colo No specific configuration. -#### Pleroma.Upload.Filter.ExiftoolReadData +#### Pleroma.Upload.Filter.Exiftool.ReadDescription -This filter only reads metadata with Exiftool so clients can prefill the media description field. +This filter reads the ImageDescription and iptc:Caption-Abstract fields with Exiftool so clients can prefill the media description field. No specific configuration. diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md index 47e2ef3fb..e43c748ea 100644 --- a/docs/installation/optional/media_graphics_packages.md +++ b/docs/installation/optional/media_graphics_packages.md @@ -30,4 +30,4 @@ It is required for the following Pleroma features: It is required for the following Pleroma features: * `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) - * `Pleroma.Upload.Filters.ExiftoolReadData` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index d206e1622..40a8a2320 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -35,7 +35,7 @@ defmodule Mix.Tasks.Pleroma.Instance do listen_ip: :string, listen_port: :string, strip_uploads: :string, - read_uploads_data: :string, + read_uploads_description: :string, anonymize_uploads: :string, dedupe_uploads: :string ], @@ -179,7 +179,7 @@ defmodule Mix.Tasks.Pleroma.Instance do strip_uploads_default ) === "y" - {read_uploads_data_message, read_uploads_data_default} = + {read_uploads_description_message, read_uploads_description_default} = if Pleroma.Utils.command_available?("exiftool") do {"Do you want to read data from uploaded files so clients can use it to prefill fields like image description? This requires exiftool, it was detected as installed. (y/n)", "y"} @@ -188,12 +188,12 @@ defmodule Mix.Tasks.Pleroma.Instance do "n"} end - read_uploads_data = + read_uploads_description = get_option( options, - :read_uploads_data, - read_uploads_data_message, - read_uploads_data_default + :read_uploads_description, + read_uploads_description_message, + read_uploads_description_default ) === "y" anonymize_uploads = @@ -248,7 +248,7 @@ defmodule Mix.Tasks.Pleroma.Instance do upload_filters: upload_filters(%{ strip: strip_uploads, - read_data: read_uploads_data, + read_description: read_uploads_description, anonymize: anonymize_uploads, dedupe: dedupe_uploads }) @@ -323,8 +323,8 @@ defmodule Mix.Tasks.Pleroma.Instance do end enabled_filters = - if filters.read_data do - enabled_filters ++ [Pleroma.Upload.Filter.ExiftoolReadData] + if filters.read_description do + enabled_filters ++ [Pleroma.Upload.Filter.Exiftool.ReadDescription] else enabled_filters end diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index ea1ee71c0..117aa88cb 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -165,7 +165,7 @@ defmodule Pleroma.ApplicationRequirements do defp check_system_commands!(:ok) do filter_commands_statuses = [ check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"), - check_filter(Pleroma.Upload.Filter.ExiftoolReadData, "exiftool"), + check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), check_filter(Pleroma.Upload.Filter.AnalyzeMetadata, "mogrify"), diff --git a/lib/pleroma/upload/filter/exiftool.ex b/lib/pleroma/upload/filter/exiftool.ex deleted file mode 100644 index 36cc045c2..000000000 --- a/lib/pleroma/upload/filter/exiftool.ex +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.Exiftool do - @moduledoc """ - Strips GPS related EXIF tags and overwrites the file in place. - Also strips or replaces filesystem metadata e.g., timestamps. - """ - @behaviour Pleroma.Upload.Filter - - @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - - # Formats not compatible with exiftool at this time - def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} - def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} - - def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do - try do - case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do - {_response, 0} -> {:ok, :filtered} - {error, 1} -> {:error, error} - end - rescue - e in ErlangError -> - {:error, "#{__MODULE__}: #{inspect(e)}"} - end - end - - def filter(_), do: {:ok, :noop} -end diff --git a/lib/pleroma/upload/filter/exiftool/exiftool.ex b/lib/pleroma/upload/filter/exiftool/exiftool.ex new file mode 100644 index 000000000..36cc045c2 --- /dev/null +++ b/lib/pleroma/upload/filter/exiftool/exiftool.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool do + @moduledoc """ + Strips GPS related EXIF tags and overwrites the file in place. + Also strips or replaces filesystem metadata e.g., timestamps. + """ + @behaviour Pleroma.Upload.Filter + + @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} + + # Formats not compatible with exiftool at this time + def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} + def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} + + def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do + try do + case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do + {_response, 0} -> {:ok, :filtered} + {error, 1} -> {:error, error} + end + rescue + e in ErlangError -> + {:error, "#{__MODULE__}: #{inspect(e)}"} + end + end + + def filter(_), do: {:ok, :noop} +end diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex new file mode 100644 index 000000000..3f7b7c798 --- /dev/null +++ b/lib/pleroma/upload/filter/exiftool/read_description.ex @@ -0,0 +1,47 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do + @moduledoc """ + Gets the description from the related EXIF tags and provides them in the response if no description is provided yet. + It will first check ImageDescription, when that's too long or empty, it will check iptc:Caption-Abstract. + When the description is too long (see `:instance, :description_limit`), an empty string is returned. + """ + @behaviour Pleroma.Upload.Filter + + @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} + + def filter(%Pleroma.Upload{description: description}) + when is_binary(description), + do: {:ok, :noop} + + def filter(%Pleroma.Upload{tempfile: file} = upload), + do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))} + + def filter(_, _), do: {:ok, :noop} + + defp read_description_from_exif_data(file) do + nil + |> read_when_empty(file, "-ImageDescription") + |> read_when_empty(file, "-iptc:Caption-Abstract") + end + + defp read_when_empty(current_description, _, _) when is_binary(current_description), + do: current_description + + defp read_when_empty(_, file, tag) do + try do + {tag_content, 0} = + System.cmd("exiftool", ["-b", "-s3", tag, file], stderr_to_stdout: true, parallelism: true) + + if tag_content != "" and + String.length(tag_content) <= + Pleroma.Config.get([:instance, :description_limit]), + do: tag_content, + else: nil + rescue + _ in ErlangError -> nil + end + end +end diff --git a/lib/pleroma/upload/filter/exiftool_read_data.ex b/lib/pleroma/upload/filter/exiftool_read_data.ex deleted file mode 100644 index c8bedfbf8..000000000 --- a/lib/pleroma/upload/filter/exiftool_read_data.ex +++ /dev/null @@ -1,47 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.ExiftoolReadData do - @moduledoc """ - Gets the description from the related EXIF tags and provides them in the response if no description is provided yet. - It will first check ImageDescription, when that's too long or empty, it will check iptc:Caption-Abstract. - When the description is too long (see `:instance, :description_limit`), an empty string is returned. - """ - @behaviour Pleroma.Upload.Filter - - @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - - def filter(%Pleroma.Upload{description: description}) - when is_binary(description), - do: {:ok, :noop} - - def filter(%Pleroma.Upload{tempfile: file} = upload), - do: {:ok, :filtered, upload |> Map.put(:description, read_description_from_exif_data(file))} - - def filter(_, _), do: {:ok, :noop} - - defp read_description_from_exif_data(file) do - nil - |> read_when_empty(file, "-ImageDescription") - |> read_when_empty(file, "-iptc:Caption-Abstract") - end - - defp read_when_empty(current_description, _, _) when is_binary(current_description), - do: current_description - - defp read_when_empty(_, file, tag) do - try do - {tag_content, 0} = - System.cmd("exiftool", ["-b", "-s3", tag, file], stderr_to_stdout: true, parallelism: true) - - if tag_content != "" and - String.length(tag_content) <= - Pleroma.Config.get([:instance, :description_limit]), - do: tag_content, - else: nil - rescue - _ in ErlangError -> nil - end - end -end diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs index e72aab701..265b679f7 100644 --- a/test/mix/tasks/pleroma/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -69,7 +69,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do "./test/../test/instance/static/", "--strip-uploads", "y", - "--read-uploads-data", + "--read-uploads-description", "y", "--dedupe-uploads", "n", @@ -95,7 +95,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" assert generated_config =~ - "filters: [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.ExiftoolReadData]" + "filters: [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription]" assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) diff --git a/test/pleroma/upload/filter/exiftool/exiftool_test.exs b/test/pleroma/upload/filter/exiftool/exiftool_test.exs new file mode 100644 index 000000000..0a0ef2bdf --- /dev/null +++ b/test/pleroma/upload/filter/exiftool/exiftool_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.ExiftoolTest do + use Pleroma.DataCase, async: true + alias Pleroma.Upload.Filter + + test "apply exiftool filter" do + assert Pleroma.Utils.command_available?("exiftool") + + File.cp!( + "test/fixtures/DSCN0010.jpg", + "test/fixtures/DSCN0010_tmp.jpg" + ) + + upload = %Pleroma.Upload{ + name: "image_with_GPS_data.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/DSCN0010.jpg"), + tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") + } + + assert Filter.Exiftool.filter(upload) == {:ok, :filtered} + + {exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"]) + {exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"]) + + refute exif_original == exif_filtered + assert String.match?(exif_original, ~r/GPS/) + refute String.match?(exif_filtered, ~r/GPS/) + end + + test "verify webp files are skipped" do + upload = %Pleroma.Upload{ + name: "sample.webp", + content_type: "image/webp" + } + + assert Filter.Exiftool.filter(upload) == {:ok, :noop} + end +end diff --git a/test/pleroma/upload/filter/exiftool/read_description_test.exs b/test/pleroma/upload/filter/exiftool/read_description_test.exs new file mode 100644 index 000000000..0e97b424b --- /dev/null +++ b/test/pleroma/upload/filter/exiftool/read_description_test.exs @@ -0,0 +1,106 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2021 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do + use Pleroma.DataCase, async: true + alias Pleroma.Upload.Filter + + @uploads %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg"), + description: nil + } + + test "keeps description when not empty" do + uploads = %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname( + "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" + ), + description: "Eight different owls" + } + + assert Filter.Exiftool.ReadDescription.filter(uploads) == + {:ok, :noop} + end + + test "otherwise returns ImageDescription when present" do + uploads_after = %Pleroma.Upload{ + name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + content_type: "image/jpeg", + path: + Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + tempfile: + Path.absname( + "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" + ), + description: "Pictures of eight different owls" + } + + assert Filter.Exiftool.ReadDescription.filter(@uploads) == + {:ok, :filtered, uploads_after} + end + + test "otherwise returns iptc:Caption-Abstract when present" do + upload = %Pleroma.Upload{ + name: "portrait_of_owls_caption-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), + description: nil + } + + upload_after = %Pleroma.Upload{ + name: "portrait_of_owls_caption-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), + description: "Pictures of eight different owls - iptc" + } + + assert Filter.Exiftool.ReadDescription.filter(upload) == + {:ok, :filtered, upload_after} + end + + test "otherwise returns nil" do + uploads = %Pleroma.Upload{ + name: "portrait_of_owls_no_description-abstract.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/portrait_of_owls_no_description.jpg"), + tempfile: Path.absname("test/fixtures/portrait_of_owls_no_description_tmp.jpg"), + description: nil + } + + assert Filter.Exiftool.ReadDescription.filter(uploads) == + {:ok, :filtered, uploads} + end + + test "Return nil when image description from EXIF data exceeds the maximum length" do + clear_config([:instance, :description_limit], 5) + + assert Filter.Exiftool.ReadDescription.filter(@uploads) == + {:ok, :filtered, @uploads} + end + + test "Return nil when image description from EXIF data can't be read" do + uploads = %Pleroma.Upload{ + name: "non-existant.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/non-existant.jpg"), + tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg"), + description: nil + } + + assert Filter.Exiftool.ReadDescription.filter(uploads) == + {:ok, :filtered, uploads} + end +end diff --git a/test/pleroma/upload/filter/exiftool_read_data_test.exs b/test/pleroma/upload/filter/exiftool_read_data_test.exs deleted file mode 100644 index 0861d293a..000000000 --- a/test/pleroma/upload/filter/exiftool_read_data_test.exs +++ /dev/null @@ -1,106 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2021 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.ExiftoolReadDataTest do - use Pleroma.DataCase, async: true - alias Pleroma.Upload.Filter - - @uploads %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", - content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), - tempfile: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg"), - description: nil - } - - test "keeps description when not empty" do - uploads = %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", - content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), - tempfile: - Path.absname( - "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" - ), - description: "Eight different owls" - } - - assert Filter.ExiftoolReadData.filter(uploads) == - {:ok, :noop} - end - - test "otherwise returns ImageDescription when present" do - uploads_after = %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", - content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), - tempfile: - Path.absname( - "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" - ), - description: "Pictures of eight different owls" - } - - assert Filter.ExiftoolReadData.filter(@uploads) == - {:ok, :filtered, uploads_after} - end - - test "otherwise returns iptc:Caption-Abstract when present" do - upload = %Pleroma.Upload{ - name: "portrait_of_owls_caption-abstract.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), - description: nil - } - - upload_after = %Pleroma.Upload{ - name: "portrait_of_owls_caption-abstract.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), - description: "Pictures of eight different owls - iptc" - } - - assert Filter.ExiftoolReadData.filter(upload) == - {:ok, :filtered, upload_after} - end - - test "otherwise returns nil" do - uploads = %Pleroma.Upload{ - name: "portrait_of_owls_no_description-abstract.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_no_description.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_no_description_tmp.jpg"), - description: nil - } - - assert Filter.ExiftoolReadData.filter(uploads) == - {:ok, :filtered, uploads} - end - - test "Return nil when image description from EXIF data exceeds the maximum length" do - clear_config([:instance, :description_limit], 5) - - assert Filter.ExiftoolReadData.filter(@uploads) == - {:ok, :filtered, @uploads} - end - - test "Return nil when image description from EXIF data can't be read" do - uploads = %Pleroma.Upload{ - name: "non-existant.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/non-existant.jpg"), - tempfile: Path.absname("test/fixtures/non-existant_tmp.jpg"), - description: nil - } - - assert Filter.ExiftoolReadData.filter(uploads) == - {:ok, :filtered, uploads} - end -end diff --git a/test/pleroma/upload/filter/exiftool_test.exs b/test/pleroma/upload/filter/exiftool_test.exs deleted file mode 100644 index 0a0ef2bdf..000000000 --- a/test/pleroma/upload/filter/exiftool_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.ExiftoolTest do - use Pleroma.DataCase, async: true - alias Pleroma.Upload.Filter - - test "apply exiftool filter" do - assert Pleroma.Utils.command_available?("exiftool") - - File.cp!( - "test/fixtures/DSCN0010.jpg", - "test/fixtures/DSCN0010_tmp.jpg" - ) - - upload = %Pleroma.Upload{ - name: "image_with_GPS_data.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/DSCN0010.jpg"), - tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") - } - - assert Filter.Exiftool.filter(upload) == {:ok, :filtered} - - {exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"]) - {exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"]) - - refute exif_original == exif_filtered - assert String.match?(exif_original, ~r/GPS/) - refute String.match?(exif_filtered, ~r/GPS/) - end - - test "verify webp files are skipped" do - upload = %Pleroma.Upload{ - name: "sample.webp", - content_type: "image/webp" - } - - assert Filter.Exiftool.filter(upload) == {:ok, :noop} - end -end -- cgit v1.2.3 From 8303af84ce818b57347db4cd12611c4dd45bdf95 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Feb 2022 13:46:29 +0100 Subject: Rename the Exiftool module No migrations or checks yet --- CHANGELOG.md | 2 ++ docs/configuration/cheatsheet.md | 2 +- .../optional/media_graphics_packages.md | 2 +- lib/mix/tasks/pleroma/instance.ex | 18 +++++----- lib/pleroma/application_requirements.ex | 2 +- lib/pleroma/upload/filter/exiftool/exiftool.ex | 31 ---------------- .../upload/filter/exiftool/strip_location.ex | 31 ++++++++++++++++ test/mix/tasks/pleroma/instance_test.exs | 4 +-- .../upload/filter/exiftool/exiftool_test.exs | 42 ---------------------- .../upload/filter/exiftool/strip_location_test.exs | 42 ++++++++++++++++++++++ 10 files changed, 89 insertions(+), 87 deletions(-) delete mode 100644 lib/pleroma/upload/filter/exiftool/exiftool.ex create mode 100644 lib/pleroma/upload/filter/exiftool/strip_location.ex delete mode 100644 test/pleroma/upload/filter/exiftool/exiftool_test.exs create mode 100644 test/pleroma/upload/filter/exiftool/strip_location_test.exs diff --git a/CHANGELOG.md b/CHANGELOG.md index 19bccd115..f1beb0cd0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed - Allow users to remove their emails if instance does not need email to register +- Uploadfilter `Pleroma.Upload.Filter.Exiftool` has been renamed to `Pleroma.Upload.Filter.Exiftool.StripLocation` ### Added - `activeMonth` and `activeHalfyear` fields in NodeInfo usage.users object @@ -30,6 +31,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Configuration: Add `birthday_required` and `birthday_min_age` settings to provide a way to require users to enter their birth date. - PleromaAPI: Add `GET /api/v1/pleroma/birthdays` API endpoint - Make backend-rendered pages translatable. This includes emails. Pages returned as a HTTP response are translated using the language specified in the `userLanguage` cookie, or the `Accept-Language` header. Emails are translated using the `language` field when registering. This language can be changed by `PATCH /api/v1/accounts/update_credentials` with the `language` field. +- Uploadfilter `Pleroma.Upload.Filter.Exiftool.ReadDescription` returns description values to the FE so they can pre fill the image description field ### Fixed - Subscription(Bell) Notifications: Don't create from Pipeline Ingested replies diff --git a/docs/configuration/cheatsheet.md b/docs/configuration/cheatsheet.md index 985a22a1d..74642397b 100644 --- a/docs/configuration/cheatsheet.md +++ b/docs/configuration/cheatsheet.md @@ -627,7 +627,7 @@ This filter replaces the filename (not the path) of an upload. For complete obfu No specific configuration. -#### Pleroma.Upload.Filter.Exiftool +#### Pleroma.Upload.Filter.Exiftool.StripLocation This filter only strips the GPS and location metadata with Exiftool leaving color profiles and attributes intact. diff --git a/docs/installation/optional/media_graphics_packages.md b/docs/installation/optional/media_graphics_packages.md index e43c748ea..de402d1c4 100644 --- a/docs/installation/optional/media_graphics_packages.md +++ b/docs/installation/optional/media_graphics_packages.md @@ -29,5 +29,5 @@ It is required for the following Pleroma features: `exiftool` is media files metadata reader/writer. It is required for the following Pleroma features: - * `Pleroma.Upload.Filters.Exiftool` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) + * `Pleroma.Upload.Filters.Exiftool.StripLocation` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) * `Pleroma.Upload.Filters.Exiftool.ReadDescription` upload filter (related config: `Plaroma.Upload/filters` in `config/config.exs`) diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index 40a8a2320..5c93f19ff 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -34,7 +34,7 @@ defmodule Mix.Tasks.Pleroma.Instance do static_dir: :string, listen_ip: :string, listen_port: :string, - strip_uploads: :string, + strip_uploads_location: :string, read_uploads_description: :string, anonymize_uploads: :string, dedupe_uploads: :string @@ -162,7 +162,7 @@ defmodule Mix.Tasks.Pleroma.Instance do ) |> Path.expand() - {strip_uploads_message, strip_uploads_default} = + {strip_uploads_location_message, strip_uploads_location_default} = if Pleroma.Utils.command_available?("exiftool") do {"Do you want to strip location (GPS) data from uploaded images? This requires exiftool, it was detected as installed. (y/n)", "y"} @@ -171,12 +171,12 @@ defmodule Mix.Tasks.Pleroma.Instance do "n"} end - strip_uploads = + strip_uploads_location = get_option( options, - :strip_uploads, - strip_uploads_message, - strip_uploads_default + :strip_uploads_location, + strip_uploads_location_message, + strip_uploads_location_default ) === "y" {read_uploads_description_message, read_uploads_description_default} = @@ -247,7 +247,7 @@ defmodule Mix.Tasks.Pleroma.Instance do listen_port: listen_port, upload_filters: upload_filters(%{ - strip: strip_uploads, + strip_location: strip_uploads_location, read_description: read_uploads_description, anonymize: anonymize_uploads, dedupe: dedupe_uploads @@ -316,8 +316,8 @@ defmodule Mix.Tasks.Pleroma.Instance do defp upload_filters(filters) when is_map(filters) do enabled_filters = - if filters.strip do - [Pleroma.Upload.Filter.Exiftool] + if filters.strip_location do + [Pleroma.Upload.Filter.Exiftool.StripLocation] else [] end diff --git a/lib/pleroma/application_requirements.ex b/lib/pleroma/application_requirements.ex index 117aa88cb..44b1c1705 100644 --- a/lib/pleroma/application_requirements.ex +++ b/lib/pleroma/application_requirements.ex @@ -164,7 +164,7 @@ defmodule Pleroma.ApplicationRequirements do defp check_system_commands!(:ok) do filter_commands_statuses = [ - check_filter(Pleroma.Upload.Filter.Exiftool, "exiftool"), + check_filter(Pleroma.Upload.Filter.Exiftool.StripLocation, "exiftool"), check_filter(Pleroma.Upload.Filter.Exiftool.ReadDescription, "exiftool"), check_filter(Pleroma.Upload.Filter.Mogrify, "mogrify"), check_filter(Pleroma.Upload.Filter.Mogrifun, "mogrify"), diff --git a/lib/pleroma/upload/filter/exiftool/exiftool.ex b/lib/pleroma/upload/filter/exiftool/exiftool.ex deleted file mode 100644 index 36cc045c2..000000000 --- a/lib/pleroma/upload/filter/exiftool/exiftool.ex +++ /dev/null @@ -1,31 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.Exiftool do - @moduledoc """ - Strips GPS related EXIF tags and overwrites the file in place. - Also strips or replaces filesystem metadata e.g., timestamps. - """ - @behaviour Pleroma.Upload.Filter - - @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} - - # Formats not compatible with exiftool at this time - def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} - def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} - - def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do - try do - case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do - {_response, 0} -> {:ok, :filtered} - {error, 1} -> {:error, error} - end - rescue - e in ErlangError -> - {:error, "#{__MODULE__}: #{inspect(e)}"} - end - end - - def filter(_), do: {:ok, :noop} -end diff --git a/lib/pleroma/upload/filter/exiftool/strip_location.ex b/lib/pleroma/upload/filter/exiftool/strip_location.ex new file mode 100644 index 000000000..6100527d3 --- /dev/null +++ b/lib/pleroma/upload/filter/exiftool/strip_location.ex @@ -0,0 +1,31 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool.StripLocation do + @moduledoc """ + Strips GPS related EXIF tags and overwrites the file in place. + Also strips or replaces filesystem metadata e.g., timestamps. + """ + @behaviour Pleroma.Upload.Filter + + @spec filter(Pleroma.Upload.t()) :: {:ok, any()} | {:error, String.t()} + + # Formats not compatible with exiftool at this time + def filter(%Pleroma.Upload{content_type: "image/heic"}), do: {:ok, :noop} + def filter(%Pleroma.Upload{content_type: "image/webp"}), do: {:ok, :noop} + + def filter(%Pleroma.Upload{tempfile: file, content_type: "image" <> _}) do + try do + case System.cmd("exiftool", ["-overwrite_original", "-gps:all=", file], parallelism: true) do + {_response, 0} -> {:ok, :filtered} + {error, 1} -> {:error, error} + end + rescue + e in ErlangError -> + {:error, "#{__MODULE__}: #{inspect(e)}"} + end + end + + def filter(_), do: {:ok, :noop} +end diff --git a/test/mix/tasks/pleroma/instance_test.exs b/test/mix/tasks/pleroma/instance_test.exs index 265b679f7..b1c10e03c 100644 --- a/test/mix/tasks/pleroma/instance_test.exs +++ b/test/mix/tasks/pleroma/instance_test.exs @@ -67,7 +67,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do "test/uploads", "--static-dir", "./test/../test/instance/static/", - "--strip-uploads", + "--strip-uploads-location", "y", "--read-uploads-description", "y", @@ -95,7 +95,7 @@ defmodule Mix.Tasks.Pleroma.InstanceTest do assert generated_config =~ "http: [ip: {127, 0, 0, 1}, port: 4000]" assert generated_config =~ - "filters: [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription]" + "filters: [Pleroma.Upload.Filter.Exiftool.StripLocation, Pleroma.Upload.Filter.Exiftool.ReadDescription]" assert File.read!(tmp_path() <> "setup.psql") == generated_setup_psql() assert File.exists?(Path.expand("./test/instance/static/robots.txt")) diff --git a/test/pleroma/upload/filter/exiftool/exiftool_test.exs b/test/pleroma/upload/filter/exiftool/exiftool_test.exs deleted file mode 100644 index 0a0ef2bdf..000000000 --- a/test/pleroma/upload/filter/exiftool/exiftool_test.exs +++ /dev/null @@ -1,42 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2022 Pleroma Authors -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Upload.Filter.ExiftoolTest do - use Pleroma.DataCase, async: true - alias Pleroma.Upload.Filter - - test "apply exiftool filter" do - assert Pleroma.Utils.command_available?("exiftool") - - File.cp!( - "test/fixtures/DSCN0010.jpg", - "test/fixtures/DSCN0010_tmp.jpg" - ) - - upload = %Pleroma.Upload{ - name: "image_with_GPS_data.jpg", - content_type: "image/jpeg", - path: Path.absname("test/fixtures/DSCN0010.jpg"), - tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") - } - - assert Filter.Exiftool.filter(upload) == {:ok, :filtered} - - {exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"]) - {exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"]) - - refute exif_original == exif_filtered - assert String.match?(exif_original, ~r/GPS/) - refute String.match?(exif_filtered, ~r/GPS/) - end - - test "verify webp files are skipped" do - upload = %Pleroma.Upload{ - name: "sample.webp", - content_type: "image/webp" - } - - assert Filter.Exiftool.filter(upload) == {:ok, :noop} - end -end diff --git a/test/pleroma/upload/filter/exiftool/strip_location_test.exs b/test/pleroma/upload/filter/exiftool/strip_location_test.exs new file mode 100644 index 000000000..7e1541f60 --- /dev/null +++ b/test/pleroma/upload/filter/exiftool/strip_location_test.exs @@ -0,0 +1,42 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2022 Pleroma Authors +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Upload.Filter.Exiftool.StripLocationTest do + use Pleroma.DataCase, async: true + alias Pleroma.Upload.Filter + + test "apply exiftool filter" do + assert Pleroma.Utils.command_available?("exiftool") + + File.cp!( + "test/fixtures/DSCN0010.jpg", + "test/fixtures/DSCN0010_tmp.jpg" + ) + + upload = %Pleroma.Upload{ + name: "image_with_GPS_data.jpg", + content_type: "image/jpeg", + path: Path.absname("test/fixtures/DSCN0010.jpg"), + tempfile: Path.absname("test/fixtures/DSCN0010_tmp.jpg") + } + + assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :filtered} + + {exif_original, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010.jpg"]) + {exif_filtered, 0} = System.cmd("exiftool", ["test/fixtures/DSCN0010_tmp.jpg"]) + + refute exif_original == exif_filtered + assert String.match?(exif_original, ~r/GPS/) + refute String.match?(exif_filtered, ~r/GPS/) + end + + test "verify webp files are skipped" do + upload = %Pleroma.Upload{ + name: "sample.webp", + content_type: "image/webp" + } + + assert Filter.Exiftool.StripLocation.filter(upload) == {:ok, :noop} + end +end -- cgit v1.2.3 From d0d48a9e8832ed81e67126a2af019981cb761a2b Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Feb 2022 14:46:37 +0100 Subject: Add deprecation warnings --- lib/pleroma/config/deprecation_warnings.ex | 40 +++++++++++++++- test/pleroma/config/deprecation_warnings_test.exs | 56 +++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 118dd3acc..4348505e2 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -20,6 +20,43 @@ defmodule Pleroma.Config.DeprecationWarnings do "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} ] + def check_exiftool_filter do + filters = Config.get([Pleroma.Upload])[:filters] + + if Pleroma.Upload.Filter.Exiftool in filters do + Logger.warn(""" + !!!DEPRECATION WARNING!!! + Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool] + ``` + + Is now + + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool.StripLocation] + ``` + """) + + new_config = + filters + |> Enum.map(fn + Pleroma.Upload.Filter.Exiftool -> Pleroma.Upload.Filter.Exiftool.StripLocation + filter -> filter + end) + + Config.put([Pleroma.Upload, :filters], new_config) + + :error + else + :ok + end + end + def check_simple_policy_tuples do has_strings = Config.get([:mrf_simple]) @@ -180,7 +217,8 @@ defmodule Pleroma.Config.DeprecationWarnings do check_old_chat_shoutbox(), check_quarantined_instances_tuples(), check_transparency_exclusions_tuples(), - check_simple_policy_tuples() + check_simple_policy_tuples(), + check_exiftool_filter() ] |> Enum.reduce(:ok, fn :ok, :ok -> :ok diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index 202ec4b90..ffd446909 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -11,6 +11,62 @@ defmodule Pleroma.Config.DeprecationWarningsTest do alias Pleroma.Config alias Pleroma.Config.DeprecationWarnings + describe "filter exiftool" do + test "gives warning when still used" do + clear_config( + [Pleroma.Upload, :filters], + [Pleroma.Upload.Filter.Exiftool] + ) + + assert capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end) =~ + """ + !!!DEPRECATION WARNING!!! + Your config is using Exiftool as a filter instead of Exiftool.StripLocation. This should work for now, but you are advised to change to the new configuration to prevent possible issues later: + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool] + ``` + + Is now + + + ``` + config :pleroma, Pleroma.Upload, + filters: [Pleroma.Upload.Filter.Exiftool.StripLocation] + ``` + """ + end + + test "changes setting to exiftool strip location" do + clear_config( + [Pleroma.Upload, :filters], + [Pleroma.Upload.Filter.Exiftool, Pleroma.Upload.Filter.Exiftool.ReadDescription] + ) + + expected_config = [ + Pleroma.Upload.Filter.Exiftool.StripLocation, + Pleroma.Upload.Filter.Exiftool.ReadDescription + ] + + capture_log(fn -> DeprecationWarnings.warn() end) + + assert Config.get([Pleroma.Upload])[:filters] == expected_config + end + + test "doesn't give a warning with correct config" do + clear_config( + [Pleroma.Upload, :filters], + [ + Pleroma.Upload.Filter.Exiftool.StripLocation, + Pleroma.Upload.Filter.Exiftool.ReadDescription + ] + ) + + assert capture_log(fn -> DeprecationWarnings.check_exiftool_filter() end) == "" + end + end + describe "simple policy tuples" do test "gives warning when there are still strings" do clear_config([:mrf_simple], -- cgit v1.2.3 From cc5686bbd6f8a5fbf345de2f54a9e07ec7ff2b5c Mon Sep 17 00:00:00 2001 From: Ilja Date: Sun, 20 Feb 2022 15:44:40 +0100 Subject: Migration for exiftool filter Rename to Exiftool.StripLocation --- ..._filter_exiftool_to_exiftool_strip_location.exs | 34 ++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs diff --git a/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs b/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs new file mode 100644 index 000000000..7afb7479a --- /dev/null +++ b/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs @@ -0,0 +1,34 @@ +defmodule Pleroma.Repo.Migrations.UploadFilterExiftoolToExiftoolStripLocation do + use Ecto.Migration + + alias Pleroma.ConfigDB + + def up, + do: + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}) + |> update_filtername( + Pleroma.Upload.Filter.Exiftool, + Pleroma.Upload.Filter.Exiftool.StripLocation + ) + + def down, + do: + ConfigDB.get_by_params(%{group: :pleroma, key: Pleroma.Upload}) + |> update_filtername( + Pleroma.Upload.Filter.Exiftool.StripLocation, + Pleroma.Upload.Filter.Exiftool + ) + + defp update_filtername(%{value: value}, from_filtername, to_filtername) do + new_filters = + value[:filters] + |> Enum.map(fn + ^from_filtername -> to_filtername + filter -> filter + end) + + new_value = value |> Keyword.update(:filters, [], fn _ -> new_filters end) + + ConfigDB.update_or_create(%{group: :pleroma, key: Pleroma.Upload, value: new_value}) + end +end -- cgit v1.2.3 From 75ea766824a5d7714fbea5d24e59444c7adfc274 Mon Sep 17 00:00:00 2001 From: Ilja Date: Mon, 21 Feb 2022 09:38:38 +0100 Subject: Migration failed when no value for Pleroma.Upload was set --- ...pload_filter_exiftool_to_exiftool_strip_location.exs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs b/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs index 7afb7479a..0878b9699 100644 --- a/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs +++ b/priv/repo/migrations/20220220135625_upload_filter_exiftool_to_exiftool_strip_location.exs @@ -20,15 +20,18 @@ defmodule Pleroma.Repo.Migrations.UploadFilterExiftoolToExiftoolStripLocation do ) defp update_filtername(%{value: value}, from_filtername, to_filtername) do - new_filters = - value[:filters] - |> Enum.map(fn - ^from_filtername -> to_filtername - filter -> filter + new_value = + value + |> Keyword.update(:filters, [], fn filters -> + filters + |> Enum.map(fn + ^from_filtername -> to_filtername + filter -> filter + end) end) - new_value = value |> Keyword.update(:filters, [], fn _ -> new_filters end) - ConfigDB.update_or_create(%{group: :pleroma, key: Pleroma.Upload, value: new_value}) end + + defp update_filtername(_, _, _), do: nil end -- cgit v1.2.3 From 81afaee37436f1802f4e0a6d33d9bb8121e51f48 Mon Sep 17 00:00:00 2001 From: Ilja Date: Tue, 22 Feb 2022 18:18:10 +0100 Subject: Better way of getting keys I used keyword_list[:key], but if the key doesn't exist, it will return nil. I actually expect a list and further down the code I use that list. I believe the key should always be present, but in case it's not, it's better to return an empty list instead of nil. That way the code wont fail further down the line. --- lib/pleroma/config/deprecation_warnings.ex | 2 +- test/pleroma/config/deprecation_warnings_test.exs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pleroma/config/deprecation_warnings.ex b/lib/pleroma/config/deprecation_warnings.ex index 4348505e2..599f1d3cf 100644 --- a/lib/pleroma/config/deprecation_warnings.ex +++ b/lib/pleroma/config/deprecation_warnings.ex @@ -21,7 +21,7 @@ defmodule Pleroma.Config.DeprecationWarnings do ] def check_exiftool_filter do - filters = Config.get([Pleroma.Upload])[:filters] + filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) if Pleroma.Upload.Filter.Exiftool in filters do Logger.warn(""" diff --git a/test/pleroma/config/deprecation_warnings_test.exs b/test/pleroma/config/deprecation_warnings_test.exs index ffd446909..f3453ddb0 100644 --- a/test/pleroma/config/deprecation_warnings_test.exs +++ b/test/pleroma/config/deprecation_warnings_test.exs @@ -51,7 +51,7 @@ defmodule Pleroma.Config.DeprecationWarningsTest do capture_log(fn -> DeprecationWarnings.warn() end) - assert Config.get([Pleroma.Upload])[:filters] == expected_config + assert Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) == expected_config end test "doesn't give a warning with correct config" do -- cgit v1.2.3 From 7d234d097c09da138f1ac71a6afb197b3873c8b6 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sat, 26 Mar 2022 15:38:00 +0100 Subject: Add option to docs about instance gen --- docs/administration/CLI_tasks/instance.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/administration/CLI_tasks/instance.md b/docs/administration/CLI_tasks/instance.md index 982b22bf3..88509cf5b 100644 --- a/docs/administration/CLI_tasks/instance.md +++ b/docs/administration/CLI_tasks/instance.md @@ -37,7 +37,8 @@ If any of the options are left unspecified, you will be prompted interactively. - `--static-dir ` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.) - `--listen-ip ` - the ip the app should listen to, defaults to 127.0.0.1 - `--listen-port ` - the port the app should listen to, defaults to 4000 -- `--strip-uploads ` - use ExifTool to strip uploads of sensitive location data +- `--strip-uploads-location ` - use ExifTool to strip uploads of sensitive location data +- `--read-uploads-description ` - use ExifTool to read image descriptions from uploads - `--anonymize-uploads ` - randomize uploaded filenames - `--dedupe-uploads ` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames - `--skip-release-env` - skip generation the release environment file -- cgit v1.2.3 From 4a9ed319b8916500bcb7b8e7c319fea9acdf95dc Mon Sep 17 00:00:00 2001 From: Ilja Date: Sat, 21 May 2022 09:37:20 +0200 Subject: Change test pictures The previous pictures were labeled as public domain, but are actually a collage of pictures under other licenses. I now replaced them with a jpeg of simply a white pixel. --- test/fixtures/image_with_caption-abstract.jpg | Bin 0 -> 697 bytes ..._with_imagedescription_and_caption-abstract.jpg | Bin 0 -> 823 bytes test/fixtures/image_with_no_description.jpg | Bin 0 -> 631 bytes .../portrait_of_owls_caption-abstract_tmp.jpg | Bin 958147 -> 0 bytes ...s_imagedescription_and_caption-abstract_tmp.jpg | Bin 958193 -> 0 bytes .../portrait_of_owls_no_description_tmp.jpg | Bin 958065 -> 0 bytes .../filter/exiftool/read_description_test.exs | 50 +++++++++------------ 7 files changed, 21 insertions(+), 29 deletions(-) create mode 100644 test/fixtures/image_with_caption-abstract.jpg create mode 100644 test/fixtures/image_with_imagedescription_and_caption-abstract.jpg create mode 100644 test/fixtures/image_with_no_description.jpg delete mode 100644 test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg delete mode 100644 test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg delete mode 100644 test/fixtures/portrait_of_owls_no_description_tmp.jpg diff --git a/test/fixtures/image_with_caption-abstract.jpg b/test/fixtures/image_with_caption-abstract.jpg new file mode 100644 index 000000000..f982ffa81 Binary files /dev/null and b/test/fixtures/image_with_caption-abstract.jpg differ diff --git a/test/fixtures/image_with_imagedescription_and_caption-abstract.jpg b/test/fixtures/image_with_imagedescription_and_caption-abstract.jpg new file mode 100644 index 000000000..c82a269ef Binary files /dev/null and b/test/fixtures/image_with_imagedescription_and_caption-abstract.jpg differ diff --git a/test/fixtures/image_with_no_description.jpg b/test/fixtures/image_with_no_description.jpg new file mode 100644 index 000000000..ec6fc4be8 Binary files /dev/null and b/test/fixtures/image_with_no_description.jpg differ diff --git a/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg b/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg deleted file mode 100644 index f5fe63999..000000000 Binary files a/test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg and /dev/null differ diff --git a/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg b/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg deleted file mode 100644 index 0b8b85754..000000000 Binary files a/test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg and /dev/null differ diff --git a/test/fixtures/portrait_of_owls_no_description_tmp.jpg b/test/fixtures/portrait_of_owls_no_description_tmp.jpg deleted file mode 100644 index d63d40af8..000000000 Binary files a/test/fixtures/portrait_of_owls_no_description_tmp.jpg and /dev/null differ diff --git a/test/pleroma/upload/filter/exiftool/read_description_test.exs b/test/pleroma/upload/filter/exiftool/read_description_test.exs index 0e97b424b..0b358215c 100644 --- a/test/pleroma/upload/filter/exiftool/read_description_test.exs +++ b/test/pleroma/upload/filter/exiftool/read_description_test.exs @@ -7,26 +7,21 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do alias Pleroma.Upload.Filter @uploads %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + name: "image_with_imagedescription_and_caption-abstract.jpg", content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), - tempfile: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg"), + path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), description: nil } test "keeps description when not empty" do uploads = %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + name: "image_with_imagedescription_and_caption-abstract.jpg", content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), tempfile: - Path.absname( - "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" - ), - description: "Eight different owls" + Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), + description: "Some description" } assert Filter.Exiftool.ReadDescription.filter(uploads) == @@ -35,15 +30,12 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do test "otherwise returns ImageDescription when present" do uploads_after = %Pleroma.Upload{ - name: "portrait_of_owls_imagedescription_and_caption-abstract.jpg", + name: "image_with_imagedescription_and_caption-abstract.jpg", content_type: "image/jpeg", - path: - Path.absname("test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract.jpg"), + path: Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), tempfile: - Path.absname( - "test/fixtures/portrait_of_owls_imagedescription_and_caption-abstract_tmp.jpg" - ), - description: "Pictures of eight different owls" + Path.absname("test/fixtures/image_with_imagedescription_and_caption-abstract.jpg"), + description: "a descriptive white pixel" } assert Filter.Exiftool.ReadDescription.filter(@uploads) == @@ -52,19 +44,19 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do test "otherwise returns iptc:Caption-Abstract when present" do upload = %Pleroma.Upload{ - name: "portrait_of_owls_caption-abstract.jpg", + name: "image_with_caption-abstract.jpg", content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), + path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"), description: nil } upload_after = %Pleroma.Upload{ - name: "portrait_of_owls_caption-abstract.jpg", + name: "image_with_caption-abstract.jpg", content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_caption-abstract.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_caption-abstract_tmp.jpg"), - description: "Pictures of eight different owls - iptc" + path: Path.absname("test/fixtures/image_with_caption-abstract.jpg"), + tempfile: Path.absname("test/fixtures/image_with_caption-abstract.jpg"), + description: "an abstract white pixel" } assert Filter.Exiftool.ReadDescription.filter(upload) == @@ -73,10 +65,10 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do test "otherwise returns nil" do uploads = %Pleroma.Upload{ - name: "portrait_of_owls_no_description-abstract.jpg", + name: "image_with_no_description.jpg", content_type: "image/jpeg", - path: Path.absname("test/fixtures/portrait_of_owls_no_description.jpg"), - tempfile: Path.absname("test/fixtures/portrait_of_owls_no_description_tmp.jpg"), + path: Path.absname("test/fixtures/image_with_no_description.jpg"), + tempfile: Path.absname("test/fixtures/image_with_no_description.jpg"), description: nil } -- cgit v1.2.3 From 8c761942b1963bff08d98eeb22b84b0f327d68e4 Mon Sep 17 00:00:00 2001 From: Ilja Date: Sat, 21 May 2022 11:31:14 +0200 Subject: update moduledoc --- lib/pleroma/upload/filter/exiftool/read_description.ex | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex index 3f7b7c798..a69f79124 100644 --- a/lib/pleroma/upload/filter/exiftool/read_description.ex +++ b/lib/pleroma/upload/filter/exiftool/read_description.ex @@ -4,9 +4,9 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do @moduledoc """ - Gets the description from the related EXIF tags and provides them in the response if no description is provided yet. - It will first check ImageDescription, when that's too long or empty, it will check iptc:Caption-Abstract. - When the description is too long (see `:instance, :description_limit`), an empty string is returned. + Gets a valid description from the related EXIF tags and provides them in the response if no description is provided yet. + It will first check ImageDescription, when that doesn't probide a valid description, it will check iptc:Caption-Abstract. + A valid description means the fields are filled in and not too long (see `:instance, :description_limit`). """ @behaviour Pleroma.Upload.Filter -- cgit v1.2.3 From 56227ef7ba097c6be39a7e70b67c426a3016e0ab Mon Sep 17 00:00:00 2001 From: Ilja Date: Fri, 1 Jul 2022 12:31:34 +0200 Subject: Descriptions from exif data with only whitespeces are considered empty I noticed that pictures taken with Ubuntu-Touch have whitespace in one of the fields This should just be ignored imo --- .../upload/filter/exiftool/read_description.ex | 2 ++ ...edescription_and_caption-abstract_whitespaces.jpg | Bin 0 -> 785 bytes .../upload/filter/exiftool/read_description_test.exs | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+) create mode 100644 test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg diff --git a/lib/pleroma/upload/filter/exiftool/read_description.ex b/lib/pleroma/upload/filter/exiftool/read_description.ex index a69f79124..03d698a81 100644 --- a/lib/pleroma/upload/filter/exiftool/read_description.ex +++ b/lib/pleroma/upload/filter/exiftool/read_description.ex @@ -35,6 +35,8 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do {tag_content, 0} = System.cmd("exiftool", ["-b", "-s3", tag, file], stderr_to_stdout: true, parallelism: true) + tag_content = String.trim(tag_content) + if tag_content != "" and String.length(tag_content) <= Pleroma.Config.get([:instance, :description_limit]), diff --git a/test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg b/test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg new file mode 100644 index 000000000..a232fd2a1 Binary files /dev/null and b/test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg differ diff --git a/test/pleroma/upload/filter/exiftool/read_description_test.exs b/test/pleroma/upload/filter/exiftool/read_description_test.exs index 0b358215c..7389fda47 100644 --- a/test/pleroma/upload/filter/exiftool/read_description_test.exs +++ b/test/pleroma/upload/filter/exiftool/read_description_test.exs @@ -83,6 +83,25 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do {:ok, :filtered, @uploads} end + test "Ignores content with only whitespace" do + uploads = %Pleroma.Upload{ + name: "non-existant.jpg", + content_type: "image/jpeg", + path: + Path.absname( + "test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg" + ), + tempfile: + Path.absname( + "test/fixtures/image_with_imagedescription_and_caption-abstract_whitespaces.jpg" + ), + description: nil + } + + assert Filter.Exiftool.ReadDescription.filter(uploads) == + {:ok, :filtered, uploads} + end + test "Return nil when image description from EXIF data can't be read" do uploads = %Pleroma.Upload{ name: "non-existant.jpg", -- cgit v1.2.3