diff options
Diffstat (limited to 'lib')
30 files changed, 340 insertions, 188 deletions
diff --git a/lib/mix/tasks/pleroma/email.ex b/lib/mix/tasks/pleroma/email.ex index 2c3801429..d3fac6ec8 100644 --- a/lib/mix/tasks/pleroma/email.ex +++ b/lib/mix/tasks/pleroma/email.ex @@ -1,5 +1,6 @@  defmodule Mix.Tasks.Pleroma.Email do    use Mix.Task +  import Mix.Pleroma    @shortdoc "Simple Email test"    @moduledoc File.read!("docs/administration/CLI_tasks/email.md") @@ -18,8 +19,6 @@ defmodule Mix.Tasks.Pleroma.Email do      email = Pleroma.Emails.AdminEmail.test_email(options[:to])      {:ok, _} = Pleroma.Emails.Mailer.deliver(email) -    Mix.shell().info( -      "Test email has been sent to #{inspect(email.to)} from #{inspect(email.from)}" -    ) +    shell_info("Test email has been sent to #{inspect(email.to)} from #{inspect(email.from)}")    end  end diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 0f8fce774..72e2256ea 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -31,7 +31,7 @@ defmodule Pleroma.Activity do      "Announce" => "reblog",      "Like" => "favourite",      "Move" => "move", -    "EmojiReaction" => "pleroma:emoji_reaction" +    "EmojiReact" => "pleroma:emoji_reaction"    }    @mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types, diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index e17068876..2c8889ce5 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -33,6 +33,7 @@ defmodule Pleroma.Application do    def start(_type, _args) do      Pleroma.HTML.compile_scrubbers()      Pleroma.Config.DeprecationWarnings.warn() +    Pleroma.Plugs.HTTPSecurityPlug.warn_if_disabled()      Pleroma.Repo.check_migrations_applied!()      setup_instrumenters()      load_custom_modules() diff --git a/lib/pleroma/config/loader.ex b/lib/pleroma/config/loader.ex index 68b247381..b8787cb49 100644 --- a/lib/pleroma/config/loader.ex +++ b/lib/pleroma/config/loader.ex @@ -3,8 +3,6 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Config.Loader do -  @paths ["config/config.exs", "config/#{Mix.env()}.exs"] -    @reject_keys [      Pleroma.Repo,      Pleroma.Web.Endpoint, @@ -35,8 +33,8 @@ defmodule Pleroma.Config.Loader do    def load_and_merge do      all_paths =        if Pleroma.Config.get(:release), -        do: @paths ++ ["config/releases.exs"], -        else: @paths +        do: ["config/config.exs", "config/releases.exs"], +        else: ["config/config.exs"]      all_paths      |> Enum.map(&load(&1)) diff --git a/lib/pleroma/config/transfer_task.ex b/lib/pleroma/config/transfer_task.ex index d54f38ee4..6c5ba1f95 100644 --- a/lib/pleroma/config/transfer_task.ex +++ b/lib/pleroma/config/transfer_task.ex @@ -10,6 +10,30 @@ defmodule Pleroma.Config.TransferTask do    require Logger +  @type env() :: :test | :benchmark | :dev | :prod + +  @reboot_time_keys [ +    {:pleroma, :hackney_pools}, +    {:pleroma, :chat}, +    {:pleroma, Oban}, +    {:pleroma, :rate_limit}, +    {:pleroma, :markup}, +    {:plerome, :streamer} +  ] + +  @reboot_time_subkeys [ +    {:pleroma, Pleroma.Captcha, [:seconds_valid]}, +    {:pleroma, Pleroma.Upload, [:proxy_remote]}, +    {:pleroma, :instance, [:upload_limit]}, +    {:pleroma, :email_notifications, [:digest]}, +    {:pleroma, :oauth2, [:clean_expired_tokens]}, +    {:pleroma, Pleroma.ActivityExpiration, [:enabled]}, +    {:pleroma, Pleroma.ScheduledActivity, [:enabled]}, +    {:pleroma, :gopher, [:enabled]} +  ] + +  @reject [nil, :prometheus] +    def start_link(_) do      load_and_update_env()      if Pleroma.Config.get(:env) == :test, do: Ecto.Adapters.SQL.Sandbox.checkin(Repo) @@ -17,21 +41,34 @@ defmodule Pleroma.Config.TransferTask do    end    @spec load_and_update_env([ConfigDB.t()]) :: :ok | false -  def load_and_update_env(deleted \\ []) do +  def load_and_update_env(deleted \\ [], restart_pleroma? \\ true) do      with true <- Pleroma.Config.get(:configurable_from_database),           true <- Ecto.Adapters.SQL.table_exists?(Repo, "config"),           started_applications <- Application.started_applications() do        # We need to restart applications for loaded settings take effect +        in_db = Repo.all(ConfigDB)        with_deleted = in_db ++ deleted -      with_deleted -      |> Enum.map(&merge_and_update(&1)) -      |> Enum.uniq() -      # TODO: some problem with prometheus after restart! -      |> Enum.reject(&(&1 in [:pleroma, nil, :prometheus])) -      |> Enum.each(&restart(started_applications, &1)) +      reject_for_restart = if restart_pleroma?, do: @reject, else: [:pleroma | @reject] + +      applications = +        with_deleted +        |> Enum.map(&merge_and_update(&1)) +        |> Enum.uniq() +        # TODO: some problem with prometheus after restart! +        |> Enum.reject(&(&1 in reject_for_restart)) + +      # to be ensured that pleroma will be restarted last +      applications = +        if :pleroma in applications do +          List.delete(applications, :pleroma) ++ [:pleroma] +        else +          applications +        end + +      Enum.each(applications, &restart(started_applications, &1, Pleroma.Config.get(:env)))        :ok      end @@ -43,12 +80,25 @@ defmodule Pleroma.Config.TransferTask do        group = ConfigDB.from_string(setting.group)        default = Pleroma.Config.Holder.config(group, key) -      merged_value = merge_value(setting, default, group, key) +      value = ConfigDB.from_binary(setting.value) + +      merged_value = +        if Ecto.get_meta(setting, :state) == :deleted do +          default +        else +          if can_be_merged?(default, value) do +            ConfigDB.merge_group(group, key, default, value) +          else +            value +          end +        end        :ok = update_env(group, key, merged_value)        if group != :logger do -        group +        if group != :pleroma or pleroma_need_restart?(group, key, value) do +          group +        end        else          # change logger configuration in runtime, without restart          if Keyword.keyword?(merged_value) and @@ -76,22 +126,31 @@ defmodule Pleroma.Config.TransferTask do      end    end -  defp merge_value(%{__meta__: %{state: :deleted}}, default, _group, _key), do: default +  @spec pleroma_need_restart?(atom(), atom(), any()) :: boolean() +  def pleroma_need_restart?(group, key, value) do +    group_and_key_need_reboot?(group, key) or group_and_subkey_need_reboot?(group, key, value) +  end -  defp merge_value(setting, default, group, key) do -    value = ConfigDB.from_binary(setting.value) +  defp group_and_key_need_reboot?(group, key) do +    Enum.any?(@reboot_time_keys, fn {g, k} -> g == group and k == key end) +  end -    if can_be_merged?(default, value) do -      ConfigDB.merge_group(group, key, default, value) -    else -      value -    end +  defp group_and_subkey_need_reboot?(group, key, value) do +    Keyword.keyword?(value) and +      Enum.any?(@reboot_time_subkeys, fn {g, k, subkeys} -> +        g == group and k == key and +          Enum.any?(Keyword.keys(value), &(&1 in subkeys)) +      end)    end    defp update_env(group, key, nil), do: Application.delete_env(group, key)    defp update_env(group, key, value), do: Application.put_env(group, key, value) -  defp restart(started_applications, app) do +  defp restart(_, :pleroma, :test), do: Logger.warn("pleroma restarted") + +  defp restart(_, :pleroma, _), do: send(Restarter.Pleroma, :after_boot) + +  defp restart(started_applications, app, _) do      with {^app, _, _} <- List.keyfind(started_applications, app, 0),           :ok <- Application.stop(app) do        :ok = Application.start(app) diff --git a/lib/pleroma/following_relationship.ex b/lib/pleroma/following_relationship.ex index 0b0219b82..b8cb3bf03 100644 --- a/lib/pleroma/following_relationship.ex +++ b/lib/pleroma/following_relationship.ex @@ -58,8 +58,8 @@ defmodule Pleroma.FollowingRelationship do    def unfollow(%User{} = follower, %User{} = following) do      case get(follower, following) do -      nil -> {:ok, nil}        %__MODULE__{} = following_relationship -> Repo.delete(following_relationship) +      _ -> {:ok, nil}      end    end diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 19b9af46c..90895374d 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -13,7 +13,8 @@ defmodule Pleroma.Formatter do    @auto_linker_config hashtag: true,                        hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,                        mention: true, -                      mention_handler: &Pleroma.Formatter.mention_handler/4 +                      mention_handler: &Pleroma.Formatter.mention_handler/4, +                      scheme: true    def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do      case User.get_cached_by_nickname(nickname) do diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index e2b75054e..2e4fe2edb 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -323,7 +323,7 @@ defmodule Pleroma.Notification do    end    def create_notifications(%Activity{data: %{"type" => type}} = activity) -      when type in ["Like", "Announce", "Follow", "Move", "EmojiReaction"] do +      when type in ["Like", "Announce", "Follow", "Move", "EmojiReact"] do      notifications =        activity        |> get_notified_from_activity() @@ -354,7 +354,7 @@ defmodule Pleroma.Notification do    def get_notified_from_activity(activity, local_only \\ true)    def get_notified_from_activity(%Activity{data: %{"type" => type}} = activity, local_only) -      when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReaction"] do +      when type in ["Create", "Like", "Announce", "Follow", "Move", "EmojiReact"] do      []      |> Utils.maybe_notify_to_recipients(activity)      |> Utils.maybe_notify_mentioned_recipients(activity) diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 38e372f6d..52556bf31 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -184,11 +184,14 @@ defmodule Pleroma.Object do      with {:ok, _obj} = swap_object_with_tombstone(object),           deleted_activity = Activity.delete_all_by_object_ap_id(id),           {:ok, true} <- Cachex.del(:object_cache, "object:#{id}"), -         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path), -         {:ok, _} <- -           Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ -             "object" => object -           }) do +         {:ok, _} <- Cachex.del(:web_resp_cache, URI.parse(id).path) do +      with true <- Pleroma.Config.get([:instance, :cleanup_attachments]) do +        {:ok, _} = +          Pleroma.Workers.AttachmentsCleanupWorker.enqueue("cleanup_attachments", %{ +            "object" => object +          }) +      end +        {:ok, object, deleted_activity}      end    end diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index a7cc22831..b04273979 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -6,6 +6,8 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do    alias Pleroma.Config    import Plug.Conn +  require Logger +    def init(opts), do: opts    def call(conn, _options) do @@ -90,6 +92,51 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do      |> Enum.join("; ")    end +  def warn_if_disabled do +    unless Config.get([:http_security, :enabled]) do +      Logger.warn(" +                                 .i;;;;i. +                               iYcviii;vXY: +                             .YXi       .i1c. +                            .YC.     .    in7. +                           .vc.   ......   ;1c. +                           i7,   ..        .;1; +                          i7,   .. ...      .Y1i +                         ,7v     .6MMM@;     .YX, +                        .7;.   ..IMMMMMM1     :t7. +                       .;Y.     ;$MMMMMM9.     :tc. +                       vY.   .. .nMMM@MMU.      ;1v. +                      i7i   ...  .#MM@M@C. .....:71i +                     it:   ....   $MMM@9;.,i;;;i,;tti +                    :t7.  .....   0MMMWv.,iii:::,,;St. +                   .nC.   .....   IMMMQ..,::::::,.,czX. +                  .ct:   ....... .ZMMMI..,:::::::,,:76Y. +                  c2:   ......,i..Y$M@t..:::::::,,..inZY +                 vov   ......:ii..c$MBc..,,,,,,,,,,..iI9i +                i9Y   ......iii:..7@MA,..,,,,,,,,,....;AA: +               iIS.  ......:ii::..;@MI....,............;Ez. +              .I9.  ......:i::::...8M1..................C0z. +             .z9;  ......:i::::,.. .i:...................zWX. +             vbv  ......,i::::,,.      ................. :AQY +            c6Y.  .,...,::::,,..:t0@@QY. ................ :8bi +           :6S. ..,,...,:::,,,..EMMMMMMI. ............... .;bZ, +          :6o,  .,,,,..:::,,,..i#MMMMMM#v.................  YW2. +         .n8i ..,,,,,,,::,,,,.. tMMMMM@C:.................. .1Wn +         7Uc. .:::,,,,,::,,,,..   i1t;,..................... .UEi +         7C...::::::::::::,,,,..        ....................  vSi. +         ;1;...,,::::::,.........       ..................    Yz: +          v97,.........                                     .voC. +           izAotX7777777777777777777777777777777777777777Y7n92: +             .;CoIIIIIUAA666666699999ZZZZZZZZZZZZZZZZZZZZ6ov. + +HTTP Security is disabled. Please re-enable it to prevent users from attacking +your instance and your users via malicious posts: + +      config :pleroma, :http_security, enabled: true +      ") +    end +  end +    defp maybe_send_sts_header(conn, true) do      max_age_sts = Config.get([:http_security, :sts_max_age])      max_age_ct = Config.get([:http_security, :ct_max_age]) diff --git a/lib/pleroma/plugs/parsers_plug.ex b/lib/pleroma/plugs/parsers_plug.ex deleted file mode 100644 index 2e493ce0e..000000000 --- a/lib/pleroma/plugs/parsers_plug.ex +++ /dev/null @@ -1,21 +0,0 @@ -# Pleroma: A lightweight social networking server -# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> -# SPDX-License-Identifier: AGPL-3.0-only - -defmodule Pleroma.Plugs.Parsers do -  @moduledoc "Initializes Plug.Parsers with upload limit set at boot time" - -  @behaviour Plug - -  def init(_opts) do -    Plug.Parsers.init( -      parsers: [:urlencoded, :multipart, :json], -      pass: ["*/*"], -      json_decoder: Jason, -      length: Pleroma.Config.get([:instance, :upload_limit]), -      body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} -    ) -  end - -  defdelegate call(conn, opts), to: Plug.Parsers -end diff --git a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex index d720508c8..7fb92489c 100644 --- a/lib/pleroma/plugs/rate_limiter/rate_limiter.ex +++ b/lib/pleroma/plugs/rate_limiter/rate_limiter.ex @@ -67,6 +67,8 @@ defmodule Pleroma.Plugs.RateLimiter do    alias Pleroma.Plugs.RateLimiter.LimiterSupervisor    alias Pleroma.User +  require Logger +    def init(opts) do      limiter_name = Keyword.get(opts, :name) @@ -89,18 +91,39 @@ defmodule Pleroma.Plugs.RateLimiter do    def call(conn, nil), do: conn    def call(conn, settings) do -    settings -    |> incorporate_conn_info(conn) -    |> check_rate() -    |> case do -      {:ok, _count} -> +    case disabled?() do +      true -> +        if Pleroma.Config.get(:env) == :prod, +          do: Logger.warn("Rate limiter is disabled for localhost/socket") +          conn -      {:error, _count} -> -        render_throttled_error(conn) +      false -> +        settings +        |> incorporate_conn_info(conn) +        |> check_rate() +        |> case do +          {:ok, _count} -> +            conn + +          {:error, _count} -> +            render_throttled_error(conn) +        end      end    end +  def disabled? do +    localhost_or_socket = +      Pleroma.Config.get([Pleroma.Web.Endpoint, :http, :ip]) +      |> Tuple.to_list() +      |> Enum.join(".") +      |> String.match?(~r/^local|^127.0.0.1/) + +    remote_ip_disabled = not Pleroma.Config.get([Pleroma.Plugs.RemoteIp, :enabled]) + +    localhost_or_socket and remote_ip_disabled +  end +    def inspect_bucket(conn, name_root, settings) do      settings =        settings diff --git a/lib/pleroma/plugs/remote_ip.ex b/lib/pleroma/plugs/remote_ip.ex index fdedc27ee..1cd5af48a 100644 --- a/lib/pleroma/plugs/remote_ip.ex +++ b/lib/pleroma/plugs/remote_ip.ex @@ -10,10 +10,7 @@ defmodule Pleroma.Plugs.RemoteIp do    @behaviour Plug    @headers ~w[ -    forwarded      x-forwarded-for -    x-client-ip -    x-real-ip    ]    # https://en.wikipedia.org/wiki/Localhost diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 3c86cdb38..5ea36fea3 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -647,25 +647,48 @@ defmodule Pleroma.User do      end    end +  def unfollow(%User{ap_id: ap_id}, %User{ap_id: ap_id}) do +    {:error, "Not subscribed!"} +  end +    def unfollow(%User{} = follower, %User{} = followed) do -    if following?(follower, followed) and follower.ap_id != followed.ap_id do -      FollowingRelationship.unfollow(follower, followed) +    case get_follow_state(follower, followed) do +      state when state in ["accept", "pending"] -> +        FollowingRelationship.unfollow(follower, followed) +        {:ok, followed} = update_follower_count(followed) -      {:ok, followed} = update_follower_count(followed) +        {:ok, follower} = +          follower +          |> update_following_count() +          |> set_cache() -      {:ok, follower} = -        follower -        |> update_following_count() -        |> set_cache() +        {:ok, follower, Utils.fetch_latest_follow(follower, followed)} -      {:ok, follower, Utils.fetch_latest_follow(follower, followed)} -    else -      {:error, "Not subscribed!"} +      nil -> +        {:error, "Not subscribed!"}      end    end    defdelegate following?(follower, followed), to: FollowingRelationship +  def get_follow_state(%User{} = follower, %User{} = following) do +    following_relationship = FollowingRelationship.get(follower, following) + +    case {following_relationship, following.local} do +      {nil, false} -> +        case Utils.fetch_latest_follow(follower, following) do +          %{data: %{"state" => state}} when state in ["pending", "accept"] -> state +          _ -> nil +        end + +      {%{state: state}, _} -> +        state + +      {nil, _} -> +        nil +    end +  end +    def locked?(%User{} = user) do      user.locked || false    end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 1ac67b618..5c436941a 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -325,12 +325,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    def react_with_emoji(user, object, emoji, options \\ []) do      with local <- Keyword.get(options, :local, true),           activity_id <- Keyword.get(options, :activity_id, nil), -         Pleroma.Emoji.is_unicode_emoji?(emoji), +         true <- Pleroma.Emoji.is_unicode_emoji?(emoji),           reaction_data <- make_emoji_reaction_data(user, object, emoji, activity_id),           {:ok, activity} <- insert(reaction_data, local),           {:ok, object} <- add_emoji_reaction_to_object(activity, object),           :ok <- maybe_federate(activity) do        {:ok, activity, object} +    else +      e -> {:error, e}      end    end @@ -345,6 +347,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do           {:ok, object} <- remove_emoji_reaction_from_object(reaction_activity, object),           :ok <- maybe_federate(activity) do        {:ok, activity, object} +    else +      e -> {:error, e}      end    end diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 2b8bfc3bd..a72d8430f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -580,7 +580,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      "star" => "⭐"    } -  @doc "Rewrite misskey likes into EmojiReactions" +  @doc "Rewrite misskey likes into EmojiReacts"    def handle_incoming(          %{            "type" => "Like", @@ -589,7 +589,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          options        ) do      data -    |> Map.put("type", "EmojiReaction") +    |> Map.put("type", "EmojiReact")      |> Map.put("content", @misskey_reactions[reaction] || reaction)      |> handle_incoming(options)    end @@ -610,7 +610,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def handle_incoming(          %{ -          "type" => "EmojiReaction", +          "type" => "EmojiReact",            "object" => object_id,            "actor" => _actor,            "id" => id, @@ -751,7 +751,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def handle_incoming(          %{            "type" => "Undo", -          "object" => %{"type" => "EmojiReaction", "id" => reaction_activity_id}, +          "object" => %{"type" => "EmojiReact", "id" => reaction_activity_id},            "actor" => _actor,            "id" => id          } = data, diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 4f7fdaf38..10ce5eee8 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -308,7 +308,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    def make_emoji_reaction_data(user, object, emoji, activity_id) do      make_like_data(user, object, activity_id) -    |> Map.put("type", "EmojiReaction") +    |> Map.put("type", "EmojiReact")      |> Map.put("content", emoji)    end @@ -490,10 +490,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do      |> Repo.one()    end +  def fetch_latest_undo(%User{ap_id: ap_id}) do +    "Undo" +    |> Activity.Queries.by_type() +    |> where(actor: ^ap_id) +    |> order_by([activity], fragment("? desc nulls last", activity.id)) +    |> limit(1) +    |> Repo.one() +  end +    def get_latest_reaction(internal_activity_id, %{ap_id: ap_id}, emoji) do      %{data: %{"object" => object_ap_id}} = Activity.get_by_id(internal_activity_id) -    "EmojiReaction" +    "EmojiReact"      |> Activity.Queries.by_type()      |> where(actor: ^ap_id)      |> where([activity], fragment("?->>'content' = ?", activity.data, ^emoji)) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 2314d3274..c95cd182d 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -97,7 +97,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    plug(      OAuthScopesPlug,      %{scopes: ["read"], admin: true} -    when action in [:config_show, :migrate_from_db, :list_log] +    when action in [:config_show, :list_log]    )    plug( @@ -793,33 +793,13 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      |> Plug.Conn.send_resp(200, @descriptions_json)    end -  def migrate_from_db(conn, _params) do -    with :ok <- configurable_from_database(conn) do -      Mix.Tasks.Pleroma.Config.run([ -        "migrate_from_db", -        "--env", -        to_string(Pleroma.Config.get(:env)), -        "-d" -      ]) - -      json(conn, %{}) -    end -  end -    def config_show(conn, %{"only_db" => true}) do      with :ok <- configurable_from_database(conn) do        configs = Pleroma.Repo.all(ConfigDB) -      if configs == [] do -        errors( -          conn, -          {:error, "To use configuration from database migrate your settings to database."} -        ) -      else -        conn -        |> put_view(ConfigView) -        |> render("index.json", %{configs: configs}) -      end +      conn +      |> put_view(ConfigView) +      |> render("index.json", %{configs: configs})      end    end @@ -827,45 +807,38 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      with :ok <- configurable_from_database(conn) do        configs = ConfigDB.get_all_as_keyword() -      if configs == [] do -        errors( -          conn, -          {:error, "To use configuration from database migrate your settings to database."} -        ) -      else -        merged = -          Pleroma.Config.Holder.config() -          |> ConfigDB.merge(configs) -          |> Enum.map(fn {group, values} -> -            Enum.map(values, fn {key, value} -> -              db = -                if configs[group][key] do -                  ConfigDB.get_db_keys(configs[group][key], key) -                end - -              db_value = configs[group][key] - -              merged_value = -                if !is_nil(db_value) and Keyword.keyword?(db_value) and -                     ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do -                  ConfigDB.merge_group(group, key, value, db_value) -                else -                  value -                end - -              setting = %{ -                group: ConfigDB.convert(group), -                key: ConfigDB.convert(key), -                value: ConfigDB.convert(merged_value) -              } - -              if db, do: Map.put(setting, :db, db), else: setting -            end) +      merged = +        Pleroma.Config.Holder.config() +        |> ConfigDB.merge(configs) +        |> Enum.map(fn {group, values} -> +          Enum.map(values, fn {key, value} -> +            db = +              if configs[group][key] do +                ConfigDB.get_db_keys(configs[group][key], key) +              end + +            db_value = configs[group][key] + +            merged_value = +              if !is_nil(db_value) and Keyword.keyword?(db_value) and +                   ConfigDB.sub_key_full_update?(group, key, Keyword.keys(db_value)) do +                ConfigDB.merge_group(group, key, value, db_value) +              else +                value +              end + +            setting = %{ +              group: ConfigDB.convert(group), +              key: ConfigDB.convert(key), +              value: ConfigDB.convert(merged_value) +            } + +            if db, do: Map.put(setting, :db, db), else: setting            end) -          |> List.flatten() +        end) +        |> List.flatten() -        json(conn, %{configs: merged}) -      end +      json(conn, %{configs: merged})      end    end @@ -890,17 +863,36 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do            Ecto.get_meta(config, :state) == :deleted          end) -      Pleroma.Config.TransferTask.load_and_update_env(deleted) +      Pleroma.Config.TransferTask.load_and_update_env(deleted, false) + +      need_reboot? = +        Enum.any?(updated, fn config -> +          group = ConfigDB.from_string(config.group) +          key = ConfigDB.from_string(config.key) +          value = ConfigDB.from_binary(config.value) +          Pleroma.Config.TransferTask.pleroma_need_restart?(group, key, value) +        end) + +      response = %{configs: updated} -      Mix.Tasks.Pleroma.Config.run([ -        "migrate_from_db", -        "--env", -        to_string(Pleroma.Config.get(:env)) -      ]) +      response = +        if need_reboot?, do: Map.put(response, :need_reboot, need_reboot?), else: response        conn        |> put_view(ConfigView) -      |> render("index.json", %{configs: updated}) +      |> render("index.json", response) +    end +  end + +  def restart(conn, _params) do +    with :ok <- configurable_from_database(conn) do +      if Pleroma.Config.get(:env) == :test do +        Logger.warn("pleroma restarted") +      else +        send(Restarter.Pleroma, {:restart, 50}) +      end + +      json(conn, %{})      end    end diff --git a/lib/pleroma/web/admin_api/views/config_view.ex b/lib/pleroma/web/admin_api/views/config_view.ex index 23d97e847..bbb53efcd 100644 --- a/lib/pleroma/web/admin_api/views/config_view.ex +++ b/lib/pleroma/web/admin_api/views/config_view.ex @@ -5,10 +5,16 @@  defmodule Pleroma.Web.AdminAPI.ConfigView do    use Pleroma.Web, :view -  def render("index.json", %{configs: configs}) do -    %{ +  def render("index.json", %{configs: configs} = params) do +    map = %{        configs: render_many(configs, __MODULE__, "show.json", as: :config)      } + +    if params[:need_reboot] do +      Map.put(map, :need_reboot, true) +    else +      map +    end    end    def render("show.json", %{config: config}) do diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index c05a6c544..2a348dcf6 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -315,8 +315,9 @@ defmodule Pleroma.Web.CommonAPI do      with %Activity{             actor: ^user_ap_id,             data: %{"type" => "Create"}, -           object: %Object{data: %{"type" => "Note"}} +           object: %Object{data: %{"type" => object_type}}           } = activity <- get_by_id_or_ap_id(id_or_ap_id), +         true <- object_type in ["Note", "Article", "Question"],           true <- Visibility.is_public?(activity),           {:ok, _user} <- User.add_pinnned_activity(user, activity) do        {:ok, activity} diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index a9b164d9a..ca6c93862 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -179,9 +179,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do          end)        end_time = -        NaiveDateTime.utc_now() -        |> NaiveDateTime.add(expires_in) -        |> NaiveDateTime.to_iso8601() +        DateTime.utc_now() +        |> DateTime.add(expires_in) +        |> DateTime.to_iso8601()        key = if truthy_param?(data["poll"]["multiple"]), do: "anyOf", else: "oneOf"        poll = %{"type" => "Question", key => option_notes, "closed" => end_time} diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index d32c38a05..a77b73109 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -61,7 +61,17 @@ defmodule Pleroma.Web.Endpoint do    plug(Plug.RequestId)    plug(Plug.Logger, log: :debug) -  plug(Pleroma.Plugs.Parsers) +  plug(Plug.Parsers, +    parsers: [ +      :urlencoded, +      {:multipart, length: {Pleroma.Config, :get, [[:instance, :upload_limit]]}}, +      :json +    ], +    pass: ["*/*"], +    json_decoder: Jason, +    length: Pleroma.Config.get([:instance, :upload_limit]), +    body_reader: {Pleroma.Web.Plugs.DigestPlug, :read_body, []} +  )    plug(Plug.MethodOverride)    plug(Plug.Head) diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index a5420f480..c6d37ead7 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -67,7 +67,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do    end    defp do_render("show.json", %{user: user} = opts) do -    display_name = HTML.strip_tags(user.name || user.nickname) +    display_name = user.name || user.nickname      image = User.avatar_url(user) |> MediaProxy.url()      header = User.banner_url(user) |> MediaProxy.url() @@ -105,7 +105,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do        |> User.fields()        |> Enum.map(fn %{"name" => name, "value" => value} ->          %{ -          "name" => Pleroma.HTML.strip_tags(name), +          "name" => name,            "value" => Pleroma.HTML.filter_tags(value, Pleroma.HTML.Scrubber.LinksOnly)          }        end) diff --git a/lib/pleroma/web/mastodon_api/views/poll_view.ex b/lib/pleroma/web/mastodon_api/views/poll_view.ex index 753039da3..6bb3652fb 100644 --- a/lib/pleroma/web/mastodon_api/views/poll_view.ex +++ b/lib/pleroma/web/mastodon_api/views/poll_view.ex @@ -5,7 +5,6 @@  defmodule Pleroma.Web.MastodonAPI.PollView do    use Pleroma.Web, :view -  alias Pleroma.HTML    alias Pleroma.Web.CommonAPI.Utils    def render("show.json", %{object: object, multiple: multiple, options: options} = params) do @@ -57,7 +56,7 @@ defmodule Pleroma.Web.MastodonAPI.PollView do        current_count = option["replies"]["totalItems"] || 0        {%{ -         title: HTML.strip_tags(name), +         title: name,           votes_count: current_count         }, current_count + count}      end) diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index e60ef709b..6cb158bbf 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -216,21 +216,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      summary = object.data["summary"] || "" -    summary_html = -      summary -      |> HTML.get_cached_scrubbed_html_for_activity( -        User.html_filter_policy(opts[:for]), -        activity, -        "mastoapi:summary" -      ) - -    summary_plaintext = -      summary -      |> HTML.get_cached_stripped_html_for_activity( -        activity, -        "mastoapi:summary" -      ) -      card = render("card.json", Pleroma.Web.RichMedia.Helpers.fetch_data_for_activity(activity))      url = @@ -256,7 +241,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      emoji_reactions =        with %{data: %{"reactions" => emoji_reactions}} <- object do          Enum.map(emoji_reactions, fn [emoji, users] -> -          %{emoji: emoji, count: length(users)} +          %{ +            emoji: emoji, +            count: length(users), +            reacted: !!(opts[:for] && opts[:for].ap_id in users) +          }          end)        else          _ -> [] @@ -282,7 +271,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        muted: thread_muted? || User.mutes?(opts[:for], user),        pinned: pinned?(activity, user),        sensitive: sensitive, -      spoiler_text: summary_html, +      spoiler_text: summary,        visibility: get_visibility(object),        media_attachments: attachments,        poll: render(PollView, "show.json", object: object, for: opts[:for]), @@ -299,7 +288,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do          conversation_id: get_context_id(activity),          in_reply_to_account_acct: reply_to_user && reply_to_user.nickname,          content: %{"text/plain" => content_plaintext}, -        spoiler_text: %{"text/plain" => summary_plaintext}, +        spoiler_text: %{"text/plain" => summary},          expires_at: expires_at,          direct_conversation_id: direct_conversation_id,          thread_muted: thread_muted?, diff --git a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex index 0bbf84fd3..a2f6d2287 100644 --- a/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/emoji_api_controller.ex @@ -573,11 +573,14 @@ keeping it in cache for #{div(cache_ms, 1000)}s")    assumed to be emojis and stored in the new `pack.json` file.    """    def import_from_fs(conn, _params) do -    with {:ok, results} <- File.ls(emoji_dir_path()) do +    emoji_path = emoji_dir_path() + +    with {:ok, %{access: :read_write}} <- File.stat(emoji_path), +         {:ok, results} <- File.ls(emoji_path) do        imported_pack_names =          results          |> Enum.filter(fn file -> -          dir_path = Path.join(emoji_dir_path(), file) +          dir_path = Path.join(emoji_path, file)            # Find the directories that do NOT have pack.json            File.dir?(dir_path) and not File.exists?(Path.join(dir_path, "pack.json"))          end) @@ -585,6 +588,11 @@ keeping it in cache for #{div(cache_ms, 1000)}s")        json(conn, imported_pack_names)      else +      {:ok, %{access: _}} -> +        conn +        |> put_status(:internal_server_error) +        |> json(%{error: "Error: emoji pack directory must be writable"}) +        {:error, _} ->          conn          |> put_status(:internal_server_error) diff --git a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex index cd1c0764f..d76e39795 100644 --- a/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex +++ b/lib/pleroma/web/pleroma_api/controllers/pleroma_api_controller.ex @@ -47,13 +47,16 @@ defmodule Pleroma.Web.PleromaAPI.PleromaAPIController do             Object.normalize(activity) do        reactions =          emoji_reactions -        |> Enum.map(fn [emoji, users] -> -          users = Enum.map(users, &User.get_cached_by_ap_id/1) +        |> Enum.map(fn [emoji, user_ap_ids] -> +          users = +            Enum.map(user_ap_ids, &User.get_cached_by_ap_id/1) +            |> Enum.filter(& &1)            %{              emoji: emoji,              count: length(users), -            accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}) +            accounts: AccountView.render("index.json", %{users: users, for: user, as: :user}), +            reacted: !!(user && user.ap_id in user_ap_ids)            }          end) diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex index c2a3c07a2..fae3c462e 100644 --- a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -48,6 +48,6 @@ defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do    defp maybe_put_title(meta, _), do: meta    defp get_page_title(html) do -    Floki.find(html, "title") |> List.first() |> Floki.text() +    Floki.find(html, "html head title") |> List.first() |> Floki.text()    end  end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index b5c1d85c7..e86bc3cc3 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -196,7 +196,7 @@ defmodule Pleroma.Web.Router do      get("/config", AdminAPIController, :config_show)      post("/config", AdminAPIController, :config_update)      get("/config/descriptions", AdminAPIController, :config_descriptions) -    get("/config/migrate_from_db", AdminAPIController, :migrate_from_db) +    get("/restart", AdminAPIController, :restart)      get("/moderation_log", AdminAPIController, :list_log) diff --git a/lib/pleroma/web/streamer/worker.ex b/lib/pleroma/web/streamer/worker.ex index a1b445f2f..5392c1ec3 100644 --- a/lib/pleroma/web/streamer/worker.ex +++ b/lib/pleroma/web/streamer/worker.ex @@ -138,7 +138,8 @@ defmodule Pleroma.Web.Streamer.Worker do      with parent <- Object.normalize(item) || item,           true <- -           Enum.all?([blocked_ap_ids, muted_ap_ids, reblog_muted_ap_ids], &(item.actor not in &1)), +           Enum.all?([blocked_ap_ids, muted_ap_ids], &(item.actor not in &1)), +         true <- item.data["type"] != "Announce" || item.actor not in reblog_muted_ap_ids,           true <- Enum.all?([blocked_ap_ids, muted_ap_ids], &(parent.data["actor"] not in &1)),           true <- MapSet.disjoint?(recipients, recipient_blocks),           %{host: item_host} <- URI.parse(item.actor),  | 
