diff options
Diffstat (limited to 'lib')
47 files changed, 688 insertions, 465 deletions
| diff --git a/lib/mix/tasks/pleroma/config.ex b/lib/mix/tasks/pleroma/config.ex index a7d0fac5d..462940e7e 100644 --- a/lib/mix/tasks/pleroma/config.ex +++ b/lib/mix/tasks/pleroma/config.ex @@ -15,7 +15,7 @@ defmodule Mix.Tasks.Pleroma.Config do        mix pleroma.config migrate_to_db -  ## Transfers config from DB to file. +  ## Transfers config from DB to file `config/env.exported_from_db.secret.exs`        mix pleroma.config migrate_from_db ENV    """ diff --git a/lib/mix/tasks/pleroma/database.ex b/lib/mix/tasks/pleroma/database.ex index e91fb31d1..8547a329a 100644 --- a/lib/mix/tasks/pleroma/database.ex +++ b/lib/mix/tasks/pleroma/database.ex @@ -8,6 +8,7 @@ defmodule Mix.Tasks.Pleroma.Database do    alias Pleroma.Repo    alias Pleroma.User    require Logger +  require Pleroma.Constants    import Mix.Pleroma    use Mix.Task @@ -99,10 +100,15 @@ defmodule Mix.Tasks.Pleroma.Database do        NaiveDateTime.utc_now()        |> NaiveDateTime.add(-(deadline * 86_400)) -    public = "https://www.w3.org/ns/activitystreams#Public" -      from(o in Object, -      where: fragment("?->'to' \\? ? OR ?->'cc' \\? ?", o.data, ^public, o.data, ^public), +      where: +        fragment( +          "?->'to' \\? ? OR ?->'cc' \\? ?", +          o.data, +          ^Pleroma.Constants.as_public(), +          o.data, +          ^Pleroma.Constants.as_public() +        ),        where: o.inserted_at < ^time_deadline,        where:          fragment("split_part(?->>'actor', '/', 3) != ?", o.data, ^Pleroma.Web.Endpoint.host()) diff --git a/lib/pleroma/activity/search.ex b/lib/pleroma/activity/search.ex index 0cc3770a7..f847ac238 100644 --- a/lib/pleroma/activity/search.ex +++ b/lib/pleroma/activity/search.ex @@ -9,6 +9,8 @@ defmodule Pleroma.Activity.Search do    alias Pleroma.User    alias Pleroma.Web.ActivityPub.Visibility +  require Pleroma.Constants +    import Ecto.Query    def search(user, search_query, options \\ []) do @@ -39,7 +41,7 @@ defmodule Pleroma.Activity.Search do    defp restrict_public(q) do      from([a, o] in q,        where: fragment("?->>'type' = 'Create'", a.data), -      where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients +      where: ^Pleroma.Constants.as_public() in a.recipients      )    end diff --git a/lib/pleroma/constants.ex b/lib/pleroma/constants.ex new file mode 100644 index 000000000..ef1418543 --- /dev/null +++ b/lib/pleroma/constants.ex @@ -0,0 +1,9 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Constants do +  use Const + +  const(as_public, do: "https://www.w3.org/ns/activitystreams#Public") +end diff --git a/lib/pleroma/flake_id.ex b/lib/pleroma/flake_id.ex index 58ab3650d..ca0610abc 100644 --- a/lib/pleroma/flake_id.ex +++ b/lib/pleroma/flake_id.ex @@ -66,6 +66,16 @@ defmodule Pleroma.FlakeId do    @spec get :: binary    def get, do: to_string(:gen_server.call(:flake, :get)) +  # checks that ID is is valid FlakeID +  # +  @spec is_flake_id?(String.t()) :: boolean +  def is_flake_id?(id), do: is_flake_id?(String.to_charlist(id), true) +  defp is_flake_id?([c | cs], true) when c >= ?0 and c <= ?9, do: is_flake_id?(cs, true) +  defp is_flake_id?([c | cs], true) when c >= ?A and c <= ?Z, do: is_flake_id?(cs, true) +  defp is_flake_id?([c | cs], true) when c >= ?a and c <= ?z, do: is_flake_id?(cs, true) +  defp is_flake_id?([], true), do: true +  defp is_flake_id?(_, _), do: false +    # -- Ecto.Type API    @impl Ecto.Type    def type, do: :uuid diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex index a1460d303..7e2c6f5e8 100644 --- a/lib/pleroma/http/connection.ex +++ b/lib/pleroma/http/connection.ex @@ -11,6 +11,7 @@ defmodule Pleroma.HTTP.Connection do      connect_timeout: 10_000,      recv_timeout: 20_000,      follow_redirect: true, +    force_redirect: true,      pool: :federation    ]    @adapter Application.get_env(:tesla, :adapter) diff --git a/lib/pleroma/object/fetcher.ex b/lib/pleroma/object/fetcher.ex index 305ce8357..8d79ddb1f 100644 --- a/lib/pleroma/object/fetcher.ex +++ b/lib/pleroma/object/fetcher.ex @@ -114,7 +114,7 @@ defmodule Pleroma.Object.Fetcher do      end    end -  def fetch_and_contain_remote_object_from_id(id) do +  def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do      Logger.info("Fetching object #{id} via AP")      date = @@ -141,4 +141,9 @@ defmodule Pleroma.Object.Fetcher do          {:error, e}      end    end + +  def fetch_and_contain_remote_object_from_id(%{"id" => id}), +    do: fetch_and_contain_remote_object_from_id(id) + +  def fetch_and_contain_remote_object_from_id(_id), do: {:error, "id must be a string"}  end diff --git a/lib/pleroma/plugs/set_format_plug.ex b/lib/pleroma/plugs/set_format_plug.ex new file mode 100644 index 000000000..5ca741c64 --- /dev/null +++ b/lib/pleroma/plugs/set_format_plug.ex @@ -0,0 +1,24 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Plugs.SetFormatPlug do +  import Plug.Conn, only: [assign: 3, fetch_query_params: 1] + +  def init(_), do: nil + +  def call(conn, _) do +    case get_format(conn) do +      nil -> conn +      format -> assign(conn, :format, format) +    end +  end + +  defp get_format(conn) do +    conn.private[:phoenix_format] || +      case fetch_query_params(conn) do +        %{query_params: %{"_format" => format}} -> format +        _ -> nil +      end +  end +end diff --git a/lib/pleroma/signature.ex b/lib/pleroma/signature.ex index 0bf49fd7c..15bf3c317 100644 --- a/lib/pleroma/signature.ex +++ b/lib/pleroma/signature.ex @@ -15,7 +15,7 @@ defmodule Pleroma.Signature do        |> Map.put(:fragment, nil)      uri = -      if String.ends_with?(uri.path, "/publickey") do +      if not is_nil(uri.path) and String.ends_with?(uri.path, "/publickey") do          Map.put(uri, :path, String.replace(uri.path, "/publickey", ""))        else          uri diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index c47d65241..9f0adde5b 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -228,7 +228,14 @@ defmodule Pleroma.Upload do            ""          end -    [base_url, "media", path] +    prefix = +      if is_nil(Pleroma.Config.get([__MODULE__, :base_url])) do +        "media" +      else +        "" +      end + +    [base_url, prefix, path]      |> Path.join()    end diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index e8a3f9663..7d18f099e 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -115,7 +115,9 @@ defmodule Pleroma.User do    def user_info(%User{} = user, args \\ %{}) do      following_count = -      if args[:following_count], do: args[:following_count], else: following_count(user) +      if args[:following_count], +        do: args[:following_count], +        else: user.info.following_count || following_count(user)      follower_count =        if args[:follower_count], do: args[:follower_count], else: user.info.follower_count @@ -227,6 +229,7 @@ defmodule Pleroma.User do      |> put_password_hash    end +  @spec reset_password(User.t(), map) :: {:ok, User.t()} | {:error, Ecto.Changeset.t()}    def reset_password(%User{id: user_id} = user, data) do      multi =        Multi.new() @@ -331,6 +334,7 @@ defmodule Pleroma.User do    def needs_update?(_), do: true +  @spec maybe_direct_follow(User.t(), User.t()) :: {:ok, User.t()} | {:error, String.t()}    def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do      {:ok, follower}    end @@ -405,6 +409,8 @@ defmodule Pleroma.User do          {1, [follower]} = Repo.update_all(q, []) +        follower = maybe_update_following_count(follower) +          {:ok, _} = update_follower_count(followed)          set_cache(follower) @@ -424,6 +430,8 @@ defmodule Pleroma.User do        {1, [follower]} = Repo.update_all(q, []) +      follower = maybe_update_following_count(follower) +        {:ok, followed} = update_follower_count(followed)        set_cache(follower) @@ -472,7 +480,7 @@ defmodule Pleroma.User do    end    def update_and_set_cache(changeset) do -    with {:ok, user} <- Repo.update(changeset) do +    with {:ok, user} <- Repo.update(changeset, stale_error_field: :id) do        set_cache(user)      else        e -> e @@ -708,32 +716,73 @@ defmodule Pleroma.User do      |> update_and_set_cache()    end +  def maybe_fetch_follow_information(user) do +    with {:ok, user} <- fetch_follow_information(user) do +      user +    else +      e -> +        Logger.error("Follower/Following counter update for #{user.ap_id} failed.\n#{inspect(e)}") + +        user +    end +  end + +  def fetch_follow_information(user) do +    with {:ok, info} <- ActivityPub.fetch_follow_information_for_user(user) do +      info_cng = User.Info.follow_information_update(user.info, info) + +      changeset = +        user +        |> change() +        |> put_embed(:info, info_cng) + +      update_and_set_cache(changeset) +    else +      {:error, _} = e -> e +      e -> {:error, e} +    end +  end +    def update_follower_count(%User{} = user) do -    follower_count_query = -      User.Query.build(%{followers: user, deactivated: false}) -      |> select([u], %{count: count(u.id)}) +    if user.local or !Pleroma.Config.get([:instance, :external_user_synchronization]) do +      follower_count_query = +        User.Query.build(%{followers: user, deactivated: false}) +        |> select([u], %{count: count(u.id)}) + +      User +      |> where(id: ^user.id) +      |> join(:inner, [u], s in subquery(follower_count_query)) +      |> update([u, s], +        set: [ +          info: +            fragment( +              "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", +              u.info, +              s.count +            ) +        ] +      ) +      |> select([u], u) +      |> Repo.update_all([]) +      |> case do +        {1, [user]} -> set_cache(user) +        _ -> {:error, user} +      end +    else +      {:ok, maybe_fetch_follow_information(user)} +    end +  end -    User -    |> where(id: ^user.id) -    |> join(:inner, [u], s in subquery(follower_count_query)) -    |> update([u, s], -      set: [ -        info: -          fragment( -            "jsonb_set(?, '{follower_count}', ?::varchar::jsonb, true)", -            u.info, -            s.count -          ) -      ] -    ) -    |> select([u], u) -    |> Repo.update_all([]) -    |> case do -      {1, [user]} -> set_cache(user) -      _ -> {:error, user} +  def maybe_update_following_count(%User{local: false} = user) do +    if Pleroma.Config.get([:instance, :external_user_synchronization]) do +      {:ok, maybe_fetch_follow_information(user)} +    else +      user      end    end +  def maybe_update_following_count(user), do: user +    def remove_duplicated_following(%User{following: following} = user) do      uniq_following = Enum.uniq(following) @@ -883,18 +932,25 @@ defmodule Pleroma.User do    def muted_notifications?(user, %{ap_id: ap_id}),      do: Enum.member?(user.info.muted_notifications, ap_id) -  def blocks?(%User{info: info} = _user, %{ap_id: ap_id}) do -    blocks = info.blocks +  def blocks?(%User{} = user, %User{} = target) do +    blocks_ap_id?(user, target) || blocks_domain?(user, target) +  end -    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(info.domain_blocks) +  def blocks?(nil, _), do: false -    %{host: host} = URI.parse(ap_id) +  def blocks_ap_id?(%User{} = user, %User{} = target) do +    Enum.member?(user.info.blocks, target.ap_id) +  end -    Enum.member?(blocks, ap_id) || -      Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host) +  def blocks_ap_id?(_, _), do: false + +  def blocks_domain?(%User{} = user, %User{} = target) do +    domain_blocks = Pleroma.Web.ActivityPub.MRF.subdomains_regex(user.info.domain_blocks) +    %{host: host} = URI.parse(target.ap_id) +    Pleroma.Web.ActivityPub.MRF.subdomain_match?(domain_blocks, host)    end -  def blocks?(nil, _), do: false +  def blocks_domain?(_, _), do: false    def subscribed_to?(user, %{ap_id: ap_id}) do      with %User{} = target <- get_cached_by_ap_id(ap_id) do diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex index 60b7a82ab..22eb9a182 100644 --- a/lib/pleroma/user/info.ex +++ b/lib/pleroma/user/info.ex @@ -16,6 +16,8 @@ defmodule Pleroma.User.Info do      field(:source_data, :map, default: %{})      field(:note_count, :integer, default: 0)      field(:follower_count, :integer, default: 0) +    # Should be filled in only for remote users +    field(:following_count, :integer, default: nil)      field(:locked, :boolean, default: false)      field(:confirmation_pending, :boolean, default: false)      field(:confirmation_token, :string, default: nil) @@ -248,7 +250,11 @@ defmodule Pleroma.User.Info do        :uri,        :hub,        :topic, -      :salmon +      :salmon, +      :hide_followers, +      :hide_follows, +      :follower_count, +      :following_count      ])    end @@ -259,7 +265,11 @@ defmodule Pleroma.User.Info do        :source_data,        :banner,        :locked, -      :magic_key +      :magic_key, +      :follower_count, +      :following_count, +      :hide_follows, +      :hide_followers      ])    end @@ -373,4 +383,14 @@ defmodule Pleroma.User.Info do      cast(info, params, [:muted_reblogs])    end + +  def follow_information_update(info, params) do +    info +    |> cast(params, [ +      :hide_followers, +      :hide_follows, +      :follower_count, +      :following_count +    ]) +  end  end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index a42c50875..07a65127b 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -23,6 +23,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    import Pleroma.Web.ActivityPub.Visibility    require Logger +  require Pleroma.Constants    # For Announce activities, we filter the recipients based on following status for any actors    # that match actual users.  See issue #164 for more information about why this is necessary. @@ -207,8 +208,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    def stream_out_participations(_, _), do: :noop    def stream_out(activity) do -    public = "https://www.w3.org/ns/activitystreams#Public" -      if activity.data["type"] in ["Create", "Announce", "Delete"] do        object = Object.normalize(activity)        # Do not stream out poll replies @@ -216,7 +215,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do          Pleroma.Web.Streamer.stream("user", activity)          Pleroma.Web.Streamer.stream("list", activity) -        if Enum.member?(activity.data["to"], public) do +        if get_visibility(activity) == "public" do            Pleroma.Web.Streamer.stream("public", activity)            if activity.local do @@ -238,13 +237,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do              end            end          else -          # TODO: Write test, replace with visibility test -          if !Enum.member?(activity.data["cc"] || [], public) && -               !Enum.member?( -                 activity.data["to"], -                 User.get_cached_by_ap_id(activity.data["actor"]).follower_address -               ), -             do: Pleroma.Web.Streamer.stream("direct", activity) +          if get_visibility(activity) == "direct", +            do: Pleroma.Web.Streamer.stream("direct", activity)          end        end      end @@ -514,7 +508,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    defp fetch_activities_for_context_query(context, opts) do -    public = ["https://www.w3.org/ns/activitystreams#Public"] +    public = [Pleroma.Constants.as_public()]      recipients =        if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public @@ -555,7 +549,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    def fetch_public_activities(opts \\ %{}) do -    q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts) +    q = fetch_activities_query([Pleroma.Constants.as_public()], opts)      q      |> restrict_unlisted() @@ -646,10 +640,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    defp user_activities_recipients(%{"reading_user" => reading_user}) do      if reading_user do -      ["https://www.w3.org/ns/activitystreams#Public"] ++ -        [reading_user.ap_id | reading_user.following] +      [Pleroma.Constants.as_public()] ++ [reading_user.ap_id | reading_user.following]      else -      ["https://www.w3.org/ns/activitystreams#Public"] +      [Pleroma.Constants.as_public()]      end    end @@ -834,7 +827,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do          fragment(            "not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",            activity.data, -          ^["https://www.w3.org/ns/activitystreams#Public"] +          ^[Pleroma.Constants.as_public()]          )      )    end @@ -971,7 +964,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do        where:          fragment("? && ?", activity.recipients, ^recipients) or            (fragment("? && ?", activity.recipients, ^recipients_with_public) and -             "https://www.w3.org/ns/activitystreams#Public" in activity.recipients) +             ^Pleroma.Constants.as_public() in activity.recipients)      )    end @@ -1016,10 +1009,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      user_data = %{        ap_id: data["id"],        info: %{ -        "ap_enabled" => true, -        "source_data" => data, -        "banner" => banner, -        "locked" => locked +        ap_enabled: true, +        source_data: data, +        banner: banner, +        locked: locked        },        avatar: avatar,        name: data["name"], @@ -1043,6 +1036,71 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do      {:ok, user_data}    end +  def fetch_follow_information_for_user(user) do +    with {:ok, following_data} <- +           Fetcher.fetch_and_contain_remote_object_from_id(user.following_address), +         following_count when is_integer(following_count) <- following_data["totalItems"], +         {:ok, hide_follows} <- collection_private(following_data), +         {:ok, followers_data} <- +           Fetcher.fetch_and_contain_remote_object_from_id(user.follower_address), +         followers_count when is_integer(followers_count) <- followers_data["totalItems"], +         {:ok, hide_followers} <- collection_private(followers_data) do +      {:ok, +       %{ +         hide_follows: hide_follows, +         follower_count: followers_count, +         following_count: following_count, +         hide_followers: hide_followers +       }} +    else +      {:error, _} = e -> +        e + +      e -> +        {:error, e} +    end +  end + +  defp maybe_update_follow_information(data) do +    with {:enabled, true} <- +           {:enabled, Pleroma.Config.get([:instance, :external_user_synchronization])}, +         {:ok, info} <- fetch_follow_information_for_user(data) do +      info = Map.merge(data.info, info) +      Map.put(data, :info, info) +    else +      {:enabled, false} -> +        data + +      e -> +        Logger.error( +          "Follower/Following counter update for #{data.ap_id} failed.\n" <> inspect(e) +        ) + +        data +    end +  end + +  defp collection_private(data) do +    if is_map(data["first"]) and +         data["first"]["type"] in ["CollectionPage", "OrderedCollectionPage"] do +      {:ok, false} +    else +      with {:ok, %{"type" => type}} when type in ["CollectionPage", "OrderedCollectionPage"] <- +             Fetcher.fetch_and_contain_remote_object_from_id(data["first"]) do +        {:ok, false} +      else +        {:error, {:ok, %{status: code}}} when code in [401, 403] -> +          {:ok, true} + +        {:error, _} = e -> +          e + +        e -> +          {:error, e} +      end +    end +  end +    def user_data_from_user_object(data) do      with {:ok, data} <- MRF.filter(data),           {:ok, data} <- object_to_user_data(data) do @@ -1054,7 +1112,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    def fetch_and_prepare_user_from_ap_id(ap_id) do      with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id), -         {:ok, data} <- user_data_from_user_object(data) do +         {:ok, data} <- user_data_from_user_object(data), +         data <- maybe_update_follow_information(data) do        {:ok, data}      else        e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex index a699f6a7e..377987cf2 100644 --- a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -4,6 +4,9 @@  defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do    alias Pleroma.User + +  require Pleroma.Constants +    @moduledoc "Block messages with too much mentions (configurable)"    @behaviour Pleroma.Web.ActivityPub.MRF @@ -19,12 +22,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do          when follower_collection? and recipients > threshold ->            message            |> Map.put("to", [follower_collection]) -          |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"]) +          |> Map.put("cc", [Pleroma.Constants.as_public()])          {:public, recipients} when recipients > threshold ->            message            |> Map.put("to", []) -          |> Map.put("cc", ["https://www.w3.org/ns/activitystreams#Public"]) +          |> Map.put("cc", [Pleroma.Constants.as_public()])          _ ->            message @@ -51,10 +54,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do      recipients = (message["to"] || []) ++ (message["cc"] || [])      follower_collection = User.get_cached_by_ap_id(message["actor"]).follower_address -    if Enum.member?(recipients, "https://www.w3.org/ns/activitystreams#Public") do +    if Enum.member?(recipients, Pleroma.Constants.as_public()) do        recipients =          recipients -        |> List.delete("https://www.w3.org/ns/activitystreams#Public") +        |> List.delete(Pleroma.Constants.as_public())          |> List.delete(follower_collection)        {:public, length(recipients)} diff --git a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex index d5c341433..4eec8b916 100644 --- a/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/keyword_policy.ex @@ -3,6 +3,8 @@  # SPDX-License-Identifier: AGPL-3.0-only  defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do +  require Pleroma.Constants +    @moduledoc "Reject or Word-Replace messages with a keyword or regex"    @behaviour Pleroma.Web.ActivityPub.MRF @@ -31,12 +33,12 @@ defmodule Pleroma.Web.ActivityPub.MRF.KeywordPolicy do    defp check_ftl_removal(           %{"to" => to, "object" => %{"content" => content, "summary" => summary}} = message         ) do -    if "https://www.w3.org/ns/activitystreams#Public" in to and +    if Pleroma.Constants.as_public() in to and           Enum.any?(Pleroma.Config.get([:mrf_keyword, :federated_timeline_removal]), fn pattern ->             string_matches?(content, pattern) or string_matches?(summary, pattern)           end) do -      to = List.delete(to, "https://www.w3.org/ns/activitystreams#Public") -      cc = ["https://www.w3.org/ns/activitystreams#Public" | message["cc"] || []] +      to = List.delete(to, Pleroma.Constants.as_public()) +      cc = [Pleroma.Constants.as_public() | message["cc"] || []]        message =          message diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index da13fd7c7..457b6ee10 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do    @behaviour Pleroma.Web.ActivityPub.MRF -  @public "https://www.w3.org/ns/activitystreams#Public" +  require Pleroma.Constants    @impl true    def filter(%{"type" => "Create"} = object) do @@ -19,8 +19,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do      # Determine visibility      visibility =        cond do -        @public in object["to"] -> "public" -        @public in object["cc"] -> "unlisted" +        Pleroma.Constants.as_public() in object["to"] -> "public" +        Pleroma.Constants.as_public() in object["cc"] -> "unlisted"          user.follower_address in object["to"] -> "followers"          true -> "direct"        end diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 2cf63d3db..f266457e3 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -8,6 +8,8 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do    @moduledoc "Filter activities depending on their origin instance"    @behaviour MRF +  require Pleroma.Constants +    defp check_accept(%{host: actor_host} = _actor_info, object) do      accepts =        Pleroma.Config.get([:mrf_simple, :accept]) @@ -89,14 +91,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do      object =        with true <- MRF.subdomain_match?(timeline_removal, actor_host),             user <- User.get_cached_by_ap_id(object["actor"]), -           true <- "https://www.w3.org/ns/activitystreams#Public" in object["to"] do -        to = -          List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++ -            [user.follower_address] - -        cc = -          List.delete(object["cc"], user.follower_address) ++ -            ["https://www.w3.org/ns/activitystreams#Public"] +           true <- Pleroma.Constants.as_public() in object["to"] do +        to = List.delete(object["to"], Pleroma.Constants.as_public()) ++ [user.follower_address] + +        cc = List.delete(object["cc"], user.follower_address) ++ [Pleroma.Constants.as_public()]          object          |> Map.put("to", to) diff --git a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex index b42c4ed76..70edf4f7f 100644 --- a/lib/pleroma/web/activity_pub/mrf/tag_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/tag_policy.ex @@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do       - `mrf_tag:disable-any-subscription`: Reject any follow requests    """ -  @public "https://www.w3.org/ns/activitystreams#Public" +  require Pleroma.Constants    defp get_tags(%User{tags: tags}) when is_list(tags), do: tags    defp get_tags(_), do: [] @@ -70,9 +70,9 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do         ) do      user = User.get_cached_by_ap_id(actor) -    if Enum.member?(to, @public) do -      to = List.delete(to, @public) ++ [user.follower_address] -      cc = List.delete(cc, user.follower_address) ++ [@public] +    if Enum.member?(to, Pleroma.Constants.as_public()) do +      to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address] +      cc = List.delete(cc, user.follower_address) ++ [Pleroma.Constants.as_public()]        object =          object @@ -103,9 +103,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.TagPolicy do         ) do      user = User.get_cached_by_ap_id(actor) -    if Enum.member?(to, @public) or Enum.member?(cc, @public) do -      to = List.delete(to, @public) ++ [user.follower_address] -      cc = List.delete(cc, @public) +    if Enum.member?(to, Pleroma.Constants.as_public()) or +         Enum.member?(cc, Pleroma.Constants.as_public()) do +      to = List.delete(to, Pleroma.Constants.as_public()) ++ [user.follower_address] +      cc = List.delete(cc, Pleroma.Constants.as_public())        object =          object diff --git a/lib/pleroma/web/activity_pub/publisher.ex b/lib/pleroma/web/activity_pub/publisher.ex index 016d78216..46edab0bd 100644 --- a/lib/pleroma/web/activity_pub/publisher.ex +++ b/lib/pleroma/web/activity_pub/publisher.ex @@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.Publisher do    alias Pleroma.Web.ActivityPub.Relay    alias Pleroma.Web.ActivityPub.Transmogrifier +  require Pleroma.Constants +    import Pleroma.Web.ActivityPub.Visibility    @behaviour Pleroma.Web.Federator.Publisher @@ -117,8 +119,6 @@ defmodule Pleroma.Web.ActivityPub.Publisher do      |> Enum.map(& &1.ap_id)    end -  @as_public "https://www.w3.org/ns/activitystreams#Public" -    defp maybe_use_sharedinbox(%User{info: %{source_data: data}}),      do: (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] @@ -145,7 +145,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do        type == "Delete" ->          maybe_use_sharedinbox(user) -      @as_public in to || @as_public in cc -> +      Pleroma.Constants.as_public() in to || Pleroma.Constants.as_public() in cc ->          maybe_use_sharedinbox(user)        length(to) + length(cc) > 1 -> diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 602ae48e1..5403b71d8 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    import Ecto.Query    require Logger +  require Pleroma.Constants    @doc """    Modifies an incoming AP object (mastodon format) to our internal format. @@ -102,8 +103,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      follower_collection = User.get_cached_by_ap_id(Containment.get_actor(object)).follower_address -    explicit_mentions = -      explicit_mentions ++ ["https://www.w3.org/ns/activitystreams#Public", follower_collection] +    explicit_mentions = explicit_mentions ++ [Pleroma.Constants.as_public(), follower_collection]      fix_explicit_addressing(object, explicit_mentions, follower_collection)    end @@ -115,11 +115,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      if followers_collection not in recipients do        cond do -        "https://www.w3.org/ns/activitystreams#Public" in cc -> +        Pleroma.Constants.as_public() in cc ->            to = to ++ [followers_collection]            Map.put(object, "to", to) -        "https://www.w3.org/ns/activitystreams#Public" in to -> +        Pleroma.Constants.as_public() in to ->            cc = cc ++ [followers_collection]            Map.put(object, "cc", cc) @@ -480,8 +480,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do           {:ok, %User{} = follower} <- User.get_or_fetch_by_ap_id(follower),           {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do        with deny_follow_blocked <- Pleroma.Config.get([:user, :deny_follow_blocked]), -           {_, false} <- -             {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked}, +           {_, false} <- {:user_blocked, User.blocks?(followed, follower) && deny_follow_blocked},             {_, false} <- {:user_locked, User.locked?(followed)},             {_, {:ok, follower}} <- {:follow, User.follow(follower, followed)},             {_, {:ok, _}} <- @@ -609,13 +608,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      with %User{ap_id: ^actor_id} = actor <- User.get_cached_by_ap_id(object["id"]) do        {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) -      banner = new_user_data[:info]["banner"] -      locked = new_user_data[:info]["locked"] || false +      banner = new_user_data[:info][:banner] +      locked = new_user_data[:info][:locked] || false        update_data =          new_user_data          |> Map.take([:name, :bio, :avatar]) -        |> Map.put(:info, %{"banner" => banner, "locked" => locked}) +        |> Map.put(:info, %{banner: banner, locked: locked})        actor        |> User.upgrade_changeset(update_data) @@ -656,20 +655,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        nil ->          case User.get_cached_by_ap_id(object_id) do            %User{ap_id: ^actor} = user -> -            {:ok, followers} = User.get_followers(user) - -            Enum.each(followers, fn follower -> -              User.unfollow(follower, user) -            end) - -            {:ok, friends} = User.get_friends(user) - -            Enum.each(friends, fn followed -> -              User.unfollow(user, followed) -            end) - -            User.invalidate_cache(user) -            Repo.delete(user) +            User.delete(user)            nil ->              :error @@ -1090,10 +1076,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          PleromaJobQueue.enqueue(:transmogrifier, __MODULE__, [:user_upgrade, user])        end -      if Pleroma.Config.get([:instance, :external_user_synchronization]) do -        update_following_followers_counters(user) -      end -        {:ok, user}      else        %User{} = user -> {:ok, user} @@ -1126,27 +1108,4 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      data      |> maybe_fix_user_url    end - -  def update_following_followers_counters(user) do -    info = %{} - -    following = fetch_counter(user.following_address) -    info = if following, do: Map.put(info, :following_count, following), else: info - -    followers = fetch_counter(user.follower_address) -    info = if followers, do: Map.put(info, :follower_count, followers), else: info - -    User.set_info_cache(user, info) -  end - -  defp fetch_counter(url) do -    with {:ok, %{body: body, status: code}} when code in 200..299 <- -           Pleroma.HTTP.get( -             url, -             [{:Accept, "application/activity+json"}] -           ), -         {:ok, data} <- Jason.decode(body) do -      data["totalItems"] -    end -  end  end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index c146f59d4..39074888b 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -18,6 +18,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    import Ecto.Query    require Logger +  require Pleroma.Constants    @supported_object_types ["Article", "Note", "Video", "Page", "Question", "Answer"]    @supported_report_states ~w(open closed resolved) @@ -418,7 +419,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do        "type" => "Follow",        "actor" => follower_id,        "to" => [followed_id], -      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "cc" => [Pleroma.Constants.as_public()],        "object" => followed_id,        "state" => "pending"      } @@ -510,7 +511,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do        "actor" => ap_id,        "object" => id,        "to" => [user.follower_address, object.data["actor"]], -      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "cc" => [Pleroma.Constants.as_public()],        "context" => object.data["context"]      } @@ -530,7 +531,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do        "actor" => ap_id,        "object" => activity.data,        "to" => [user.follower_address, activity.data["actor"]], -      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "cc" => [Pleroma.Constants.as_public()],        "context" => context      } @@ -547,7 +548,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do        "actor" => ap_id,        "object" => activity.data,        "to" => [user.follower_address, activity.data["actor"]], -      "cc" => ["https://www.w3.org/ns/activitystreams#Public"], +      "cc" => [Pleroma.Constants.as_public()],        "context" => context      } @@ -556,7 +557,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    def add_announce_to_object(          %Activity{ -          data: %{"actor" => actor, "cc" => ["https://www.w3.org/ns/activitystreams#Public"]} +          data: %{"actor" => actor, "cc" => [Pleroma.Constants.as_public()]}          },          object        ) do @@ -765,7 +766,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do         ) do      cc = Map.get(data, "cc", [])      follower_address = User.get_cached_by_ap_id(data["actor"]).follower_address -    public = "https://www.w3.org/ns/activitystreams#Public" +    public = Pleroma.Constants.as_public()      case visibility do        "public" -> diff --git a/lib/pleroma/web/activity_pub/visibility.ex b/lib/pleroma/web/activity_pub/visibility.ex index 097fceb08..dfb166b65 100644 --- a/lib/pleroma/web/activity_pub/visibility.ex +++ b/lib/pleroma/web/activity_pub/visibility.ex @@ -8,14 +8,14 @@ defmodule Pleroma.Web.ActivityPub.Visibility do    alias Pleroma.Repo    alias Pleroma.User -  @public "https://www.w3.org/ns/activitystreams#Public" +  require Pleroma.Constants    @spec is_public?(Object.t() | Activity.t() | map()) :: boolean()    def is_public?(%Object{data: %{"type" => "Tombstone"}}), do: false    def is_public?(%Object{data: data}), do: is_public?(data)    def is_public?(%Activity{data: data}), do: is_public?(data)    def is_public?(%{"directMessage" => true}), do: false -  def is_public?(data), do: @public in (data["to"] ++ (data["cc"] || [])) +  def is_public?(data), do: Pleroma.Constants.as_public() in (data["to"] ++ (data["cc"] || []))    def is_private?(activity) do      with false <- is_public?(activity), @@ -73,10 +73,10 @@ defmodule Pleroma.Web.ActivityPub.Visibility do      cc = object.data["cc"] || []      cond do -      @public in to -> +      Pleroma.Constants.as_public() in to ->          "public" -      @public in cc -> +      Pleroma.Constants.as_public() in cc ->          "unlisted"        # this should use the sql for the object's activity diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 1ae5acd91..fcda57b3e 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -379,6 +379,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      end    end +  def migrate_to_db(conn, _params) do +    Mix.Tasks.Pleroma.Config.run(["migrate_to_db"]) +    json(conn, %{}) +  end + +  def migrate_from_db(conn, _params) do +    Mix.Tasks.Pleroma.Config.run(["migrate_from_db", Pleroma.Config.get(:env), "true"]) +    json(conn, %{}) +  end +    def config_show(conn, _params) do      configs = Pleroma.Repo.all(Config) diff --git a/lib/pleroma/web/auth/authenticator.ex b/lib/pleroma/web/auth/authenticator.ex index d4e0ffa80..dd49987f7 100644 --- a/lib/pleroma/web/auth/authenticator.ex +++ b/lib/pleroma/web/auth/authenticator.ex @@ -21,8 +21,7 @@ defmodule Pleroma.Web.Auth.Authenticator do    def create_from_registration(plug, registration),      do: implementation().create_from_registration(plug, registration) -  @callback get_registration(Plug.Conn.t()) :: -              {:ok, Registration.t()} | {:error, any()} +  @callback get_registration(Plug.Conn.t()) :: {:ok, Registration.t()} | {:error, any()}    def get_registration(plug), do: implementation().get_registration(plug)    @callback handle_error(Plug.Conn.t(), any()) :: any() diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 44af6a773..2db58324b 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -300,8 +300,7 @@ defmodule Pleroma.Web.CommonAPI do             }           } = activity <- get_by_id_or_ap_id(id_or_ap_id),           true <- Visibility.is_public?(activity), -         %{valid?: true} = info_changeset <- -           User.Info.add_pinnned_activity(user.info, activity), +         %{valid?: true} = info_changeset <- User.Info.add_pinnned_activity(user.info, activity),           changeset <-             Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset),           {:ok, _user} <- User.update_and_set_cache(changeset) do diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 94462c3dd..c8a743e8e 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -19,11 +19,17 @@ defmodule Pleroma.Web.CommonAPI.Utils do    alias Pleroma.Web.MediaProxy    require Logger +  require Pleroma.Constants    # This is a hack for twidere.    def get_by_id_or_ap_id(id) do      activity = -      Activity.get_by_id_with_object(id) || Activity.get_create_by_object_ap_id_with_object(id) +      with true <- Pleroma.FlakeId.is_flake_id?(id), +           %Activity{} = activity <- Activity.get_by_id_with_object(id) do +        activity +      else +        _ -> Activity.get_create_by_object_ap_id_with_object(id) +      end      activity &&        if activity.data["type"] == "Create" do @@ -66,7 +72,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do    @spec get_to_and_cc(User.t(), list(String.t()), Activity.t() | nil, String.t()) ::            {list(String.t()), list(String.t())}    def get_to_and_cc(user, mentioned_users, inReplyTo, "public") do -    to = ["https://www.w3.org/ns/activitystreams#Public" | mentioned_users] +    to = [Pleroma.Constants.as_public() | mentioned_users]      cc = [user.follower_address]      if inReplyTo do @@ -78,7 +84,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do    def get_to_and_cc(user, mentioned_users, inReplyTo, "unlisted") do      to = [user.follower_address | mentioned_users] -    cc = ["https://www.w3.org/ns/activitystreams#Public"] +    cc = [Pleroma.Constants.as_public()]      if inReplyTo do        {Enum.uniq([inReplyTo.data["actor"] | to]), cc} diff --git a/lib/pleroma/web/fallback_redirect_controller.ex b/lib/pleroma/web/fallback_redirect_controller.ex new file mode 100644 index 000000000..5fbf3695f --- /dev/null +++ b/lib/pleroma/web/fallback_redirect_controller.ex @@ -0,0 +1,77 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Fallback.RedirectController do +  use Pleroma.Web, :controller +  require Logger +  alias Pleroma.User +  alias Pleroma.Web.Metadata + +  def api_not_implemented(conn, _params) do +    conn +    |> put_status(404) +    |> json(%{error: "Not implemented"}) +  end + +  def redirector(conn, _params, code \\ 200) + +  # redirect to admin section +  # /pleroma/admin -> /pleroma/admin/ +  # +  def redirector(conn, %{"path" => ["pleroma", "admin"]} = _, _code) do +    redirect(conn, to: "/pleroma/admin/") +  end + +  def redirector(conn, _params, code) do +    conn +    |> put_resp_content_type("text/html") +    |> send_file(code, index_file_path()) +  end + +  def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do +    with %User{} = user <- User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do +      redirector_with_meta(conn, %{user: user}) +    else +      nil -> +        redirector(conn, params) +    end +  end + +  def redirector_with_meta(conn, params) do +    {:ok, index_content} = File.read(index_file_path()) + +    tags = +      try do +        Metadata.build_tags(params) +      rescue +        e -> +          Logger.error( +            "Metadata rendering for #{conn.request_path} failed.\n" <> +              Exception.format(:error, e, __STACKTRACE__) +          ) + +          "" +      end + +    response = String.replace(index_content, "<!--server-generated-meta-->", tags) + +    conn +    |> put_resp_content_type("text/html") +    |> send_resp(200, response) +  end + +  def index_file_path do +    Pleroma.Plugs.InstanceStatic.file_path("index.html") +  end + +  def registration_page(conn, params) do +    redirector(conn, params) +  end + +  def empty(conn, _params) do +    conn +    |> put_status(204) +    |> text("") +  end +end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index d660f3f05..174e93468 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -4,6 +4,9 @@  defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    use Pleroma.Web, :controller + +  import Pleroma.Web.ControllerHelper, only: [json_response: 3] +    alias Ecto.Changeset    alias Pleroma.Activity    alias Pleroma.Bookmark @@ -46,6 +49,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    import Ecto.Query    require Logger +  require Pleroma.Constants    @rate_limited_relations_actions ~w(follow unfollow)a @@ -74,6 +78,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    plug(RateLimiter, :app_account_creation when action == :account_register)    plug(RateLimiter, :search when action in [:search, :search2, :account_search])    plug(RateLimiter, :password_reset when action == :password_reset) +  plug(RateLimiter, :account_confirmation_resend when action == :account_confirmation_resend)    @local_mastodon_name "Mastodon-Local" @@ -1220,10 +1225,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do        recipients =          if for_user do -          ["https://www.w3.org/ns/activitystreams#Public"] ++ -            [for_user.ap_id | for_user.following] +          [Pleroma.Constants.as_public()] ++ [for_user.ap_id | for_user.following]          else -          ["https://www.w3.org/ns/activitystreams#Public"] +          [Pleroma.Constants.as_public()]          end        activities = @@ -1839,6 +1843,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      end    end +  def account_confirmation_resend(conn, params) do +    nickname_or_email = params["email"] || params["nickname"] + +    with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), +         {:ok, _} <- User.try_send_confirmation_email(user) do +      conn +      |> json_response(:no_content, "") +    end +  end +    def try_render(conn, target, params)        when is_binary(target) do      case render(conn, target, params) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index befb35c26..b2b06eeb9 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -50,13 +50,13 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do        id: to_string(target.id),        following: User.following?(user, target),        followed_by: User.following?(target, user), -      blocking: User.blocks?(user, target), -      blocked_by: User.blocks?(target, user), +      blocking: User.blocks_ap_id?(user, target), +      blocked_by: User.blocks_ap_id?(target, user),        muting: User.mutes?(user, target),        muting_notifications: User.muted_notifications?(user, target),        subscribing: User.subscribed_to?(user, target),        requested: requested, -      domain_blocking: false, +      domain_blocking: User.blocks_domain?(user, target),        showing_reblogs: User.showing_reblogs?(user, target),        endorsed: false      } diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index de9425959..80df9b2ac 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -222,7 +222,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        if user.local do          Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, activity)        else -        object.data["external_url"] || object.data["id"] +        object.data["url"] || object.data["external_url"] || object.data["id"]        end      %{ diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index a1d7fcc7d..54f89e65c 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -165,6 +165,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do          },          accountActivationRequired: Config.get([:instance, :account_activation_required], false),          invitesEnabled: Config.get([:instance, :invites_enabled], false), +        mailerEnabled: Config.get([Pleroma.Emails.Mailer, :enabled], false),          features: features,          restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),          skipThreadContainment: Config.get([:instance, :skip_thread_containment], false) diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index ef53b7ae3..81eae2c8b 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -365,8 +365,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do    def register(%Plug.Conn{} = conn, %{"authorization" => _, "op" => "connect"} = params) do      with registration_id when not is_nil(registration_id) <- get_session_registration_id(conn),           %Registration{} = registration <- Repo.get(Registration, registration_id), -         {_, {:ok, auth}} <- -           {:create_authorization, do_create_authorization(conn, params)}, +         {_, {:ok, auth}} <- {:create_authorization, do_create_authorization(conn, params)},           %User{} = user <- Repo.preload(auth, :user).user,           {:ok, _updated_registration} <- Registration.bind_to_user(registration, user) do        conn diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index 90c304487..40f131b57 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -44,8 +44,7 @@ defmodule Pleroma.Web.OAuth.Token do      |> Repo.find_resource()    end -  @spec exchange_token(App.t(), Authorization.t()) :: -          {:ok, Token.t()} | {:error, Changeset.t()} +  @spec exchange_token(App.t(), Authorization.t()) :: {:ok, Token.t()} | {:error, Changeset.t()}    def exchange_token(app, auth) do      with {:ok, auth} <- Authorization.use_token(auth),           true <- auth.app_id == app.id do diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index 95037125d..760345301 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -9,6 +9,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do    alias Pleroma.Web.OStatus.UserRepresenter    require Logger +  require Pleroma.Constants    defp get_href(id) do      with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do @@ -34,7 +35,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do      Enum.map(to, fn id ->        cond do          # Special handling for the AP/Ostatus public collections -        "https://www.w3.org/ns/activitystreams#Public" == id -> +        Pleroma.Constants.as_public() == id ->            {:link,             [               rel: "mentioned", diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex index 263d3b2dc..24513972e 100644 --- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex @@ -9,14 +9,18 @@ defmodule Pleroma.Web.OStatus.FollowHandler do    alias Pleroma.Web.XML    def handle(entry, doc) do -    with {:ok, actor} <- OStatus.find_make_or_update_user(doc), +    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),           id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),           followed_uri when not is_nil(followed_uri) <-             XML.string_from_xpath("/entry/activity:object/id", entry),           {:ok, followed} <- OStatus.find_or_make_user(followed_uri), +         {:locked, false} <- {:locked, followed.info.locked},           {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do        User.follow(actor, followed)        {:ok, activity} +    else +      {:locked, true} -> +        {:error, "It's not possible to follow locked accounts over OStatus"}      end    end  end diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex index 8e0adad91..7fae14f7b 100644 --- a/lib/pleroma/web/ostatus/handlers/note_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex @@ -4,6 +4,7 @@  defmodule Pleroma.Web.OStatus.NoteHandler do    require Logger +  require Pleroma.Constants    alias Pleroma.Activity    alias Pleroma.Object @@ -49,7 +50,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do    def get_collection_mentions(entry) do      transmogrify = fn        "http://activityschema.org/collection/public" -> -        "https://www.w3.org/ns/activitystreams#Public" +        Pleroma.Constants.as_public()        group ->          group @@ -110,7 +111,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do      with id <- XML.string_from_xpath("//id", entry),           activity when is_nil(activity) <- Activity.get_create_by_object_ap_id_with_object(id),           [author] <- :xmerl_xpath.string('//author[1]', doc), -         {:ok, actor} <- OStatus.find_make_or_update_user(author), +         {:ok, actor} <- OStatus.find_make_or_update_actor(author),           content_html <- OStatus.get_content(entry),           cw <- OStatus.get_cw(entry),           in_reply_to <- XML.string_from_xpath("//thr:in-reply-to[1]/@ref", entry), @@ -126,7 +127,7 @@ defmodule Pleroma.Web.OStatus.NoteHandler do           to <- make_to_list(actor, mentions),           date <- XML.string_from_xpath("//published", entry),           unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted", -         cc <- if(unlisted, do: ["https://www.w3.org/ns/activitystreams#Public"], else: []), +         cc <- if(unlisted, do: [Pleroma.Constants.as_public()], else: []),           note <-             CommonAPI.Utils.make_note_data(               actor.ap_id, diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex index 6596ada3b..2062432e3 100644 --- a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex @@ -9,7 +9,7 @@ defmodule Pleroma.Web.OStatus.UnfollowHandler do    alias Pleroma.Web.XML    def handle(entry, doc) do -    with {:ok, actor} <- OStatus.find_make_or_update_user(doc), +    with {:ok, actor} <- OStatus.find_make_or_update_actor(doc),           id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry),           followed_uri when not is_nil(followed_uri) <-             XML.string_from_xpath("/entry/activity:object/id", entry), diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 502410c83..331cbc0b7 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -56,7 +56,7 @@ defmodule Pleroma.Web.OStatus do    def handle_incoming(xml_string, options \\ []) do      with doc when doc != :error <- parse_document(xml_string) do -      with {:ok, actor_user} <- find_make_or_update_user(doc), +      with {:ok, actor_user} <- find_make_or_update_actor(doc),             do: Pleroma.Instances.set_reachable(actor_user.ap_id)        entries = :xmerl_xpath.string('//entry', doc) @@ -120,7 +120,7 @@ defmodule Pleroma.Web.OStatus do    end    def make_share(entry, doc, retweeted_activity) do -    with {:ok, actor} <- find_make_or_update_user(doc), +    with {:ok, actor} <- find_make_or_update_actor(doc),           %Object{} = object <- Object.normalize(retweeted_activity),           id when not is_nil(id) <- string_from_xpath("/entry/id", entry),           {:ok, activity, _object} = ActivityPub.announce(actor, object, id, false) do @@ -138,7 +138,7 @@ defmodule Pleroma.Web.OStatus do    end    def make_favorite(entry, doc, favorited_activity) do -    with {:ok, actor} <- find_make_or_update_user(doc), +    with {:ok, actor} <- find_make_or_update_actor(doc),           %Object{} = object <- Object.normalize(favorited_activity),           id when not is_nil(id) <- string_from_xpath("/entry/id", entry),           {:ok, activity, _object} = ActivityPub.like(actor, object, id, false) do @@ -264,11 +264,18 @@ defmodule Pleroma.Web.OStatus do      end    end -  def find_make_or_update_user(doc) do +  def find_make_or_update_actor(doc) do      uri = string_from_xpath("//author/uri[1]", doc) -    with {:ok, user} <- find_or_make_user(uri) do +    with {:ok, %User{} = user} <- find_or_make_user(uri), +         {:ap_enabled, false} <- {:ap_enabled, User.ap_enabled?(user)} do        maybe_update(doc, user) +    else +      {:ap_enabled, true} -> +        {:error, :invalid_protocol} + +      _ -> +        {:error, :unknown_user}      end    end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 372d52899..c70063b84 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -5,6 +5,7 @@  defmodule Pleroma.Web.OStatus.OStatusController do    use Pleroma.Web, :controller +  alias Fallback.RedirectController    alias Pleroma.Activity    alias Pleroma.Object    alias Pleroma.User @@ -12,42 +13,44 @@ defmodule Pleroma.Web.OStatus.OStatusController do    alias Pleroma.Web.ActivityPub.ActivityPubController    alias Pleroma.Web.ActivityPub.ObjectView    alias Pleroma.Web.ActivityPub.Visibility +  alias Pleroma.Web.Endpoint    alias Pleroma.Web.Federator +  alias Pleroma.Web.Metadata.PlayerView    alias Pleroma.Web.OStatus    alias Pleroma.Web.OStatus.ActivityRepresenter    alias Pleroma.Web.OStatus.FeedRepresenter +  alias Pleroma.Web.Router    alias Pleroma.Web.XML    plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming]) -  action_fallback(:errors) +  plug( +    Pleroma.Plugs.SetFormatPlug +    when action in [:feed_redirect, :object, :activity, :notice] +  ) -  def feed_redirect(conn, %{"nickname" => nickname}) do -    case get_format(conn) do -      "html" -> -        with %User{} = user <- User.get_cached_by_nickname_or_id(nickname) do -          Fallback.RedirectController.redirector_with_meta(conn, %{user: user}) -        else -          nil -> {:error, :not_found} -        end +  action_fallback(:errors) -      "activity+json" -> -        ActivityPubController.call(conn, :user) +  def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do +    with {_, %User{} = user} <- +           {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do +      RedirectController.redirector_with_meta(conn, %{user: user}) +    end +  end -      "json" -> -        ActivityPubController.call(conn, :user) +  def feed_redirect(%{assigns: %{format: format}} = conn, _params) +      when format in ["json", "activity+json"] do +    ActivityPubController.call(conn, :user) +  end -      _ -> -        with %User{} = user <- User.get_cached_by_nickname(nickname) do -          redirect(conn, external: OStatus.feed_path(user)) -        else -          nil -> {:error, :not_found} -        end +  def feed_redirect(conn, %{"nickname" => nickname}) do +    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do +      redirect(conn, external: OStatus.feed_path(user))      end    end    def feed(conn, %{"nickname" => nickname} = params) do -    with %User{} = user <- User.get_cached_by_nickname(nickname) do +    with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do        query_params =          Map.take(params, ["max_id"])          |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) @@ -65,8 +68,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do        conn        |> put_resp_content_type("application/atom+xml")        |> send_resp(200, response) -    else -      nil -> {:error, :not_found}      end    end @@ -97,93 +98,82 @@ defmodule Pleroma.Web.OStatus.OStatusController do      |> send_resp(200, "")    end -  def object(conn, %{"uuid" => uuid}) do -    if get_format(conn) in ["activity+json", "json"] do -      ActivityPubController.call(conn, :object) -    else -      with id <- o_status_url(conn, :object, uuid), -           {_, %Activity{} = activity} <- -             {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, -           {_, true} <- {:public?, Visibility.is_public?(activity)}, -           %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do -        case get_format(conn) do -          "html" -> redirect(conn, to: "/notice/#{activity.id}") -          _ -> represent_activity(conn, nil, activity, user) -        end -      else -        {:public?, false} -> -          {:error, :not_found} - -        {:activity, nil} -> -          {:error, :not_found} +  def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid}) +      when format in ["json", "activity+json"] do +    ActivityPubController.call(conn, :object) +  end -        e -> -          e +  def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do +    with id <- o_status_url(conn, :object, uuid), +         {_, %Activity{} = activity} <- +           {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, +         {_, true} <- {:public?, Visibility.is_public?(activity)}, +         %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do +      case format do +        "html" -> redirect(conn, to: "/notice/#{activity.id}") +        _ -> represent_activity(conn, nil, activity, user)        end +    else +      reason when reason in [{:public?, false}, {:activity, nil}] -> +        {:error, :not_found} + +      e -> +        e      end    end -  def activity(conn, %{"uuid" => uuid}) do -    if get_format(conn) in ["activity+json", "json"] do -      ActivityPubController.call(conn, :activity) -    else -      with id <- o_status_url(conn, :activity, uuid), -           {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, -           {_, true} <- {:public?, Visibility.is_public?(activity)}, -           %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do -        case format = get_format(conn) do -          "html" -> redirect(conn, to: "/notice/#{activity.id}") -          _ -> represent_activity(conn, format, activity, user) -        end -      else -        {:public?, false} -> -          {:error, :not_found} - -        {:activity, nil} -> -          {:error, :not_found} +  def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid}) +      when format in ["json", "activity+json"] do +    ActivityPubController.call(conn, :activity) +  end -        e -> -          e +  def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do +    with id <- o_status_url(conn, :activity, uuid), +         {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, +         {_, true} <- {:public?, Visibility.is_public?(activity)}, +         %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do +      case format do +        "html" -> redirect(conn, to: "/notice/#{activity.id}") +        _ -> represent_activity(conn, format, activity, user)        end +    else +      reason when reason in [{:public?, false}, {:activity, nil}] -> +        {:error, :not_found} + +      e -> +        e      end    end -  def notice(conn, %{"id" => id}) do +  def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do      with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)},           {_, true} <- {:public?, Visibility.is_public?(activity)},           %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do -      case format = get_format(conn) do -        "html" -> -          if activity.data["type"] == "Create" do -            %Object{} = object = Object.normalize(activity) +      cond do +        format == "html" && activity.data["type"] == "Create" -> +          %Object{} = object = Object.normalize(activity) -            Fallback.RedirectController.redirector_with_meta(conn, %{ +          RedirectController.redirector_with_meta( +            conn, +            %{                activity_id: activity.id,                object: object, -              url: -                Pleroma.Web.Router.Helpers.o_status_url( -                  Pleroma.Web.Endpoint, -                  :notice, -                  activity.id -                ), +              url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id),                user: user -            }) -          else -            Fallback.RedirectController.redirector(conn, nil) -          end +            } +          ) -        _ -> +        format == "html" -> +          RedirectController.redirector(conn, nil) + +        true ->            represent_activity(conn, format, activity, user)        end      else -      {:public?, false} -> +      reason when reason in [{:public?, false}, {:activity, nil}] ->          conn          |> put_status(404) -        |> Fallback.RedirectController.redirector(nil, 404) - -      {:activity, nil} -> -        conn -        |> Fallback.RedirectController.redirector(nil, 404) +        |> RedirectController.redirector(nil, 404)        e ->          e @@ -204,13 +194,13 @@ defmodule Pleroma.Web.OStatus.OStatusController do          "content-security-policy",          "default-src 'none';style-src 'self' 'unsafe-inline';img-src 'self' data: https:; media-src 'self' https:;"        ) -      |> put_view(Pleroma.Web.Metadata.PlayerView) +      |> put_view(PlayerView)        |> render("player.html", url)      else        _error ->          conn          |> put_status(404) -        |> Fallback.RedirectController.redirector(nil, 404) +        |> RedirectController.redirector(nil, 404)      end    end @@ -248,6 +238,8 @@ defmodule Pleroma.Web.OStatus.OStatusController do      render_error(conn, :not_found, "Not found")    end +  def errors(conn, {:fetch_user, nil}), do: errors(conn, {:error, :not_found}) +    def errors(conn, _) do      render_error(conn, :internal_server_error, "Something went wrong")    end diff --git a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex b/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex index 014c0935f..0dc1efdaf 100644 --- a/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex +++ b/lib/pleroma/web/rich_media/parsers/ttl/aws_signed_url.ex @@ -19,8 +19,7 @@ defmodule Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl do    defp is_aws_signed_url(image) when is_binary(image) do      %URI{host: host, query: query} = URI.parse(image) -    if String.contains?(host, "amazonaws.com") and -         String.contains?(query, "X-Amz-Expires") do +    if String.contains?(host, "amazonaws.com") and String.contains?(query, "X-Amz-Expires") do        image      else        nil diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 0717e9aa0..c8c1c22dd 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -196,6 +196,8 @@ defmodule Pleroma.Web.Router do      get("/config", AdminAPIController, :config_show)      post("/config", AdminAPIController, :config_update) +    get("/config/migrate_to_db", AdminAPIController, :migrate_to_db) +    get("/config/migrate_from_db", AdminAPIController, :migrate_from_db)    end    scope "/", Pleroma.Web.TwitterAPI do @@ -412,6 +414,12 @@ defmodule Pleroma.Web.Router do      get("/accounts/search", SearchController, :account_search) +    post( +      "/pleroma/accounts/confirmation_resend", +      MastodonAPIController, +      :account_confirmation_resend +    ) +      scope [] do        pipe_through(:oauth_read_or_public) @@ -694,7 +702,7 @@ defmodule Pleroma.Web.Router do      post("/auth/password", MastodonAPIController, :password_reset)      scope [] do -      pipe_through(:oauth_read_or_public) +      pipe_through(:oauth_read)        get("/web/*path", MastodonAPIController, :index)      end    end @@ -731,68 +739,3 @@ defmodule Pleroma.Web.Router do      options("/*path", RedirectController, :empty)    end  end - -defmodule Fallback.RedirectController do -  use Pleroma.Web, :controller -  require Logger -  alias Pleroma.User -  alias Pleroma.Web.Metadata - -  def api_not_implemented(conn, _params) do -    conn -    |> put_status(404) -    |> json(%{error: "Not implemented"}) -  end - -  def redirector(conn, _params, code \\ 200) do -    conn -    |> put_resp_content_type("text/html") -    |> send_file(code, index_file_path()) -  end - -  def redirector_with_meta(conn, %{"maybe_nickname_or_id" => maybe_nickname_or_id} = params) do -    with %User{} = user <- User.get_cached_by_nickname_or_id(maybe_nickname_or_id) do -      redirector_with_meta(conn, %{user: user}) -    else -      nil -> -        redirector(conn, params) -    end -  end - -  def redirector_with_meta(conn, params) do -    {:ok, index_content} = File.read(index_file_path()) - -    tags = -      try do -        Metadata.build_tags(params) -      rescue -        e -> -          Logger.error( -            "Metadata rendering for #{conn.request_path} failed.\n" <> -              Exception.format(:error, e, __STACKTRACE__) -          ) - -          "" -      end - -    response = String.replace(index_content, "<!--server-generated-meta-->", tags) - -    conn -    |> put_resp_content_type("text/html") -    |> send_resp(200, response) -  end - -  def index_file_path do -    Pleroma.Plugs.InstanceStatic.file_path("index.html") -  end - -  def registration_page(conn, params) do -    redirector(conn, params) -  end - -  def empty(conn, _params) do -    conn -    |> put_status(204) -    |> text("") -  end -end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 9e4da7dca..3405bd3b7 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -15,11 +15,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do    alias Pleroma.Plugs.AuthenticationPlug    alias Pleroma.User    alias Pleroma.Web -  alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.CommonAPI -  alias Pleroma.Web.OStatus    alias Pleroma.Web.WebFinger +  plug(Pleroma.Plugs.SetFormatPlug when action in [:config, :version]) +    def help_test(conn, _params) do      json(conn, "ok")    end @@ -60,27 +60,25 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do        %Activity{id: activity_id} = Activity.get_create_by_object_ap_id(object.data["id"])        redirect(conn, to: "/notice/#{activity_id}")      else -      {err, followee} = OStatus.find_or_make_user(acct) -      avatar = User.avatar_url(followee) -      name = followee.nickname -      id = followee.id - -      if !!user do +      with {:ok, followee} <- User.get_or_fetch(acct) do          conn -        |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id}) -      else -        conn -        |> render("follow_login.html", %{ +        |> render(follow_template(user), %{            error: false,            acct: acct, -          avatar: avatar, -          name: name, -          id: id +          avatar: User.avatar_url(followee), +          name: followee.nickname, +          id: followee.id          }) +      else +        {:error, _reason} -> +          render(conn, follow_template(user), %{error: :error})        end      end    end +  defp follow_template(%User{} = _user), do: "follow.html" +  defp follow_template(_), do: "follow_login.html" +    defp is_status?(acct) do      case Pleroma.Object.Fetcher.fetch_and_contain_remote_object_from_id(acct) do        {:ok, %{"type" => type}} when type in ["Article", "Note", "Video", "Page", "Question"] -> @@ -94,50 +92,53 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do    def do_remote_follow(conn, %{          "authorization" => %{"name" => username, "password" => password, "id" => id}        }) do -    followee = User.get_cached_by_id(id) -    avatar = User.avatar_url(followee) -    name = followee.nickname - -    with %User{} = user <- User.get_cached_by_nickname(username), -         true <- AuthenticationPlug.checkpw(password, user.password_hash), -         %User{} = _followed <- User.get_cached_by_id(id), -         {:ok, follower} <- User.follow(user, followee), -         {:ok, _activity} <- ActivityPub.follow(follower, followee) do +    with %User{} = followee <- User.get_cached_by_id(id), +         {_, %User{} = user, _} <- {:auth, User.get_cached_by_nickname(username), followee}, +         {_, true, _} <- { +           :auth, +           AuthenticationPlug.checkpw(password, user.password_hash), +           followee +         }, +         {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do        conn        |> render("followed.html", %{error: false})      else        # Was already following user        {:error, "Could not follow user:" <> _rest} -> -        render(conn, "followed.html", %{error: false}) +        render(conn, "followed.html", %{error: "Error following account"}) -      _e -> +      {:auth, _, followee} ->          conn          |> render("follow_login.html", %{            error: "Wrong username or password",            id: id, -          name: name, -          avatar: avatar +          name: followee.nickname, +          avatar: User.avatar_url(followee)          }) + +      e -> +        Logger.debug("Remote follow failed with error #{inspect(e)}") +        render(conn, "followed.html", %{error: "Something went wrong."})      end    end    def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do -    with %User{} = followee <- User.get_cached_by_id(id), -         {:ok, follower} <- User.follow(user, followee), -         {:ok, _activity} <- ActivityPub.follow(follower, followee) do +    with {:fetch_user, %User{} = followee} <- {:fetch_user, User.get_cached_by_id(id)}, +         {:ok, _follower, _followee, _activity} <- CommonAPI.follow(user, followee) do        conn        |> render("followed.html", %{error: false})      else        # Was already following user        {:error, "Could not follow user:" <> _rest} -> -        conn -        |> render("followed.html", %{error: false}) +        render(conn, "followed.html", %{error: "Error following account"}) + +      {:fetch_user, error} -> +        Logger.debug("Remote follow failed with error #{inspect(error)}") +        render(conn, "followed.html", %{error: "Could not find user"})        e ->          Logger.debug("Remote follow failed with error #{inspect(e)}") - -        conn -        |> render("followed.html", %{error: inspect(e)}) +        render(conn, "followed.html", %{error: "Something went wrong."})      end    end @@ -152,67 +153,70 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do      end    end -  def config(conn, _params) do +  def config(%{assigns: %{format: "xml"}} = conn, _params) do      instance = Pleroma.Config.get(:instance) -    case get_format(conn) do -      "xml" -> -        response = """ -        <config> -          <site> -            <name>#{Keyword.get(instance, :name)}</name> -            <site>#{Web.base_url()}</site> -            <textlimit>#{Keyword.get(instance, :limit)}</textlimit> -            <closed>#{!Keyword.get(instance, :registrations_open)}</closed> -          </site> -        </config> -        """ +    response = """ +    <config> +    <site> +    <name>#{Keyword.get(instance, :name)}</name> +    <site>#{Web.base_url()}</site> +    <textlimit>#{Keyword.get(instance, :limit)}</textlimit> +    <closed>#{!Keyword.get(instance, :registrations_open)}</closed> +    </site> +    </config> +    """ -        conn -        |> put_resp_content_type("application/xml") -        |> send_resp(200, response) +    conn +    |> put_resp_content_type("application/xml") +    |> send_resp(200, response) +  end -      _ -> -        vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) - -        uploadlimit = %{ -          uploadlimit: to_string(Keyword.get(instance, :upload_limit)), -          avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)), -          backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)), -          bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit)) -        } - -        data = %{ -          name: Keyword.get(instance, :name), -          description: Keyword.get(instance, :description), -          server: Web.base_url(), -          textlimit: to_string(Keyword.get(instance, :limit)), -          uploadlimit: uploadlimit, -          closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"), -          private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), -          vapidPublicKey: vapid_public_key, -          accountActivationRequired: -            if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), -          invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0"), -          safeDMMentionsEnabled: -            if(Pleroma.Config.get([:instance, :safe_dm_mentions]), do: "1", else: "0") -        } +  def config(conn, _params) do +    instance = Pleroma.Config.get(:instance) +    vapid_public_key = Keyword.get(Pleroma.Web.Push.vapid_config(), :public_key) + +    uploadlimit = %{ +      uploadlimit: to_string(Keyword.get(instance, :upload_limit)), +      avatarlimit: to_string(Keyword.get(instance, :avatar_upload_limit)), +      backgroundlimit: to_string(Keyword.get(instance, :background_upload_limit)), +      bannerlimit: to_string(Keyword.get(instance, :banner_upload_limit)) +    } + +    data = %{ +      name: Keyword.get(instance, :name), +      description: Keyword.get(instance, :description), +      server: Web.base_url(), +      textlimit: to_string(Keyword.get(instance, :limit)), +      uploadlimit: uploadlimit, +      closed: bool_to_val(Keyword.get(instance, :registrations_open), "0", "1"), +      private: bool_to_val(Keyword.get(instance, :public, true), "0", "1"), +      vapidPublicKey: vapid_public_key, +      accountActivationRequired: +        bool_to_val(Keyword.get(instance, :account_activation_required, false)), +      invitesEnabled: bool_to_val(Keyword.get(instance, :invites_enabled, false)), +      safeDMMentionsEnabled: bool_to_val(Pleroma.Config.get([:instance, :safe_dm_mentions])) +    } + +    managed_config = Keyword.get(instance, :managed_config) + +    data = +      if managed_config do          pleroma_fe = Pleroma.Config.get([:frontend_configurations, :pleroma_fe]) +        Map.put(data, "pleromafe", pleroma_fe) +      else +        data +      end -        managed_config = Keyword.get(instance, :managed_config) - -        data = -          if managed_config do -            data |> Map.put("pleromafe", pleroma_fe) -          else -            data -          end - -        json(conn, %{site: data}) -    end +    json(conn, %{site: data})    end +  defp bool_to_val(true), do: "1" +  defp bool_to_val(_), do: "0" +  defp bool_to_val(true, val, _), do: val +  defp bool_to_val(_, _, val), do: val +    def frontend_configurations(conn, _params) do      config =        Pleroma.Config.get(:frontend_configurations, %{}) @@ -221,20 +225,16 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do      json(conn, config)    end -  def version(conn, _params) do +  def version(%{assigns: %{format: "xml"}} = conn, _params) do      version = Pleroma.Application.named_version() -    case get_format(conn) do -      "xml" -> -        response = "<version>#{version}</version>" - -        conn -        |> put_resp_content_type("application/xml") -        |> send_resp(200, response) +    conn +    |> put_resp_content_type("application/xml") +    |> send_resp(200, "<version>#{version}</version>") +  end -      _ -> -        json(conn, version) -    end +  def version(conn, _params) do +    json(conn, Pleroma.Application.named_version())    end    def emoji(conn, _params) do diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index bb5dda204..80082ea84 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -15,6 +15,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    import Ecto.Query +  require Pleroma.Constants +    def create_status(%User{} = user, %{"status" => _} = data) do      CommonAPI.post(user, data)    end @@ -286,7 +288,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do        from(          [a, o] in Activity.with_preloaded_object(Activity),          where: fragment("?->>'type' = 'Create'", a.data), -        where: "https://www.w3.org/ns/activitystreams#Public" in a.recipients, +        where: ^Pleroma.Constants.as_public() in a.recipients,          where:            fragment(              "to_tsvector('english', ?->>'content') @@ plainto_tsquery('english', ?)", diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index e84af84dc..abae63877 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -19,6 +19,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do    import Ecto.Query    require Logger +  require Pleroma.Constants    defp query_context_ids([]), do: [] @@ -91,7 +92,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do        String.ends_with?(ap_id, "/followers") ->          nil -      ap_id == "https://www.w3.org/ns/activitystreams#Public" -> +      ap_id == Pleroma.Constants.as_public() ->          nil        user = User.get_cached_by_ap_id(ap_id) -> diff --git a/lib/pleroma/web/twitter_api/views/notification_view.ex b/lib/pleroma/web/twitter_api/views/notification_view.ex index e7c7a7496..085cd5aa3 100644 --- a/lib/pleroma/web/twitter_api/views/notification_view.ex +++ b/lib/pleroma/web/twitter_api/views/notification_view.ex @@ -10,6 +10,8 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do    alias Pleroma.Web.TwitterAPI.ActivityView    alias Pleroma.Web.TwitterAPI.UserView +  require Pleroma.Constants +    defp get_user(ap_id, opts) do      cond do        user = opts[:users][ap_id] -> @@ -18,7 +20,7 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do        String.ends_with?(ap_id, "/followers") ->          nil -      ap_id == "https://www.w3.org/ns/activitystreams#Public" -> +      ap_id == Pleroma.Constants.as_public() ->          nil        true -> diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index fa34c7ced..ecb39ee50 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -86,11 +86,17 @@ defmodule Pleroma.Web.WebFinger do      |> XmlBuilder.to_doc()    end -  defp get_magic_key(magic_key) do -    "data:application/magic-public-key," <> magic_key = magic_key +  defp get_magic_key("data:application/magic-public-key," <> magic_key) do      {:ok, magic_key} -  rescue -    MatchError -> {:error, "Missing magic key data."} +  end + +  defp get_magic_key(nil) do +    Logger.debug("Undefined magic key.") +    {:ok, nil} +  end + +  defp get_magic_key(_) do +    {:error, "Missing magic key data."}    end    defp webfinger_from_xml(doc) do @@ -187,6 +193,7 @@ defmodule Pleroma.Web.WebFinger do      end    end +  @spec finger(String.t()) :: {:ok, map()} | {:error, any()}    def finger(account) do      account = String.trim_leading(account, "@") @@ -220,8 +227,6 @@ defmodule Pleroma.Web.WebFinger do        else          with {:ok, doc} <- Jason.decode(body) do            webfinger_from_json(doc) -        else -          {:error, e} -> e          end        end      else diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index b77c75ec5..896eb15f9 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -7,6 +7,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do    alias Pleroma.Web.WebFinger +  plug(Pleroma.Plugs.SetFormatPlug)    plug(Pleroma.Web.FederatingPlug)    def host_meta(conn, _params) do @@ -17,30 +18,28 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do      |> send_resp(200, xml)    end -  def webfinger(conn, %{"resource" => resource}) do -    case get_format(conn) do -      n when n in ["xml", "xrd+xml"] -> -        with {:ok, response} <- WebFinger.webfinger(resource, "XML") do -          conn -          |> put_resp_content_type("application/xrd+xml") -          |> send_resp(200, response) -        else -          _e -> send_resp(conn, 404, "Couldn't find user") -        end - -      n when n in ["json", "jrd+json"] -> -        with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do -          json(conn, response) -        else -          _e -> send_resp(conn, 404, "Couldn't find user") -        end - -      _ -> -        send_resp(conn, 404, "Unsupported format") +  def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource}) +      when format in ["xml", "xrd+xml"] do +    with {:ok, response} <- WebFinger.webfinger(resource, "XML") do +      conn +      |> put_resp_content_type("application/xrd+xml") +      |> send_resp(200, response) +    else +      _e -> send_resp(conn, 404, "Couldn't find user")      end    end -  def webfinger(conn, _params) do -    send_resp(conn, 400, "Bad Request") +  def webfinger(%{assigns: %{format: format}} = conn, %{"resource" => resource}) +      when format in ["json", "jrd+json"] do +    with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do +      json(conn, response) +    else +      _e -> +        conn +        |> put_status(404) +        |> json("Couldn't find user") +    end    end + +  def webfinger(conn, _params), do: send_resp(conn, 400, "Bad Request")  end | 
