diff options
Diffstat (limited to 'lib')
70 files changed, 689 insertions, 387 deletions
| diff --git a/lib/mix/tasks/pleroma/common.ex b/lib/mix/tasks/pleroma/common.ex index 06893af05..36432c291 100644 --- a/lib/mix/tasks/pleroma/common.ex +++ b/lib/mix/tasks/pleroma/common.ex @@ -1,5 +1,5 @@  defmodule Mix.Tasks.Pleroma.Common do -  @shortdoc "Common functions to be reused in mix tasks" +  @doc "Common functions to be reused in mix tasks"    def start_pleroma do      Mix.Task.run("app.start")    end diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex index c66322707..3be856115 100644 --- a/lib/mix/tasks/pleroma/instance.ex +++ b/lib/mix/tasks/pleroma/instance.ex @@ -1,6 +1,5 @@  defmodule Mix.Tasks.Pleroma.Instance do    use Mix.Task -  alias Pleroma.{Repo, User}    alias Mix.Tasks.Pleroma.Common    @shortdoc "Manages Pleroma instance" diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex index 2502923af..03586d6c3 100644 --- a/lib/mix/tasks/pleroma/relay.ex +++ b/lib/mix/tasks/pleroma/relay.ex @@ -22,7 +22,7 @@ defmodule Mix.Tasks.Pleroma.Relay do    def run(["follow", target]) do      Common.start_pleroma() -    with {:ok, activity} <- Relay.follow(target) do +    with {:ok, _activity} <- Relay.follow(target) do        # put this task to sleep to allow the genserver to push out the messages        :timer.sleep(500)      else @@ -33,7 +33,7 @@ defmodule Mix.Tasks.Pleroma.Relay do    def run(["unfollow", target]) do      Common.start_pleroma() -    with {:ok, activity} <- Relay.follow(target) do +    with {:ok, _activity} <- Relay.unfollow(target) do        # put this task to sleep to allow the genserver to push out the messages        :timer.sleep(500)      else diff --git a/lib/mix/tasks/pleroma/uploads.ex b/lib/mix/tasks/pleroma/uploads.ex new file mode 100644 index 000000000..63299b2ae --- /dev/null +++ b/lib/mix/tasks/pleroma/uploads.ex @@ -0,0 +1,103 @@ +defmodule Mix.Tasks.Pleroma.Uploads do +  use Mix.Task +  alias Pleroma.{Upload, Uploaders.Local} +  alias Mix.Tasks.Pleroma.Common +  require Logger + +  @log_every 50 + +  @shortdoc "Migrates uploads from local to remote storage" +  @moduledoc """ +   Manages uploads + +   ## Migrate uploads from local to remote storage +       mix pleroma.uploads migrate_local TARGET_UPLOADER [OPTIONS...] +   Options: +   - `--delete` - delete local uploads after migrating them to the target uploader + + +   A list of avalible uploaders can be seen in config.exs +  """ +  def run(["migrate_local", target_uploader | args]) do +    delete? = Enum.member?(args, "--delete") +    Common.start_pleroma() +    local_path = Pleroma.Config.get!([Local, :uploads]) +    uploader = Module.concat(Pleroma.Uploaders, target_uploader) + +    unless Code.ensure_loaded?(uploader) do +      raise("The uploader #{inspect(uploader)} is not an existing/loaded module.") +    end + +    target_enabled? = Pleroma.Config.get([Upload, :uploader]) == uploader + +    unless target_enabled? do +      Pleroma.Config.put([Upload, :uploader], uploader) +    end + +    Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}") + +    if delete? do +      Mix.shell().info( +        "Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)" +      ) + +      :timer.sleep(:timer.seconds(5)) +    end + +    uploads = +      File.ls!(local_path) +      |> Enum.map(fn id -> +        root_path = Path.join(local_path, id) + +        cond do +          File.dir?(root_path) -> +            files = for file <- File.ls!(root_path), do: {id, file, Path.join([root_path, file])} + +            case List.first(files) do +              {id, file, path} -> +                {%Pleroma.Upload{id: id, name: file, path: id <> "/" <> file, tempfile: path}, +                 root_path} + +              _ -> +                nil +            end + +          File.exists?(root_path) -> +            file = Path.basename(id) +            hash = Path.rootname(id) +            {%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path} + +          true -> +            nil +        end +      end) +      |> Enum.filter(& &1) + +    total_count = length(uploads) +    Mix.shell().info("Found #{total_count} uploads") + +    uploads +    |> Task.async_stream( +      fn {upload, root_path} -> +        case Upload.store(upload, uploader: uploader, filters: [], size_limit: nil) do +          {:ok, _} -> +            if delete?, do: File.rm_rf!(root_path) +            Logger.debug("uploaded: #{inspect(upload.path)} #{inspect(upload)}") +            :ok + +          error -> +            Mix.shell().error("failed to upload #{inspect(upload.path)}: #{inspect(error)}") +        end +      end, +      timeout: 150_000 +    ) +    |> Stream.chunk_every(@log_every) +    |> Enum.reduce(0, fn done, count -> +      count = count + length(done) +      Mix.shell().info("Uploaded #{count}/#{total_count} files") +      count +    end) + +    Mix.shell().info("Done!") +  end +end diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex index 590553443..fe6e6935f 100644 --- a/lib/mix/tasks/pleroma/user.ex +++ b/lib/mix/tasks/pleroma/user.ex @@ -20,7 +20,7 @@ defmodule Mix.Tasks.Pleroma.User do    - `--admin`/`--no-admin` - whether the user is an admin    ## Generate an invite link. -     +        mix pleroma.user invite    ## Delete the user's account. @@ -32,7 +32,7 @@ defmodule Mix.Tasks.Pleroma.User do        mix pleroma.user toggle_activated NICKNAME    ## Unsubscribe local users from user's account and deactivate it -      +        mix pleroma.user unsubscribe NICKNAME    ## Create a password reset link. @@ -142,8 +142,11 @@ defmodule Mix.Tasks.Pleroma.User do      Common.start_pleroma()      with %User{} = user <- User.get_by_nickname(nickname) do -      User.deactivate(user, !user.info["deactivated"]) -      Mix.shell().info("Activation status of #{nickname}: #{user.info["deactivated"]}") +      {:ok, user} = User.deactivate(user, !user.info.deactivated) + +      Mix.shell().info( +        "Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated" +      )      else        _ ->          Mix.shell().error("No user #{nickname}") @@ -215,23 +218,46 @@ defmodule Mix.Tasks.Pleroma.User do        )      with %User{local: true} = user <- User.get_by_nickname(nickname) do -      case Keyword.get(options, :moderator) do -        nil -> nil -        value -> set_moderator(user, value) -      end +      user = +        case Keyword.get(options, :moderator) do +          nil -> user +          value -> set_moderator(user, value) +        end + +      user = +        case Keyword.get(options, :locked) do +          nil -> user +          value -> set_locked(user, value) +        end + +      _user = +        case Keyword.get(options, :admin) do +          nil -> user +          value -> set_admin(user, value) +        end +    else +      _ -> +        Mix.shell().error("No local user #{nickname}") +    end +  end -      case Keyword.get(options, :locked) do -        nil -> nil -        value -> set_locked(user, value) -      end +  def run(["invite"]) do +    Common.start_pleroma() -      case Keyword.get(options, :admin) do -        nil -> nil -        value -> set_admin(user, value) -      end +    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do +      Mix.shell().info("Generated user invite token") + +      url = +        Pleroma.Web.Router.Helpers.redirect_url( +          Pleroma.Web.Endpoint, +          :registration_page, +          token.token +        ) + +      IO.puts(url)      else        _ -> -        Mix.shell().error("No local user #{nickname}") +        Mix.shell().error("Could not create invite token.")      end    end @@ -245,6 +271,7 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng)      Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}") +    user    end    defp set_admin(user, value) do @@ -256,7 +283,8 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng) -    Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_moderator}") +    Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_admin}") +    user    end    defp set_locked(user, value) do @@ -269,25 +297,6 @@ defmodule Mix.Tasks.Pleroma.User do      {:ok, user} = User.update_and_set_cache(user_cng)      Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}") -  end - -  def run(["invite"]) do -    Common.start_pleroma() - -    with {:ok, token} <- Pleroma.UserInviteToken.create_token() do -      Mix.shell().info("Generated user invite token") - -      url = -        Pleroma.Web.Router.Helpers.redirect_url( -          Pleroma.Web.Endpoint, -          :registration_page, -          token.token -        ) - -      IO.puts(url) -    else -      _ -> -        Mix.shell().error("Could not create invite token.") -    end +    user    end  end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 0b0ec0197..8705395a4 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -15,7 +15,6 @@ defmodule Pleroma.Application do    # See http://elixir-lang.org/docs/stable/elixir/Application.html    # for more information on OTP Applications -  @env Mix.env()    def start(_type, _args) do      import Cachex.Spec diff --git a/lib/pleroma/emails/mailer.ex b/lib/pleroma/emails/mailer.ex new file mode 100644 index 000000000..14ed32ea8 --- /dev/null +++ b/lib/pleroma/emails/mailer.ex @@ -0,0 +1,3 @@ +defmodule Pleroma.Mailer do +  use Swoosh.Mailer, otp_app: :pleroma +end diff --git a/lib/pleroma/emails/user_email.ex b/lib/pleroma/emails/user_email.ex new file mode 100644 index 000000000..9cdf002f3 --- /dev/null +++ b/lib/pleroma/emails/user_email.ex @@ -0,0 +1,40 @@ +defmodule Pleroma.UserEmail do +  @moduledoc "User emails" + +  import Swoosh.Email + +  alias Pleroma.Web.{Endpoint, Router} + +  defp instance_config, do: Pleroma.Config.get(:instance) + +  defp instance_name, do: instance_config()[:name] + +  defp sender do +    {instance_name(), instance_config()[:email]} +  end + +  defp recipient(email, nil), do: email +  defp recipient(email, name), do: {name, email} + +  def password_reset_email(user, password_reset_token) when is_binary(password_reset_token) do +    password_reset_url = +      Router.Helpers.util_url( +        Endpoint, +        :show_password_reset, +        password_reset_token +      ) + +    html_body = """ +    <h3>Reset your password at #{instance_name()}</h3> +    <p>Someone has requested password change for your account at #{instance_name()}.</p> +    <p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p> +    <p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p> +    """ + +    new() +    |> to(recipient(user.email, user.name)) +    |> from(sender()) +    |> subject("Password reset") +    |> html_body(html_body) +  end +end diff --git a/lib/pleroma/emoji.ex b/lib/pleroma/emoji.ex index 0a5e1d5ce..bedad99d6 100644 --- a/lib/pleroma/emoji.ex +++ b/lib/pleroma/emoji.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Emoji do    """    use GenServer    @ets __MODULE__.Ets -  @ets_options [:set, :protected, :named_table, {:read_concurrency, true}] +  @ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]    @doc false    def start_link() do @@ -165,7 +165,7 @@ defmodule Pleroma.Emoji do    defp load_from_file_stream(stream) do      stream -    |> Stream.map(&String.strip/1) +    |> Stream.map(&String.trim/1)      |> Stream.map(fn line ->        case String.split(line, ~r/,\s*/) do          [name, file] -> {name, file} diff --git a/lib/pleroma/filter.ex b/lib/pleroma/filter.ex index 25ed38f34..c57bd3bf8 100644 --- a/lib/pleroma/filter.ex +++ b/lib/pleroma/filter.ex @@ -1,10 +1,10 @@  defmodule Pleroma.Filter do    use Ecto.Schema    import Ecto.{Changeset, Query} -  alias Pleroma.{User, Repo, Activity} +  alias Pleroma.{User, Repo}    schema "filters" do -    belongs_to(:user, Pleroma.User) +    belongs_to(:user, User)      field(:filter_id, :integer)      field(:hide, :boolean, default: false)      field(:whole_word, :boolean, default: true) @@ -26,7 +26,7 @@ defmodule Pleroma.Filter do      Repo.one(query)    end -  def get_filters(%Pleroma.User{id: user_id} = user) do +  def get_filters(%User{id: user_id} = _user) do      query =        from(          f in Pleroma.Filter, @@ -38,9 +38,9 @@ defmodule Pleroma.Filter do    def create(%Pleroma.Filter{user_id: user_id, filter_id: nil} = filter) do      # If filter_id wasn't given, use the max filter_id for this user plus 1. -    # XXX This could result in a race condition if a user tries to add two  -    # different filters for their account from two different clients at the  -    # same time, but that should be unlikely.  +    # XXX This could result in a race condition if a user tries to add two +    # different filters for their account from two different clients at the +    # same time, but that should be unlikely.      max_id_query =        from( diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 5b03e9aeb..133683794 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -18,7 +18,7 @@ defmodule Pleroma.Formatter do    def parse_mentions(text) do      # Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address      regex = -      ~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]*@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u +      ~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]*@?[a-zA-Z0-9_-](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u      Regex.scan(regex, text)      |> List.flatten() diff --git a/lib/pleroma/gopher/server.ex b/lib/pleroma/gopher/server.ex index 3b0569a99..4d582ef25 100644 --- a/lib/pleroma/gopher/server.ex +++ b/lib/pleroma/gopher/server.ex @@ -22,7 +22,7 @@ defmodule Pleroma.Gopher.Server do        :gopher,        100,        :ranch_tcp, -      [port: port], +      [ip: ip, port: port],        __MODULE__.ProtocolHandler,        []      ) diff --git a/lib/pleroma/html.ex b/lib/pleroma/html.ex index 5daaa5e69..8a0333461 100644 --- a/lib/pleroma/html.ex +++ b/lib/pleroma/html.ex @@ -166,7 +166,7 @@ defmodule Pleroma.HTML.Transform.MediaProxy do      {"src", media_url}    end -  def scrub_attribute(tag, attribute), do: attribute +  def scrub_attribute(_tag, attribute), do: attribute    def scrub({"img", attributes, children}) do      attributes = @@ -177,9 +177,9 @@ defmodule Pleroma.HTML.Transform.MediaProxy do      {"img", attributes, children}    end -  def scrub({:comment, children}), do: "" +  def scrub({:comment, _children}), do: ""    def scrub({tag, attributes, children}), do: {tag, attributes, children} -  def scrub({tag, children}), do: children +  def scrub({_tag, children}), do: children    def scrub(text), do: text  end diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex index db46f9e55..7b11060b2 100644 --- a/lib/pleroma/http/connection.ex +++ b/lib/pleroma/http/connection.ex @@ -25,7 +25,7 @@ defmodule Pleroma.HTTP.Connection do    # fetch Hackney options    # -  defp hackney_options(opts \\ []) do +  defp hackney_options(opts) do      options = Keyword.get(opts, :adapter, [])      @hackney_options ++ options    end diff --git a/lib/pleroma/list.ex b/lib/pleroma/list.ex index 891c73f5a..c5bf3e083 100644 --- a/lib/pleroma/list.ex +++ b/lib/pleroma/list.ex @@ -23,7 +23,7 @@ defmodule Pleroma.List do      |> validate_required([:following])    end -  def for_user(user, opts) do +  def for_user(user, _opts) do      query =        from(          l in Pleroma.List, @@ -46,7 +46,7 @@ defmodule Pleroma.List do      Repo.one(query)    end -  def get_following(%Pleroma.List{following: following} = list) do +  def get_following(%Pleroma.List{following: following} = _list) do      q =        from(          u in User, diff --git a/lib/pleroma/mime.ex b/lib/pleroma/mime.ex index db8b7c742..2cb3d8bd1 100644 --- a/lib/pleroma/mime.ex +++ b/lib/pleroma/mime.ex @@ -3,7 +3,7 @@ defmodule Pleroma.MIME do    Returns the mime-type of a binary and optionally a normalized file-name.    """    @default "application/octet-stream" -  @read_bytes 31 +  @read_bytes 35    @spec file_mime_type(String.t()) ::            {:ok, content_type :: String.t(), filename :: String.t()} | {:error, any()} | :error @@ -33,10 +33,10 @@ defmodule Pleroma.MIME do      {:ok, check_mime_type(head)}    end -  def mime_type(<<_::binary>>), do: {:ok, @default} -    def bin_mime_type(_), do: :error +  def mime_type(<<_::binary>>), do: {:ok, @default} +    defp fix_extension(filename, content_type) do      parts = String.split(filename, ".") diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index a40b8f8c9..47f6b6ee7 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -118,7 +118,7 @@ defmodule Pleroma.Notification do    def get_notified_from_activity(activity, local_only \\ true)    def get_notified_from_activity( -        %Activity{data: %{"to" => _, "type" => type} = data} = activity, +        %Activity{data: %{"to" => _, "type" => type} = _data} = activity,          local_only        )        when type in ["Create", "Like", "Announce", "Follow"] do @@ -131,18 +131,18 @@ defmodule Pleroma.Notification do      User.get_users_from_set(recipients, local_only)    end -  def get_notified_from_activity(_, local_only), do: [] +  def get_notified_from_activity(_, _local_only), do: []    defp maybe_notify_to_recipients(           recipients, -         %Activity{data: %{"to" => to, "type" => type}} = activity +         %Activity{data: %{"to" => to, "type" => _type}} = _activity         ) do      recipients ++ to    end    defp maybe_notify_mentioned_recipients(           recipients, -         %Activity{data: %{"to" => to, "type" => type} = data} = activity +         %Activity{data: %{"to" => _to, "type" => type} = data} = _activity         )         when type == "Create" do      object = Object.normalize(data["object"]) diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 3ac301b97..b240ff29f 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -26,14 +26,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do      end    end -  def call( -        %{ -          assigns: %{ -            auth_credentials: %{password: password} -          } -        } = conn, -        _ -      ) do +  def call(%{assigns: %{auth_credentials: %{password: _}}} = conn, _) do      Pbkdf2.dummy_checkpw()      conn    end diff --git a/lib/pleroma/plugs/basic_auth_decoder_plug.ex b/lib/pleroma/plugs/basic_auth_decoder_plug.ex index fc8fcee98..f7ebf7db2 100644 --- a/lib/pleroma/plugs/basic_auth_decoder_plug.ex +++ b/lib/pleroma/plugs/basic_auth_decoder_plug.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Plugs.BasicAuthDecoderPlug do      options    end -  def call(conn, opts) do +  def call(conn, _opts) do      with ["Basic " <> header] <- get_req_header(conn, "authorization"),           {:ok, userinfo} <- Base.decode64(header),           [username, password] <- String.split(userinfo, ":", parts: 2) do diff --git a/lib/pleroma/plugs/federating_plug.ex b/lib/pleroma/plugs/federating_plug.ex index 4108d90af..f0442ca15 100644 --- a/lib/pleroma/plugs/federating_plug.ex +++ b/lib/pleroma/plugs/federating_plug.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.FederatingPlug do      options    end -  def call(conn, opts) do +  def call(conn, _opts) do      if Keyword.get(Application.get_env(:pleroma, :instance), :federating) do        conn      else diff --git a/lib/pleroma/plugs/http_security_plug.ex b/lib/pleroma/plugs/http_security_plug.ex index 4c32653ea..f34f2364b 100644 --- a/lib/pleroma/plugs/http_security_plug.ex +++ b/lib/pleroma/plugs/http_security_plug.ex @@ -4,11 +4,11 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do    def init(opts), do: opts -  def call(conn, options) do +  def call(conn, _options) do      if Config.get([:http_security, :enabled]) do -      conn = -        merge_resp_headers(conn, headers()) -        |> maybe_send_sts_header(Config.get([:http_security, :sts])) +      conn +      |> merge_resp_headers(headers()) +      |> maybe_send_sts_header(Config.get([:http_security, :sts]))      else        conn      end @@ -42,7 +42,7 @@ defmodule Pleroma.Plugs.HTTPSecurityPlug do        "script-src 'self'",        "connect-src 'self' " <> String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"),        "manifest-src 'self'", -      if @protocol == "https" do +      if protocol == "https" do          "upgrade-insecure-requests"        end      ] diff --git a/lib/pleroma/plugs/session_authentication_plug.ex b/lib/pleroma/plugs/session_authentication_plug.ex index 904a27952..aed619432 100644 --- a/lib/pleroma/plugs/session_authentication_plug.ex +++ b/lib/pleroma/plugs/session_authentication_plug.ex @@ -1,6 +1,5 @@  defmodule Pleroma.Plugs.SessionAuthenticationPlug do    import Plug.Conn -  alias Pleroma.User    def init(options) do      options diff --git a/lib/pleroma/plugs/uploaded_media.ex b/lib/pleroma/plugs/uploaded_media.ex index 994cc8bf6..7e1e84126 100644 --- a/lib/pleroma/plugs/uploaded_media.ex +++ b/lib/pleroma/plugs/uploaded_media.ex @@ -8,10 +8,6 @@ defmodule Pleroma.Plugs.UploadedMedia do    @behaviour Plug    # no slashes    @path "media" -  @cache_control %{ -    default: "public, max-age=1209600", -    error: "public, must-revalidate, max-age=160" -  }    def init(_opts) do      static_plug_opts = diff --git a/lib/pleroma/plugs/user_fetcher_plug.ex b/lib/pleroma/plugs/user_fetcher_plug.ex index 9cbaaf40a..e24785ad1 100644 --- a/lib/pleroma/plugs/user_fetcher_plug.ex +++ b/lib/pleroma/plugs/user_fetcher_plug.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Plugs.UserFetcherPlug do      options    end -  def call(conn, options) do +  def call(conn, _options) do      with %{auth_credentials: %{username: username}} <- conn.assigns,           {:ok, %User{} = user} <- user_fetcher(username) do        conn diff --git a/lib/pleroma/reverse_proxy.ex b/lib/pleroma/reverse_proxy.ex index 4ca84152a..7f328d00d 100644 --- a/lib/pleroma/reverse_proxy.ex +++ b/lib/pleroma/reverse_proxy.ex @@ -85,7 +85,9 @@ defmodule Pleroma.ReverseProxy do            | {:redirect_on_failure, boolean()}    @spec call(Plug.Conn.t(), url :: String.t(), [option()]) :: Plug.Conn.t() -  def call(conn = %{method: method}, url, opts \\ []) when method in @methods do +  def call(_conn, _url, _opts \\ []) + +  def call(conn = %{method: method}, url, opts) when method in @methods do      hackney_opts =        @default_hackney_options        |> Keyword.merge(Keyword.get(opts, :http, [])) @@ -240,24 +242,23 @@ defmodule Pleroma.ReverseProxy do    end    defp build_req_headers(headers, opts) do -    headers = -      headers -      |> downcase_headers() -      |> Enum.filter(fn {k, _} -> k in @keep_req_headers end) -      |> (fn headers -> -            headers = headers ++ Keyword.get(opts, :req_headers, []) - -            if Keyword.get(opts, :keep_user_agent, false) do -              List.keystore( -                headers, -                "user-agent", -                0, -                {"user-agent", Pleroma.Application.user_agent()} -              ) -            else -              headers -            end -          end).() +    headers +    |> downcase_headers() +    |> Enum.filter(fn {k, _} -> k in @keep_req_headers end) +    |> (fn headers -> +          headers = headers ++ Keyword.get(opts, :req_headers, []) + +          if Keyword.get(opts, :keep_user_agent, false) do +            List.keystore( +              headers, +              "user-agent", +              0, +              {"user-agent", Pleroma.Application.user_agent()} +            ) +          else +            headers +          end +        end).()    end    defp build_resp_headers(headers, opts) do @@ -268,7 +269,7 @@ defmodule Pleroma.ReverseProxy do      |> (fn headers -> headers ++ Keyword.get(opts, :resp_headers, []) end).()    end -  defp build_resp_cache_headers(headers, opts) do +  defp build_resp_cache_headers(headers, _opts) do      has_cache? = Enum.any?(headers, fn {k, _} -> k in @resp_cache_headers end)      if has_cache? do diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index bf2c60102..07031ac58 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -128,19 +128,18 @@ defmodule Pleroma.Upload do          opts        end -    opts = -      if Pleroma.Config.get([:instance, :dedupe_media]) == true && -           !Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do -        Logger.warn(""" -        Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set: +    if Pleroma.Config.get([:instance, :dedupe_media]) == true && +         !Enum.member?(opts.filters, Pleroma.Upload.Filter.Dedupe) do +      Logger.warn(""" +      Pleroma: configuration `:instance, :dedupe_media` is deprecated, please instead set: -          :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]] -        """) +      :pleroma, Pleroma.Upload, [filters: [Pleroma.Upload.Filter.Dedupe]] +      """) -        Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe]) -      else -        opts -      end +      Map.put(opts, :filters, opts.filters ++ [Pleroma.Upload.Filter.Dedupe]) +    else +      opts +    end    end    defp prepare_upload(%Plug.Upload{} = file, opts) do @@ -216,7 +215,5 @@ defmodule Pleroma.Upload do      |> Path.join()    end -  defp url_from_spec({:url, url}) do -    url -  end +  defp url_from_spec(_base_url, {:url, url}), do: url  end diff --git a/lib/pleroma/upload/filter/anonymize_filename.ex b/lib/pleroma/upload/filter/anonymize_filename.ex index a83e764e5..39eed7af3 100644 --- a/lib/pleroma/upload/filter/anonymize_filename.ex +++ b/lib/pleroma/upload/filter/anonymize_filename.ex @@ -1,10 +1,23 @@  defmodule Pleroma.Upload.Filter.AnonymizeFilename do -  @moduledoc "Replaces the original filename with a randomly generated string." +  @moduledoc """ +  Replaces the original filename with a pre-defined text or randomly generated string. + +  Should be used after `Pleroma.Upload.Filter.Dedupe`. +  """    @behaviour Pleroma.Upload.Filter    def filter(upload) do      extension = List.last(String.split(upload.name, ".")) -    string = Base.url_encode64(:crypto.strong_rand_bytes(10), padding: false) -    {:ok, %Pleroma.Upload{upload | name: string <> "." <> extension}} +    name = Pleroma.Config.get([__MODULE__, :text], random(extension)) +    {:ok, %Pleroma.Upload{upload | name: name}} +  end + +  defp random(extension) do +    string = +      10 +      |> :crypto.strong_rand_bytes() +      |> Base.url_encode64(padding: false) + +    string <> "." <> extension    end  end diff --git a/lib/pleroma/upload/filter/dedupe.ex b/lib/pleroma/upload/filter/dedupe.ex index 28091a627..0657b2c8d 100644 --- a/lib/pleroma/upload/filter/dedupe.ex +++ b/lib/pleroma/upload/filter/dedupe.ex @@ -1,10 +1,11 @@  defmodule Pleroma.Upload.Filter.Dedupe do    @behaviour Pleroma.Upload.Filter +  alias Pleroma.Upload -  def filter(upload = %Pleroma.Upload{name: name, tempfile: path}) do +  def filter(upload = %Upload{name: name}) do      extension = String.split(name, ".") |> List.last()      shasum = :crypto.hash(:sha256, File.read!(upload.tempfile)) |> Base.encode16(case: :lower)      filename = shasum <> "." <> extension -    {:ok, %Pleroma.Upload{upload | id: shasum, path: filename}} +    {:ok, %Upload{upload | id: shasum, path: filename}}    end  end diff --git a/lib/pleroma/upload/filter/mogrify.ex b/lib/pleroma/upload/filter/mogrify.ex index d6ed471ed..f106bd4b1 100644 --- a/lib/pleroma/upload/filter/mogrify.ex +++ b/lib/pleroma/upload/filter/mogrify.ex @@ -1,5 +1,5 @@  defmodule Pleroma.Upload.Filter.Mogrify do -  @behaviour Pleroma.Uploader.Filter +  @behaviour Pleroma.Upload.Filter    @type conversion :: action :: String.t() | {action :: String.t(), opts :: String.t()}    @type conversions :: conversion() | [conversion()] diff --git a/lib/pleroma/uploaders/local.ex b/lib/pleroma/uploaders/local.ex index 434a6b515..2994bcd51 100644 --- a/lib/pleroma/uploaders/local.ex +++ b/lib/pleroma/uploaders/local.ex @@ -1,8 +1,6 @@  defmodule Pleroma.Uploaders.Local do    @behaviour Pleroma.Uploaders.Uploader -  alias Pleroma.Web -    def get_file(_) do      {:ok, {:static_dir, upload_path()}}    end diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex index 820cf88f5..f06755056 100644 --- a/lib/pleroma/uploaders/mdii.ex +++ b/lib/pleroma/uploaders/mdii.ex @@ -12,8 +12,8 @@ defmodule Pleroma.Uploaders.MDII do    end    def put_file(upload) do -    cgi = Pleroma.Config.get([Pleroma.Uploaders.MDII, :cgi]) -    files = Pleroma.Config.get([Pleroma.Uploaders.MDII, :files]) +    cgi = Config.get([Pleroma.Uploaders.MDII, :cgi]) +    files = Config.get([Pleroma.Uploaders.MDII, :files])      {:ok, file_data} = File.read(upload.tempfile) diff --git a/lib/pleroma/uploaders/swift/swift.ex b/lib/pleroma/uploaders/swift/swift.ex index a5b3d2852..d4e758bbb 100644 --- a/lib/pleroma/uploaders/swift/swift.ex +++ b/lib/pleroma/uploaders/swift/swift.ex @@ -9,7 +9,6 @@ defmodule Pleroma.Uploaders.Swift.Client do    end    def upload_file(filename, body, content_type) do -    object_url = Pleroma.Config.get!([Pleroma.Uploaders.Swift, :object_url])      token = Pleroma.Uploaders.Swift.Keystone.get_token()      case put("#{filename}", body, "X-Auth-Token": token, "Content-Type": content_type) do diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 9da674982..49928bc13 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -9,6 +9,13 @@ defmodule Pleroma.User do    alias Pleroma.Web.{OStatus, Websub, OAuth}    alias Pleroma.Web.ActivityPub.{Utils, ActivityPub} +  @type t :: %__MODULE__{} + +  @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ + +  @strict_local_nickname_regex ~r/^[a-zA-Z\d]+$/ +  @extended_local_nickname_regex ~r/^[a-zA-Z\d_-]+$/ +    schema "users" do      field(:bio, :string)      field(:email, :string) @@ -23,6 +30,7 @@ defmodule Pleroma.User do      field(:local, :boolean, default: true)      field(:follower_address, :string)      field(:search_distance, :float, virtual: true) +    field(:tags, {:array, :string}, default: [])      field(:last_refreshed_at, :naive_datetime)      has_many(:notifications, Notification)      embeds_one(:info, Pleroma.User.Info) @@ -74,7 +82,6 @@ defmodule Pleroma.User do      }    end -  @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/    def remote_user_creation(params) do      params =        params @@ -114,7 +121,7 @@ defmodule Pleroma.User do      struct      |> cast(params, [:bio, :name, :avatar])      |> unique_constraint(:nickname) -    |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) +    |> validate_format(:nickname, local_nickname_regex())      |> validate_length(:bio, max: 5000)      |> validate_length(:name, min: 1, max: 100)    end @@ -131,7 +138,7 @@ defmodule Pleroma.User do      struct      |> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at])      |> unique_constraint(:nickname) -    |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) +    |> validate_format(:nickname, local_nickname_regex())      |> validate_length(:bio, max: 5000)      |> validate_length(:name, max: 100)      |> put_embed(:info, info_cng) @@ -169,7 +176,7 @@ defmodule Pleroma.User do        |> validate_confirmation(:password)        |> unique_constraint(:email)        |> unique_constraint(:nickname) -      |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) +      |> validate_format(:nickname, local_nickname_regex())        |> validate_format(:email, @email_regex)        |> validate_length(:bio, max: 1000)        |> validate_length(:name, min: 1, max: 100) @@ -209,14 +216,14 @@ defmodule Pleroma.User do    end    def maybe_direct_follow(%User{} = follower, %User{} = followed) do -    if !User.ap_enabled?(followed) do +    if not User.ap_enabled?(followed) do        follow(follower, followed)      else        {:ok, follower}      end    end -  def maybe_follow(%User{} = follower, %User{info: info} = followed) do +  def maybe_follow(%User{} = follower, %User{info: _info} = followed) do      if not following?(follower, followed) do        follow(follower, followed)      else @@ -278,6 +285,7 @@ defmodule Pleroma.User do      end    end +  @spec following?(User.t(), User.t()) :: boolean    def following?(%User{} = follower, %User{} = followed) do      Enum.member?(follower.following, followed.follower_address)    end @@ -732,7 +740,8 @@ defmodule Pleroma.User do          source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}}        }) do      key = -      :public_key.pem_decode(public_key_pem) +      public_key_pem +      |> :public_key.pem_decode()        |> hd()        |> :public_key.pem_entry_decode() @@ -770,13 +779,10 @@ defmodule Pleroma.User do    def ap_enabled?(%User{info: info}), do: info.ap_enabled    def ap_enabled?(_), do: false -  def get_or_fetch(uri_or_nickname) do -    if String.starts_with?(uri_or_nickname, "http") do -      get_or_fetch_by_ap_id(uri_or_nickname) -    else -      get_or_fetch_by_nickname(uri_or_nickname) -    end -  end +  @doc "Gets or fetch a user by uri or nickname." +  @spec get_or_fetch(String.t()) :: User.t() +  def get_or_fetch("http" <> _host = uri), do: get_or_fetch_by_ap_id(uri) +  def get_or_fetch(nickname), do: get_or_fetch_by_nickname(nickname)    # wait a period of time and return newest version of the User structs    # this is because we have synchronous follow APIs and need to simulate them @@ -802,7 +808,11 @@ defmodule Pleroma.User do      end    end -  def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do +  def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) +  def parse_bio(nil, _user), do: "" +  def parse_bio(bio, _user) when bio == "", do: bio + +  def parse_bio(bio, user) do      mentions = Formatter.parse_mentions(bio)      tags = Formatter.parse_tags(bio) @@ -813,6 +823,54 @@ defmodule Pleroma.User do          {String.trim(name, ":"), url}        end) -    CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji) +    bio +    |> CommonUtils.format_input(mentions, tags, "text/plain") +    |> Formatter.emojify(emoji) +  end + +  def tag(user_identifiers, tags) when is_list(user_identifiers) do +    Repo.transaction(fn -> +      for user_identifier <- user_identifiers, do: tag(user_identifier, tags) +    end) +  end + +  def tag(nickname, tags) when is_binary(nickname), +    do: tag(User.get_by_nickname(nickname), tags) + +  def tag(%User{} = user, tags), +    do: update_tags(user, Enum.uniq(user.tags ++ normalize_tags(tags))) + +  def untag(user_identifiers, tags) when is_list(user_identifiers) do +    Repo.transaction(fn -> +      for user_identifier <- user_identifiers, do: untag(user_identifier, tags) +    end) +  end + +  def untag(nickname, tags) when is_binary(nickname), +    do: untag(User.get_by_nickname(nickname), tags) + +  def untag(%User{} = user, tags), do: update_tags(user, user.tags -- normalize_tags(tags)) + +  defp update_tags(%User{} = user, new_tags) do +    {:ok, updated_user} = +      user +      |> change(%{tags: new_tags}) +      |> Repo.update() + +    updated_user +  end + +  defp normalize_tags(tags) do +    [tags] +    |> List.flatten() +    |> Enum.map(&String.downcase(&1)) +  end + +  defp local_nickname_regex() do +    if Pleroma.Config.get([:instance, :extended_nickname_format]) do +      @extended_local_nickname_regex +    else +      @strict_local_nickname_regex +    end    end  end diff --git a/lib/pleroma/user_invite_token.ex b/lib/pleroma/user_invite_token.ex index 48ee1019a..ce804f78e 100644 --- a/lib/pleroma/user_invite_token.ex +++ b/lib/pleroma/user_invite_token.ex @@ -3,7 +3,8 @@ defmodule Pleroma.UserInviteToken do    import Ecto.Changeset -  alias Pleroma.{User, UserInviteToken, Repo} +  alias Pleroma.UserInviteToken +  alias Pleroma.Repo    schema "user_invite_tokens" do      field(:token, :string) diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index bf81d8039..31455343c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -799,7 +799,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do    end    # guard -  def entire_thread_visible_for_user?(nil, user), do: false +  def entire_thread_visible_for_user?(nil, _user), do: false    # child    def entire_thread_visible_for_user?( diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 3570a75cb..0317f3c8c 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -141,7 +141,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do      json(conn, "error")    end -  def relay(conn, params) do +  def relay(conn, _params) do      with %User{} = user <- Relay.get_actor(),           {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do        conn diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index c8c74ede6..6fa48454a 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do    @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])    def filter_by_summary( -        %{"summary" => parent_summary} = parent, +        %{"summary" => parent_summary} = _parent,          %{"summary" => child_summary} = child        )        when not is_nil(child_summary) and byte_size(child_summary) > 0 and @@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do      end    end -  def filter_by_summary(parent, child), do: child +  def filter_by_summary(_parent, child), do: child    def filter(%{"type" => activity_type} = object) when activity_type == "Create" do      child = object["object"] diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 17b063609..e6af4b211 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -37,9 +37,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    @doc """    Checks that an imported AP object's actor matches the domain it came from.    """ -  def contain_origin(id, %{"actor" => nil}), do: :error +  def contain_origin(_id, %{"actor" => nil}), do: :error -  def contain_origin(id, %{"actor" => actor} = params) do +  def contain_origin(id, %{"actor" => _actor} = params) do      id_uri = URI.parse(id)      actor_uri = URI.parse(get_actor(params)) @@ -50,9 +50,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      end    end -  def contain_origin_from_id(id, %{"id" => nil}), do: :error +  def contain_origin_from_id(_id, %{"id" => nil}), do: :error -  def contain_origin_from_id(id, %{"id" => other_id} = params) do +  def contain_origin_from_id(id, %{"id" => other_id} = _params) do      id_uri = URI.parse(id)      other_uri = URI.parse(other_id) @@ -266,6 +266,32 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    def fix_content_map(object), do: object +  defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do +    with true <- id =~ "follows", +         %User{local: true} = follower <- User.get_cached_by_ap_id(follower_id), +         %Activity{} = activity <- Utils.fetch_latest_follow(follower, followed) do +      {:ok, activity} +    else +      _ -> {:error, nil} +    end +  end + +  defp mastodon_follow_hack(_, _), do: {:error, nil} + +  defp get_follow_activity(follow_object, followed) do +    with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object), +         {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do +      {:ok, activity} +    else +      # Can't find the activity. This might a Mastodon 2.3 "Accept" +      {:activity, nil} -> +        mastodon_follow_hack(follow_object, followed) + +      _ -> +        {:error, nil} +    end +  end +    # disallow objects with bogus IDs    def handle_incoming(%{"id" => nil}), do: :error    def handle_incoming(%{"id" => ""}), do: :error @@ -331,34 +357,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do      end    end -  defp mastodon_follow_hack(%{"id" => id, "actor" => follower_id}, followed) do -    with true <- id =~ "follows", -         %User{local: true} = follower <- User.get_cached_by_ap_id(follower_id), -         %Activity{} = activity <- Utils.fetch_latest_follow(follower, followed) do -      {:ok, activity} -    else -      _ -> {:error, nil} -    end -  end - -  defp mastodon_follow_hack(_), do: {:error, nil} - -  defp get_follow_activity(follow_object, followed) do -    with object_id when not is_nil(object_id) <- Utils.get_ap_id(follow_object), -         {_, %Activity{} = activity} <- {:activity, Activity.get_by_ap_id(object_id)} do -      {:ok, activity} -    else -      # Can't find the activity. This might a Mastodon 2.3 "Accept" -      {:activity, nil} -> -        mastodon_follow_hack(follow_object, followed) - -      _ -> -        {:error, nil} -    end -  end -    def handle_incoming( -        %{"type" => "Accept", "object" => follow_object, "actor" => actor, "id" => id} = data +        %{"type" => "Accept", "object" => follow_object, "actor" => _actor, "id" => _id} = data        ) do      with actor <- get_actor(data),           %User{} = followed <- User.get_or_fetch_by_ap_id(actor), @@ -374,7 +374,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do               local: false             }) do        if not User.following?(follower, followed) do -        {:ok, follower} = User.follow(follower, followed) +        {:ok, _follower} = User.follow(follower, followed)        end        {:ok, activity} @@ -384,7 +384,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    end    def handle_incoming( -        %{"type" => "Reject", "object" => follow_object, "actor" => actor, "id" => id} = data +        %{"type" => "Reject", "object" => follow_object, "actor" => _actor, "id" => _id} = data        ) do      with actor <- get_actor(data),           %User{} = followed <- User.get_or_fetch_by_ap_id(actor), @@ -408,7 +408,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    end    def handle_incoming( -        %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data +        %{"type" => "Like", "object" => object_id, "actor" => _actor, "id" => id} = data        ) do      with actor <- get_actor(data),           %User{} = actor <- User.get_or_fetch_by_ap_id(actor), @@ -421,7 +421,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do    end    def handle_incoming( -        %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data +        %{"type" => "Announce", "object" => object_id, "actor" => _actor, "id" => id} = data        ) do      with actor <- get_actor(data),           %User{} = actor <- User.get_or_fetch_by_ap_id(actor), @@ -492,7 +492,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          %{            "type" => "Undo",            "object" => %{"type" => "Announce", "object" => object_id}, -          "actor" => actor, +          "actor" => _actor,            "id" => id          } = data        ) do @@ -520,7 +520,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        User.unfollow(follower, followed)        {:ok, activity}      else -      e -> :error +      _e -> :error      end    end @@ -539,12 +539,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        User.unblock(blocker, blocked)        {:ok, activity}      else -      e -> :error +      _e -> :error      end    end    def handle_incoming( -        %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = data +        %{"type" => "Block", "object" => blocked, "actor" => blocker, "id" => id} = _data        ) do      with true <- Pleroma.Config.get([:activitypub, :accept_blocks]),           %User{local: true} = blocked = User.get_cached_by_ap_id(blocked), @@ -554,7 +554,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do        User.block(blocker, blocked)        {:ok, activity}      else -      e -> :error +      _e -> :error      end    end @@ -562,7 +562,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do          %{            "type" => "Undo",            "object" => %{"type" => "Like", "object" => object_id}, -          "actor" => actor, +          "actor" => _actor,            "id" => id          } = data        ) do diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 549148989..074622f2b 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -292,7 +292,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do    """    def make_follow_data(          %User{ap_id: follower_id}, -        %User{ap_id: followed_id} = followed, +        %User{ap_id: followed_id} = _followed,          activity_id        ) do      data = %{ diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 2c67d9cda..06c3c7c81 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -3,6 +3,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    alias Pleroma.{User, Repo}    alias Pleroma.Web.ActivityPub.Relay +  import Pleroma.Web.ControllerHelper, only: [json_response: 3] +    require Logger    action_fallback(:errors) @@ -40,6 +42,16 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      |> json(new_user.nickname)    end +  def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do +    with {:ok, _} <- User.tag(nicknames, tags), +         do: json_response(conn, :no_content, "") +  end + +  def untag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do +    with {:ok, _} <- User.untag(nicknames, tags), +         do: json_response(conn, :no_content, "") +  end +    def right_add(conn, %{"permission_group" => permission_group, "nickname" => nickname})        when permission_group in ["moderator", "admin"] do      user = User.get_by_nickname(nickname) @@ -51,13 +63,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      info_cng = User.Info.admin_api_update(user.info, info)      cng = -      Ecto.Changeset.change(user) +      user +      |> Ecto.Changeset.change()        |> Ecto.Changeset.put_embed(:info, info_cng) -    {:ok, user} = User.update_and_set_cache(cng) +    {:ok, _user} = User.update_and_set_cache(cng) + +    json(conn, info) +  end +  def right_add(conn, _) do      conn -    |> json(info) +    |> put_status(404) +    |> json(%{error: "No such permission_group"})    end    def right_get(conn, %{"nickname" => nickname}) do @@ -70,12 +88,6 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      })    end -  def right_add(conn, _) do -    conn -    |> put_status(404) -    |> json(%{error: "No such permission_group"}) -  end -    def right_delete(          %{assigns: %{user: %User{:nickname => admin_nickname}}} = conn,          %{ @@ -101,10 +113,9 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do          Ecto.Changeset.change(user)          |> Ecto.Changeset.put_embed(:info, info_cng) -      {:ok, user} = User.update_and_set_cache(cng) +      {:ok, _user} = User.update_and_set_cache(cng) -      conn -      |> json(info) +      json(conn, info)      end    end @@ -115,32 +126,28 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do    end    def relay_follow(conn, %{"relay_url" => target}) do -    {status, message} = Relay.follow(target) - -    if status == :ok do -      conn -      |> json(target) +    with {:ok, _message} <- Relay.follow(target) do +      json(conn, target)      else -      conn -      |> put_status(500) -      |> json(target) +      _ -> +        conn +        |> put_status(500) +        |> json(target)      end    end    def relay_unfollow(conn, %{"relay_url" => target}) do -    {status, message} = Relay.unfollow(target) - -    if status == :ok do -      conn -      |> json(target) +    with {:ok, _message} <- Relay.unfollow(target) do +      json(conn, target)      else -      conn -      |> put_status(500) -      |> json(target) +      _ -> +        conn +        |> put_status(500) +        |> json(target)      end    end -  @shortdoc "Get a account registeration invite token (base64 string)" +  @doc "Get a account registeration invite token (base64 string)"    def get_invite_token(conn, _params) do      {:ok, token} = Pleroma.UserInviteToken.create_token() @@ -148,7 +155,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do      |> json(token.token)    end -  @shortdoc "Get a password reset token (base64 string) for given nickname" +  @doc "Get a password reset token (base64 string) for given nickname"    def get_password_reset(conn, %{"nickname" => nickname}) do      (%User{local: true} = user) = User.get_by_nickname(nickname)      {:ok, token} = Pleroma.PasswordResetToken.create_token(user) diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index e3385310f..f01d36370 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -1,6 +1,7 @@  defmodule Pleroma.Web.CommonAPI do    alias Pleroma.{User, Repo, Activity, Object}    alias Pleroma.Web.ActivityPub.ActivityPub +  alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Formatter    import Pleroma.Web.CommonAPI.Utils @@ -16,7 +17,8 @@ defmodule Pleroma.Web.CommonAPI do    def repeat(id_or_ap_id, user) do      with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), -         object <- Object.normalize(activity.data["object"]["id"]) do +         object <- Object.normalize(activity.data["object"]["id"]), +         nil <- Utils.get_existing_announce(user.ap_id, object) do        ActivityPub.announce(user, object)      else        _ -> @@ -36,7 +38,8 @@ defmodule Pleroma.Web.CommonAPI do    def favorite(id_or_ap_id, user) do      with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), -         object <- Object.normalize(activity.data["object"]["id"]) do +         object <- Object.normalize(activity.data["object"]["id"]), +         nil <- Utils.get_existing_like(user.ap_id, object) do        ActivityPub.like(user, object)      else        _ -> diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 728f24c7e..ce0926b99 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -1,11 +1,12 @@  defmodule Pleroma.Web.CommonAPI.Utils do -  alias Pleroma.{Repo, Object, Formatter, Activity} +  alias Calendar.Strftime +  alias Comeonin.Pbkdf2 +  alias Pleroma.{Activity, Formatter, Object, Repo} +  alias Pleroma.User +  alias Pleroma.Web    alias Pleroma.Web.ActivityPub.Utils    alias Pleroma.Web.Endpoint    alias Pleroma.Web.MediaProxy -  alias Pleroma.User -  alias Calendar.Strftime -  alias Comeonin.Pbkdf2    # This is a hack for twidere.    def get_by_id_or_ap_id(id) do @@ -122,7 +123,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do      |> Formatter.finalize()    end -  def format_input(text, mentions, tags, "text/html") do +  def format_input(text, mentions, _tags, "text/html") do      text      |> Formatter.html_escape("text/html")      |> String.replace(~r/\r?\n/, "<br>") @@ -148,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do        |> Enum.sort_by(fn {tag, _} -> -String.length(tag) end)      Enum.reduce(tags, text, fn {full, tag}, text -> -      url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>##{tag}</a>" +      url = "<a href='#{Web.base_url()}/tag/#{tag}' rel='tag'>##{tag}</a>"        String.replace(text, full, url)      end)    end @@ -236,7 +237,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do      end    end -  def emoji_from_profile(%{info: info} = user) do +  def emoji_from_profile(%{info: _info} = user) do      (Formatter.get_emoji(user.bio) ++ Formatter.get_emoji(user.name))      |> Enum.map(fn {shortcode, url} ->        %{ diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex new file mode 100644 index 000000000..ddf958811 --- /dev/null +++ b/lib/pleroma/web/controller_helper.ex @@ -0,0 +1,9 @@ +defmodule Pleroma.Web.ControllerHelper do +  use Pleroma.Web, :controller + +  def json_response(conn, status, json) do +    conn +    |> put_status(status) +    |> json(json) +  end +end diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index ac3d7c132..a9c7aecd5 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -13,7 +13,6 @@ defmodule Pleroma.Web.Federator do    @websub Application.get_env(:pleroma, :websub)    @ostatus Application.get_env(:pleroma, :ostatus) -  @httpoison Application.get_env(:pleroma, :httpoison)    @max_jobs 20    def init(args) do @@ -134,7 +133,7 @@ defmodule Pleroma.Web.Federator do    def handle(          :publish_single_websub, -        %{xml: xml, topic: topic, callback: callback, secret: secret} = params +        %{xml: _xml, topic: _topic, callback: _callback, secret: _secret} = params        ) do      case Websub.publish_one(params) do        {:ok, _} -> @@ -151,7 +150,7 @@ defmodule Pleroma.Web.Federator do    end    if Mix.env() == :test do -    def enqueue(type, payload, priority \\ 1) do +    def enqueue(type, payload, _priority \\ 1) do        if Pleroma.Config.get([:instance, :federating]) do          handle(type, payload)        end diff --git a/lib/pleroma/web/federator/retry_queue.ex b/lib/pleroma/web/federator/retry_queue.ex index 13df40c80..510b4315d 100644 --- a/lib/pleroma/web/federator/retry_queue.ex +++ b/lib/pleroma/web/federator/retry_queue.ex @@ -1,13 +1,8 @@  defmodule Pleroma.Web.Federator.RetryQueue do    use GenServer -  alias Pleroma.Web.{WebFinger, Websub} -  alias Pleroma.Web.ActivityPub.ActivityPub +    require Logger -  @websub Application.get_env(:pleroma, :websub) -  @ostatus Application.get_env(:pleroma, :websub) -  @httpoison Application.get_env(:pleroma, :websub) -  @instance Application.get_env(:pleroma, :websub)    # initial timeout, 5 min    @initial_timeout 30_000    @max_retries 5 @@ -46,7 +41,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do          Process.send_after(            __MODULE__,            {:send, data, transport, retries}, -          growth_function(retries) +          timeout          )          {:noreply, state} @@ -62,7 +57,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do        {:ok, _} ->          {:noreply, %{state | delivered: delivery_count + 1}} -      {:error, reason} -> +      {:error, _reason} ->          enqueue(data, transport, retries)          {:noreply, state}      end diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 2d7b1a00c..5c8602322 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -437,10 +437,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    # Instead of returning a 400 when no "id" params is present, Mastodon returns an empty array. -  def relationships(%{assigns: %{user: user}} = conn, _) do -    conn -    |> json([]) -  end +  def relationships(%{assigns: %{user: _user}} = conn, _), do: json(conn, [])    def update_media(%{assigns: %{user: user}} = conn, data) do      with %Object{} = object <- Repo.get(Object, data["id"]), @@ -850,7 +847,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do    end    def list_timeline(%{assigns: %{user: user}} = conn, %{"list_id" => id} = params) do -    with %Pleroma.List{title: title, following: following} <- Pleroma.List.get(id, user) do +    with %Pleroma.List{title: _title, following: following} <- Pleroma.List.get(id, user) do        params =          params          |> Map.put("type", "Create") @@ -983,13 +980,11 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do      with changeset <- User.update_changeset(user),           changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), -         {:ok, user} <- User.update_and_set_cache(changeset) do -      conn -      |> json(%{}) +         {:ok, _user} <- User.update_and_set_cache(changeset) do +      json(conn, %{})      else        e -> -        conn -        |> json(%{error: inspect(e)}) +        json(conn, %{error: inspect(e)})      end    end diff --git a/lib/pleroma/web/mastodon_api/mastodon_socket.ex b/lib/pleroma/web/mastodon_api/mastodon_socket.ex index f3c13d1aa..755ac5730 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_socket.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_socket.ex @@ -5,12 +5,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do    alias Pleroma.{User, Repo}    transport( -    :streaming, +    :websocket,      Phoenix.Transports.WebSocket.Raw,      # We never receive data.      timeout: :infinity    ) +  @spec connect(params :: map(), Phoenix.Socket.t()) :: {:ok, Phoenix.Socket.t()} | :error    def connect(%{"access_token" => token} = params, socket) do      with %Token{user_id: user_id} <- Repo.get_by(Token, token: token),           %User{} = user <- Repo.get(User, user_id), @@ -52,16 +53,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do          _ -> stream        end -    with socket = -           socket -           |> assign(:topic, topic) do -      Pleroma.Web.Streamer.add_socket(topic, socket) -      {:ok, socket} -    else -      _e -> :error -    end +    socket = +      socket +      |> assign(:topic, topic) + +    Pleroma.Web.Streamer.add_socket(topic, socket) +    {:ok, socket}    end +  def connect(_params, _socket), do: :error +    def id(_), do: nil    def handle(:text, message, _state) do diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index bcfa8836e..ebcf9230b 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -58,6 +58,11 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do          note: "",          privacy: user_info.default_scope,          sensitive: false +      }, + +      # Pleroma extension +      pleroma: %{ +        tags: user.tags        }      }    end diff --git a/lib/pleroma/web/mastodon_api/views/mastodon_view.ex b/lib/pleroma/web/mastodon_api/views/mastodon_view.ex index 370fad374..1fd05d9f1 100644 --- a/lib/pleroma/web/mastodon_api/views/mastodon_view.ex +++ b/lib/pleroma/web/mastodon_api/views/mastodon_view.ex @@ -1,5 +1,4 @@  defmodule Pleroma.Web.MastodonAPI.MastodonView do    use Pleroma.Web, :view    import Phoenix.HTML -  import Phoenix.HTML.Form  end diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex index 68bb45494..c8b95d14c 100644 --- a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex @@ -1,6 +1,5 @@  defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do    use Pleroma.Web, :view -  alias Pleroma.Web.MastodonAPI.PushSubscriptionView    def render("push_subscription.json", %{subscription: subscription}) do      %{ diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 2d9a915f0..c3c735d5d 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,18 +1,21 @@  defmodule Pleroma.Web.MastodonAPI.StatusView do    use Pleroma.Web, :view -  alias Pleroma.Web.MastodonAPI.{AccountView, StatusView} -  alias Pleroma.{User, Activity} + +  alias Pleroma.Activity +  alias Pleroma.HTML +  alias Pleroma.Repo +  alias Pleroma.User    alias Pleroma.Web.CommonAPI.Utils    alias Pleroma.Web.MediaProxy -  alias Pleroma.Repo -  alias Pleroma.HTML +  alias Pleroma.Web.MastodonAPI.AccountView +  alias Pleroma.Web.MastodonAPI.StatusView    # TODO: Add cached version.    defp get_replied_to_activities(activities) do      activities      |> Enum.map(fn -      %{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}} -> -        inReplyTo != "" && inReplyTo +      %{data: %{"type" => "Create", "object" => %{"inReplyTo" => in_reply_to}}} -> +        in_reply_to != "" && in_reply_to        _ ->          nil @@ -28,8 +31,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do    def render("index.json", opts) do      replied_to_activities = get_replied_to_activities(opts.activities) -    render_many( -      opts.activities, +    opts.activities +    |> render_many(        StatusView,        "status.json",        Map.put(opts, :replied_to_activities, replied_to_activities) @@ -72,9 +75,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        sensitive: false,        spoiler_text: "",        visibility: "public", -      media_attachments: [], +      media_attachments: reblogged[:media_attachments] || [],        mentions: mentions, -      tags: [], +      tags: reblogged[:tags] || [],        application: %{          name: "Web",          website: nil @@ -111,20 +114,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do      reply_to = get_reply_to(activity, opts)      reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"]) -    emojis = -      (activity.data["object"]["emoji"] || []) -      |> Enum.map(fn {name, url} -> -        name = HTML.strip_tags(name) - -        url = -          HTML.strip_tags(url) -          |> MediaProxy.url() - -        %{shortcode: name, url: url, static_url: url, visible_in_picker: false} -      end) -      content = -      render_content(object) +      object +      |> render_content()        |> HTML.filter_tags(User.html_filter_policy(opts[:for]))      %{ @@ -140,22 +132,21 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do        reblogs_count: announcement_count,        replies_count: 0,        favourites_count: like_count, -      reblogged: !!repeated, -      favourited: !!favorited, +      reblogged: present?(repeated), +      favourited: present?(favorited),        muted: false,        sensitive: sensitive,        spoiler_text: object["summary"] || "",        visibility: get_visibility(object),        media_attachments: attachments |> Enum.take(4),        mentions: mentions, -      # fix, -      tags: [], +      tags: tags,        application: %{          name: "Web",          website: nil        },        language: nil, -      emojis: emojis +      emojis: build_emojis(activity.data["object"]["emoji"])      }    end @@ -224,30 +215,56 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do    end    def render_content(%{"type" => "Video"} = object) do -    name = object["name"] - -    content = -      if !!name and name != "" do -        "<p><a href=\"#{object["id"]}\">#{name}</a></p>#{object["content"]}" -      else -        object["content"] || "" -      end +    with name when not is_nil(name) and name != "" <- object["name"] do +      "<p><a href=\"#{object["id"]}\">#{name}</a></p>#{object["content"]}" +    else +      _ -> object["content"] || "" +    end +  end -    content +  def render_content(%{"type" => object_type} = object) +      when object_type in ["Article", "Page"] do +    with summary when not is_nil(summary) and summary != "" <- object["name"], +         url when is_bitstring(url) <- object["url"] do +      "<p><a href=\"#{url}\">#{summary}</a></p>#{object["content"]}" +    else +      _ -> object["content"] || "" +    end    end -  def render_content(%{"type" => object_type} = object) when object_type in ["Article", "Page"] do -    summary = object["name"] +  def render_content(object), do: object["content"] || "" -    content = -      if !!summary and summary != "" and is_bitstring(object["url"]) do -        "<p><a href=\"#{object["url"]}\">#{summary}</a></p>#{object["content"]}" -      else -        object["content"] || "" -      end +  @doc """ +  Builds list emojis. + +  Arguments: `nil` or list tuple of name and url. + +  Returns list emojis. -    content +  ## Examples + +  iex> Pleroma.Web.MastodonAPI.StatusView.build_emojis([{"2hu", "corndog.png"}]) +  [%{shortcode: "2hu", static_url: "corndog.png", url: "corndog.png", visible_in_picker: false}] + +  """ +  @spec build_emojis(nil | list(tuple())) :: list(map()) +  def build_emojis(nil), do: [] + +  def build_emojis(emojis) do +    emojis +    |> Enum.map(fn {name, url} -> +      name = HTML.strip_tags(name) + +      url = +        url +        |> HTML.strip_tags() +        |> MediaProxy.url() + +      %{shortcode: name, url: url, static_url: url, visible_in_picker: false} +    end)    end -  def render_content(object), do: object["content"] || "" +  defp present?(nil), do: false +  defp present?(false), do: false +  defp present?(_), do: true  end diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex index e1b87e026..63140feb9 100644 --- a/lib/pleroma/web/media_proxy/controller.ex +++ b/lib/pleroma/web/media_proxy/controller.ex @@ -2,13 +2,12 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do    use Pleroma.Web, :controller    alias Pleroma.{Web.MediaProxy, ReverseProxy} -  @default_proxy_opts [max_body_length: 25 * 1_048_576] +  @default_proxy_opts [max_body_length: 25 * 1_048_576, http: [follow_redirect: true]]    def remote(conn, params = %{"sig" => sig64, "url" => url64}) do      with config <- Pleroma.Config.get([:media_proxy], []),           true <- Keyword.get(config, :enabled, false),           {:ok, url} <- MediaProxy.decode_url(sig64, url64), -         filename <- Path.basename(URI.parse(url).path),           :ok <- filename_matches(Map.has_key?(params, "filename"), conn.request_path, url) do        ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))      else @@ -24,11 +23,17 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do    end    def filename_matches(has_filename, path, url) do -    filename = MediaProxy.filename(url) +    filename = +      url +      |> MediaProxy.filename() +      |> URI.decode() -    cond do -      has_filename && filename && Path.basename(path) != filename -> {:wrong_filename, filename} -      true -> :ok +    path = URI.decode(path) + +    if has_filename && filename && Path.basename(path) != filename do +      {:wrong_filename, filename} +    else +      :ok      end    end  end diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 28aacb0b1..902ab1b77 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -14,7 +14,14 @@ defmodule Pleroma.Web.MediaProxy do        url      else        secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] -      base64 = Base.url_encode64(url, @base64_opts) + +      # The URL is url-decoded and encoded again to ensure it is correctly encoded and not twice. +      base64 = +        url +        |> URI.decode() +        |> URI.encode() +        |> Base.url_encode64(@base64_opts) +        sig = :crypto.hmac(:sha, secret, base64)        sig64 = sig |> Base.url_encode64(@base64_opts) diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 2ea75cf16..277dc6ba1 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -71,23 +71,28 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do          %{}        end -    features = [ -      "pleroma_api", -      "mastodon_api", -      "mastodon_api_streaming", -      if Keyword.get(media_proxy, :enabled) do -        "media_proxy" -      end, -      if Keyword.get(gopher, :enabled) do -        "gopher" -      end, -      if Keyword.get(chat, :enabled) do -        "chat" -      end, -      if Keyword.get(suggestions, :enabled) do -        "suggestions" -      end -    ] +    features = +      [ +        "pleroma_api", +        "mastodon_api", +        "mastodon_api_streaming", +        if Keyword.get(media_proxy, :enabled) do +          "media_proxy" +        end, +        if Keyword.get(gopher, :enabled) do +          "gopher" +        end, +        if Keyword.get(chat, :enabled) do +          "chat" +        end, +        if Keyword.get(suggestions, :enabled) do +          "suggestions" +        end, +        if Keyword.get(instance, :allow_relay) do +          "relay" +        end +      ] +      |> Enum.filter(& &1)      response = %{        version: "2.0", diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index d03c8b05a..20c2e799b 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -121,7 +121,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do    def token_exchange(          conn, -        %{"grant_type" => "password", "name" => name, "password" => password} = params +        %{"grant_type" => "password", "name" => name, "password" => _password} = params        ) do      params =        params diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 53d71440e..c6440c20e 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -375,19 +375,14 @@ defmodule Pleroma.Web.OStatus do    end    def fetch_activity_from_url(url) do -    try do -      with {:ok, activities} when length(activities) > 0 <- fetch_activity_from_atom_url(url) do -        {:ok, activities} -      else -        _e -> -          with {:ok, activities} <- fetch_activity_from_html_url(url) do -            {:ok, activities} -          end -      end -    rescue -      e -> -        Logger.debug("Couldn't get #{url}: #{inspect(e)}") -        {:error, "Couldn't get #{url}: #{inspect(e)}"} +    with {:ok, [_ | _] = activities} <- fetch_activity_from_atom_url(url) do +      {:ok, activities} +    else +      _e -> fetch_activity_from_html_url(url)      end +  rescue +    e -> +      Logger.debug("Couldn't get #{url}: #{inspect(e)}") +      {:error, "Couldn't get #{url}: #{inspect(e)}"}    end  end diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index af6e22c2b..9dfcf0f95 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -157,7 +157,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do           conn,           "activity+json",           %Activity{data: %{"type" => "Create"}} = activity, -         user +         _user         ) do      object = Object.normalize(activity.data["object"]) @@ -166,7 +166,7 @@ defmodule Pleroma.Web.OStatus.OStatusController do      |> json(ObjectView.render("object.json", %{object: object}))    end -  defp represent_activity(conn, "activity+json", _, _) do +  defp represent_activity(_conn, "activity+json", _, _) do      {:error, :not_found}    end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 75d965c6d..6253a28db 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -1,8 +1,6 @@  defmodule Pleroma.Web.Router do    use Pleroma.Web, :router -  alias Pleroma.{Repo, User, Web.Router} -    pipeline :api do      plug(:accepts, ["json"])      plug(:fetch_session) @@ -87,6 +85,15 @@ defmodule Pleroma.Web.Router do      plug(:accepts, ["html", "json"])    end +  pipeline :mailbox_preview do +    plug(:accepts, ["html"]) + +    plug(:put_secure_browser_headers, %{ +      "content-security-policy" => +        "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' 'unsafe-eval'" +    }) +  end +    scope "/api/pleroma", Pleroma.Web.TwitterAPI do      pipe_through(:pleroma_api)      get("/password_reset/:token", UtilController, :show_password_reset) @@ -98,6 +105,8 @@ defmodule Pleroma.Web.Router do      pipe_through(:admin_api)      delete("/user", AdminAPIController, :user_delete)      post("/user", AdminAPIController, :user_create) +    put("/users/tag", AdminAPIController, :tag_users) +    delete("/users/tag", AdminAPIController, :untag_users)      get("/permission_group/:nickname", AdminAPIController, :right_get)      get("/permission_group/:nickname/:permission_group", AdminAPIController, :right_get) @@ -268,6 +277,7 @@ defmodule Pleroma.Web.Router do      get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation)      post("/account/register", TwitterAPI.Controller, :register) +    post("/account/password_reset", TwitterAPI.Controller, :password_reset)      get("/search", TwitterAPI.Controller, :search)      get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) @@ -424,6 +434,14 @@ defmodule Pleroma.Web.Router do      get("/:sig/:url/:filename", MediaProxyController, :remote)    end +  if Mix.env() == :dev do +    scope "/dev" do +      pipe_through([:mailbox_preview]) + +      forward("/mailbox", Plug.Swoosh.MailboxPreview, base_path: "/dev/mailbox") +    end +  end +    scope "/", Fallback do      get("/registration/:token", RedirectController, :registration_page)      get("/*path", RedirectController, :redirector) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 0e2cfddd0..b67b1333f 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -180,7 +180,7 @@ defmodule Pleroma.Web.Salmon do      "Undo",      "Delete"    ] -  def publish(user, activity, poster \\ &@httpoison.post/4) +  def publish(user, activity, poster \\ &@httpoison.post/3)    def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster)        when type in @supported_activities do diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 99b8b7063..29c44e9d5 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -61,8 +61,6 @@ defmodule Pleroma.Web.Streamer do    end    def handle_cast(%{action: :stream, topic: "list", item: item}, topics) do -    author = User.get_cached_by_ap_id(item.data["actor"]) -      # filter the recipient list if the activity is not public, see #270.      recipient_lists =        case ActivityPub.is_public?(item) do diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex index 58a3736fd..df037c01e 100644 --- a/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_failed.html.eex @@ -1 +1,2 @@  <h2>Password reset failed</h2> +<h3><a href="<%= Pleroma.Web.base_url() %>">Homepage</a></h3> diff --git a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex index c7dfcb6dd..f30ba3274 100644 --- a/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex +++ b/lib/pleroma/web/templates/twitter_api/util/password_reset_success.html.eex @@ -1 +1,2 @@  <h2>Password changed!</h2> +<h3><a href="<%= Pleroma.Web.base_url() %>">Homepage</a></h3> diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 1459f3c90..387f5bd71 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -6,9 +6,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do    alias Pleroma.Web.WebFinger    alias Pleroma.Web.CommonAPI    alias Comeonin.Pbkdf2 -  alias Pleroma.{Formatter, Emoji}    alias Pleroma.Web.ActivityPub.ActivityPub -  alias Pleroma.{Repo, PasswordResetToken, User} +  alias Pleroma.{Repo, PasswordResetToken, User, Emoji}    def show_password_reset(conn, %{"token" => token}) do      with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), @@ -192,7 +191,10 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do            formattingOptionsEnabled: Keyword.get(instance_fe, :formatting_options_enabled),            collapseMessageWithSubject: Keyword.get(instance_fe, :collapse_message_with_subject),            hidePostStats: Keyword.get(instance_fe, :hide_post_stats), -          hideUserStats: Keyword.get(instance_fe, :hide_user_stats) +          hideUserStats: Keyword.get(instance_fe, :hide_user_stats), +          scopeCopy: Keyword.get(instance_fe, :scope_copy), +          subjectLineBehavior: Keyword.get(instance_fe, :subject_line_behavior), +          alwaysShowSubjectInput: Keyword.get(instance_fe, :always_show_subject_input)          }          managed_config = Keyword.get(instance, :managed_config) diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index fbd33f07e..2808192b0 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -141,7 +141,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do    end    def to_map( -        %Activity{data: %{"object" => %{"content" => content} = object}} = activity, +        %Activity{data: %{"object" => %{"content" => _content} = object}} = activity,          %{user: user} = opts        ) do      created_at = object["published"] |> Utils.date_to_asctime() @@ -165,7 +165,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do      tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags -    {summary, content} = ActivityView.render_content(object) +    {_summary, content} = ActivityView.render_content(object)      html =        HTML.filter_tags(content, User.html_filter_policy(opts[:for])) @@ -173,7 +173,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do      video =        if object["type"] == "Video" do -        vid = [object] +        [object]        else          []        end diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 9c485d965..1e764f24a 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -2,18 +2,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    alias Pleroma.{UserInviteToken, User, Activity, Repo, Object}    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.TwitterAPI.UserView -  alias Pleroma.Web.{OStatus, CommonAPI} -  alias Pleroma.Web.MediaProxy +  alias Pleroma.Web.CommonAPI    import Ecto.Query -  @httpoison Application.get_env(:pleroma, :httpoison) -    def create_status(%User{} = user, %{"status" => _} = data) do      CommonAPI.post(user, data)    end    def delete(%User{} = user, id) do -    with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id), +    with %Activity{data: %{"type" => _type}} <- Repo.get(Activity, id),           {:ok, activity} <- CommonAPI.delete(id, user) do        {:ok, activity}      end @@ -37,7 +34,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do    def unfollow(%User{} = follower, params) do      with {:ok, %User{} = unfollowed} <- get_user(params), -         {:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed), +         {:ok, follower, _follow_activity} <- User.unfollow(follower, unfollowed),           {:ok, _activity} <- ActivityPub.unfollow(follower, unfollowed) do        {:ok, follower, unfollowed}      else @@ -170,6 +167,25 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do      end    end +  def password_reset(nickname_or_email) do +    with true <- is_binary(nickname_or_email), +         %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email), +         {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do +      user +      |> Pleroma.UserEmail.password_reset_email(token_record.token) +      |> Pleroma.Mailer.deliver() +    else +      false -> +        {:error, "bad user identifier"} + +      %User{local: false} -> +        {:error, "remote user"} + +      nil -> +        {:error, "unknown user"} +    end +  end +    def get_by_id_or_nickname(id_or_nickname) do      if !is_integer(id_or_nickname) && :error == Integer.parse(id_or_nickname) do        Repo.get_by(User, nickname: id_or_nickname) @@ -244,10 +260,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do      _activities = Repo.all(q)    end -  defp make_date do -    DateTime.utc_now() |> DateTime.to_iso8601() -  end -    # DEPRECATED mostly, context objects are now created at insertion time.    def context_to_conversation_id(context) do      with %Object{id: id} <- Object.get_cached_by_ap_id(context) do diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 0ccf937b0..38eff8191 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -1,9 +1,10 @@  defmodule Pleroma.Web.TwitterAPI.Controller do    use Pleroma.Web, :controller -  alias Pleroma.Formatter + +  import Pleroma.Web.ControllerHelper, only: [json_response: 3] +    alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}    alias Pleroma.Web.CommonAPI -  alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils    alias Pleroma.{Repo, Activity, Object, User, Notification}    alias Pleroma.Web.ActivityPub.ActivityPub    alias Pleroma.Web.ActivityPub.Utils @@ -155,7 +156,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do      |> render(NotificationView, "notification.json", %{notifications: notifications, for: user})    end -  def notifications_read(%{assigns: %{user: user}} = conn, _) do +  def notifications_read(%{assigns: %{user: _user}} = conn, _) do      bad_request_reply(conn, "You need to specify latest_id")    end @@ -324,6 +325,14 @@ defmodule Pleroma.Web.TwitterAPI.Controller do      end    end +  def password_reset(conn, params) do +    nickname_or_email = params["email"] || params["nickname"] + +    with {:ok, _} <- TwitterAPI.password_reset(nickname_or_email) do +      json_response(conn, :no_content, "") +    end +  end +    def update_avatar(%{assigns: %{user: user}} = conn, params) do      {:ok, object} = ActivityPub.upload(params, type: :avatar)      change = Changeset.change(user, %{avatar: object.data}) @@ -416,7 +425,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do      end    end -  def approve_friend_request(conn, %{"user_id" => uid} = params) do +  def approve_friend_request(conn, %{"user_id" => uid} = _params) do      with followed <- conn.assigns[:user],           uid when is_number(uid) <- String.to_integer(uid),           %User{} = follower <- Repo.get(User, uid), @@ -436,7 +445,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do      end    end -  def deny_friend_request(conn, %{"user_id" => uid} = params) do +  def deny_friend_request(conn, %{"user_id" => uid} = _params) do      with followed <- conn.assigns[:user],           uid when is_number(uid) <- String.to_integer(uid),           %User{} = follower <- Repo.get(User, uid), diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 83e8fb765..e5caed28f 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -190,6 +190,11 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do      text = "#{user.nickname} favorited a status." +    favorited_status = +      if liked_activity, +        do: render("activity.json", Map.merge(opts, %{activity: liked_activity})), +        else: nil +      %{        "id" => activity.id,        "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), @@ -199,6 +204,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do        "is_post_verb" => false,        "uri" => "tag:#{activity.data["id"]}:objectType=Favourite",        "created_at" => created_at, +      "favorited_status" => favorited_status,        "in_reply_to_status_id" => liked_activity_id,        "external_url" => activity.data["id"],        "activity_type" => "like" diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index b78024ed7..b3459af9a 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -77,7 +77,12 @@ defmodule Pleroma.Web.TwitterAPI.UserView do        "locked" => user.info.locked,        "default_scope" => user.info.default_scope,        "no_rich_text" => user.info.no_rich_text, -      "fields" => fields +      "fields" => fields, + +      # Pleroma extension +      "pleroma" => %{ +        "tags" => user.tags +      }      }      if assigns[:token] do diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 0ff3b8b5f..47c733da2 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -256,8 +256,7 @@ defmodule Pleroma.Web.WebFinger do      with response <-             @httpoison.get(               address, -             [Accept: "application/xrd+xml,application/jrd+json"], -             follow_redirect: true +             Accept: "application/xrd+xml,application/jrd+json"             ),           {:ok, %{status: status, body: body}} when status in 200..299 <- response do        doc = XML.parse_document(body) diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml/xml.ex index da3f68ecb..b3ccf4a55 100644 --- a/lib/pleroma/web/xml/xml.ex +++ b/lib/pleroma/web/xml/xml.ex @@ -25,15 +25,15 @@ defmodule Pleroma.Web.XML do        {doc, _rest} =          text          |> :binary.bin_to_list() -        |> :xmerl_scan.string() +        |> :xmerl_scan.string(quiet: true)        doc -    catch -      :exit, _error -> +    rescue +      _e ->          Logger.debug("Couldn't parse XML: #{inspect(text)}")          :error -    rescue -      e -> +    catch +      :exit, _error ->          Logger.debug("Couldn't parse XML: #{inspect(text)}")          :error      end | 
