summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mix/tasks/deactivate_user.ex19
-rw-r--r--lib/mix/tasks/generate_config.ex47
-rw-r--r--lib/mix/tasks/generate_invite_token.ex32
-rw-r--r--lib/mix/tasks/generate_password_reset.ex33
-rw-r--r--lib/mix/tasks/make_moderator.ex37
-rw-r--r--lib/mix/tasks/pleroma/common.ex24
-rw-r--r--lib/mix/tasks/pleroma/instance.ex156
-rw-r--r--lib/mix/tasks/pleroma/relay.ex43
-rw-r--r--lib/mix/tasks/pleroma/sample_config.eex (renamed from lib/mix/tasks/sample_config.eex)24
-rw-r--r--lib/mix/tasks/pleroma/sample_psql.eex (renamed from lib/mix/tasks/sample_psql.eex)1
-rw-r--r--lib/mix/tasks/pleroma/uploads.ex (renamed from lib/mix/tasks/migrate_local_uploads.ex)33
-rw-r--r--lib/mix/tasks/pleroma/user.ex293
-rw-r--r--lib/mix/tasks/reactivate_user.ex19
-rw-r--r--lib/mix/tasks/register_user.ex30
-rw-r--r--lib/mix/tasks/relay_follow.ex24
-rw-r--r--lib/mix/tasks/relay_unfollow.ex23
-rw-r--r--lib/mix/tasks/rm_user.ex19
-rw-r--r--lib/mix/tasks/set_admin.ex32
-rw-r--r--lib/mix/tasks/set_locked.ex39
-rw-r--r--lib/mix/tasks/unsubscribe_user.ex38
-rw-r--r--lib/pleroma/application.ex4
-rw-r--r--lib/pleroma/emoji.ex4
-rw-r--r--lib/pleroma/filter.ex12
-rw-r--r--lib/pleroma/formatter.ex10
-rw-r--r--lib/pleroma/gopher/server.ex2
-rw-r--r--lib/pleroma/html.ex10
-rw-r--r--lib/pleroma/http/connection.ex32
-rw-r--r--lib/pleroma/http/http.ex47
-rw-r--r--lib/pleroma/http/request_builder.ex126
-rw-r--r--lib/pleroma/list.ex4
-rw-r--r--lib/pleroma/mime.ex4
-rw-r--r--lib/pleroma/notification.ex9
-rw-r--r--lib/pleroma/object.ex9
-rw-r--r--lib/pleroma/plugs/authentication_plug.ex9
-rw-r--r--lib/pleroma/plugs/basic_auth_decoder_plug.ex2
-rw-r--r--lib/pleroma/plugs/federating_plug.ex2
-rw-r--r--lib/pleroma/plugs/http_security_plug.ex10
-rw-r--r--lib/pleroma/plugs/oauth_plug.ex72
-rw-r--r--lib/pleroma/plugs/session_authentication_plug.ex1
-rw-r--r--lib/pleroma/plugs/uploaded_media.ex4
-rw-r--r--lib/pleroma/plugs/user_fetcher_plug.ex2
-rw-r--r--lib/pleroma/reverse_proxy.ex43
-rw-r--r--lib/pleroma/upload.ex25
-rw-r--r--lib/pleroma/upload/filter/anonymize_filename.ex19
-rw-r--r--lib/pleroma/upload/filter/dedupe.ex5
-rw-r--r--lib/pleroma/upload/filter/mogrify.ex2
-rw-r--r--lib/pleroma/uploaders/local.ex2
-rw-r--r--lib/pleroma/uploaders/mdii.ex6
-rw-r--r--lib/pleroma/uploaders/swift/keystone.ex4
-rw-r--r--lib/pleroma/uploaders/swift/swift.ex5
-rw-r--r--lib/pleroma/user.ex65
-rw-r--r--lib/pleroma/user/info.ex12
-rw-r--r--lib/pleroma/user_invite_token.ex3
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub.ex18
-rw-r--r--lib/pleroma/web/activity_pub/activity_pub_controller.ex2
-rw-r--r--lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex40
-rw-r--r--lib/pleroma/web/activity_pub/mrf/simple_policy.ex2
-rw-r--r--lib/pleroma/web/activity_pub/transmogrifier.ex82
-rw-r--r--lib/pleroma/web/activity_pub/utils.ex2
-rw-r--r--lib/pleroma/web/activity_pub/views/user_view.ex12
-rw-r--r--lib/pleroma/web/admin_api/admin_api_controller.ex67
-rw-r--r--lib/pleroma/web/common_api/utils.ex4
-rw-r--r--lib/pleroma/web/controller_helper.ex9
-rw-r--r--lib/pleroma/web/federator/federator.ex5
-rw-r--r--lib/pleroma/web/federator/retry_queue.ex21
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_api_controller.ex127
-rw-r--r--lib/pleroma/web/mastodon_api/mastodon_socket.ex19
-rw-r--r--lib/pleroma/web/mastodon_api/views/account_view.ex5
-rw-r--r--lib/pleroma/web/mastodon_api/views/mastodon_view.ex1
-rw-r--r--lib/pleroma/web/mastodon_api/views/push_subscription_view.ex11
-rw-r--r--lib/pleroma/web/media_proxy/controller.ex21
-rw-r--r--lib/pleroma/web/media_proxy/media_proxy.ex9
-rw-r--r--lib/pleroma/web/nodeinfo/nodeinfo_controller.ex39
-rw-r--r--lib/pleroma/web/oauth/oauth_controller.ex2
-rw-r--r--lib/pleroma/web/ostatus/ostatus.ex31
-rw-r--r--lib/pleroma/web/ostatus/ostatus_controller.ex4
-rw-r--r--lib/pleroma/web/push/push.ex116
-rw-r--r--lib/pleroma/web/push/subscription.ex66
-rw-r--r--lib/pleroma/web/router.ex10
-rw-r--r--lib/pleroma/web/salmon/salmon.ex7
-rw-r--r--lib/pleroma/web/streamer.ex2
-rw-r--r--lib/pleroma/web/twitter_api/controllers/util_controller.ex9
-rw-r--r--lib/pleroma/web/twitter_api/representers/activity_representer.ex6
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api.ex19
-rw-r--r--lib/pleroma/web/twitter_api/twitter_api_controller.ex92
-rw-r--r--lib/pleroma/web/twitter_api/views/user_view.ex7
-rw-r--r--lib/pleroma/web/web_finger/web_finger.ex6
-rw-r--r--lib/pleroma/web/websub/websub.ex11
-rw-r--r--lib/pleroma/web/xml/xml.ex8
89 files changed, 1611 insertions, 813 deletions
diff --git a/lib/mix/tasks/deactivate_user.ex b/lib/mix/tasks/deactivate_user.ex
deleted file mode 100644
index e71ed1ec0..000000000
--- a/lib/mix/tasks/deactivate_user.ex
+++ /dev/null
@@ -1,19 +0,0 @@
-defmodule Mix.Tasks.DeactivateUser do
- use Mix.Task
- alias Pleroma.User
-
- @moduledoc """
- Deactivates a user (local or remote)
-
- Usage: ``mix deactivate_user <nickname>``
-
- Example: ``mix deactivate_user lain``
- """
- def run([nickname]) do
- Mix.Task.run("app.start")
-
- with user <- User.get_by_nickname(nickname) do
- User.deactivate(user)
- end
- end
-end
diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex
deleted file mode 100644
index e3cbbf131..000000000
--- a/lib/mix/tasks/generate_config.ex
+++ /dev/null
@@ -1,47 +0,0 @@
-defmodule Mix.Tasks.GenerateConfig do
- use Mix.Task
-
- @moduledoc """
- Generate a new config
-
- ## Usage
- ``mix generate_config``
-
- This mix task is interactive, and will overwrite the config present at ``config/generated_config.exs``.
- """
-
- def run(_) do
- IO.puts("Answer a few questions to generate a new config\n")
- IO.puts("--- THIS WILL OVERWRITE YOUR config/generated_config.exs! ---\n")
- domain = IO.gets("What is your domain name? (e.g. pleroma.soykaf.com): ") |> String.trim()
- name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim()
- email = IO.gets("What's your admin email address: ") |> String.trim()
-
- secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
- dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
-
- resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass)
-
- result =
- EEx.eval_file(
- "lib/mix/tasks/sample_config.eex",
- domain: domain,
- email: email,
- name: name,
- secret: secret,
- dbpass: dbpass
- )
-
- IO.puts(
- "\nWriting config to config/generated_config.exs.\n\nCheck it and configure your database, then copy it to either config/dev.secret.exs or config/prod.secret.exs"
- )
-
- File.write("config/generated_config.exs", result)
-
- IO.puts(
- "\nWriting setup_db.psql, please run it as postgre superuser, i.e.: sudo su postgres -c 'psql -f config/setup_db.psql'"
- )
-
- File.write("config/setup_db.psql", resultSql)
- end
-end
diff --git a/lib/mix/tasks/generate_invite_token.ex b/lib/mix/tasks/generate_invite_token.ex
deleted file mode 100644
index 418ef3790..000000000
--- a/lib/mix/tasks/generate_invite_token.ex
+++ /dev/null
@@ -1,32 +0,0 @@
-defmodule Mix.Tasks.GenerateInviteToken do
- use Mix.Task
-
- @moduledoc """
- Generates invite token
-
- This is in the form of a URL to be used by the Invited user to register themselves.
-
- ## Usage
- ``mix generate_invite_token``
- """
- def run([]) do
- Mix.Task.run("app.start")
-
- with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
- IO.puts("Generated user invite token")
-
- IO.puts(
- "Url: #{
- Pleroma.Web.Router.Helpers.redirect_url(
- Pleroma.Web.Endpoint,
- :registration_page,
- token.token
- )
- }"
- )
- else
- _ ->
- IO.puts("Error creating token")
- end
- end
-end
diff --git a/lib/mix/tasks/generate_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex
deleted file mode 100644
index f7f4c4f59..000000000
--- a/lib/mix/tasks/generate_password_reset.ex
+++ /dev/null
@@ -1,33 +0,0 @@
-defmodule Mix.Tasks.GeneratePasswordReset do
- use Mix.Task
- alias Pleroma.User
-
- @moduledoc """
- Generate password reset link for user
-
- Usage: ``mix generate_password_reset <nickname>``
-
- Example: ``mix generate_password_reset lain``
- """
- def run([nickname]) do
- Mix.Task.run("app.start")
-
- with %User{local: true} = user <- User.get_by_nickname(nickname),
- {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
- IO.puts("Generated password reset token for #{user.nickname}")
-
- IO.puts(
- "Url: #{
- Pleroma.Web.Router.Helpers.util_url(
- Pleroma.Web.Endpoint,
- :show_password_reset,
- token.token
- )
- }"
- )
- else
- _ ->
- IO.puts("No local user #{nickname}")
- end
- end
-end
diff --git a/lib/mix/tasks/make_moderator.ex b/lib/mix/tasks/make_moderator.ex
deleted file mode 100644
index 15586dc30..000000000
--- a/lib/mix/tasks/make_moderator.ex
+++ /dev/null
@@ -1,37 +0,0 @@
-defmodule Mix.Tasks.SetModerator do
- @moduledoc """
- Set moderator to a local user
-
- Usage: ``mix set_moderator <nickname>``
-
- Example: ``mix set_moderator lain``
- """
-
- use Mix.Task
- import Mix.Ecto
- alias Pleroma.{Repo, User}
-
- def run([nickname | rest]) do
- Application.ensure_all_started(:pleroma)
-
- moderator =
- case rest do
- [moderator] -> moderator == "true"
- _ -> true
- end
-
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
- info =
- user.info
- |> Map.put("is_moderator", !!moderator)
-
- cng = User.info_changeset(user, %{info: info})
- {:ok, user} = User.update_and_set_cache(cng)
-
- IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}")
- else
- _ ->
- IO.puts("No local user #{nickname}")
- end
- end
-end
diff --git a/lib/mix/tasks/pleroma/common.ex b/lib/mix/tasks/pleroma/common.ex
new file mode 100644
index 000000000..36432c291
--- /dev/null
+++ b/lib/mix/tasks/pleroma/common.ex
@@ -0,0 +1,24 @@
+defmodule Mix.Tasks.Pleroma.Common do
+ @doc "Common functions to be reused in mix tasks"
+ def start_pleroma do
+ Mix.Task.run("app.start")
+ end
+
+ def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
+ Keyword.get(options, opt) ||
+ case Mix.shell().prompt("#{prompt} [#{defname || defval}]") do
+ "\n" ->
+ case defval do
+ nil -> get_option(options, opt, prompt, defval)
+ defval -> defval
+ end
+
+ opt ->
+ opt |> String.trim()
+ end
+ end
+
+ def escape_sh_path(path) do
+ ~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
+ end
+end
diff --git a/lib/mix/tasks/pleroma/instance.ex b/lib/mix/tasks/pleroma/instance.ex
new file mode 100644
index 000000000..3be856115
--- /dev/null
+++ b/lib/mix/tasks/pleroma/instance.ex
@@ -0,0 +1,156 @@
+defmodule Mix.Tasks.Pleroma.Instance do
+ use Mix.Task
+ alias Mix.Tasks.Pleroma.Common
+
+ @shortdoc "Manages Pleroma instance"
+ @moduledoc """
+ Manages Pleroma instance.
+
+ ## Generate a new instance config.
+
+ mix pleroma.instance gen [OPTION...]
+
+ If any options are left unspecified, you will be prompted interactively
+
+ ## Options
+
+ - `-f`, `--force` - overwrite any output files
+ - `-o PATH`, `--output PATH` - the output file for the generated configuration
+ - `--output-psql PATH` - the output file for the generated PostgreSQL setup
+ - `--domain DOMAIN` - the domain of your instance
+ - `--instance-name INSTANCE_NAME` - the name of your instance
+ - `--admin-email ADMIN_EMAIL` - the email address of the instance admin
+ - `--dbhost HOSTNAME` - the hostname of the PostgreSQL database to use
+ - `--dbname DBNAME` - the name of the database to use
+ - `--dbuser DBUSER` - the user (aka role) to use for the database connection
+ - `--dbpass DBPASS` - the password to use for the database connection
+ """
+
+ def run(["gen" | rest]) do
+ {options, [], []} =
+ OptionParser.parse(
+ rest,
+ strict: [
+ force: :boolean,
+ output: :string,
+ output_psql: :string,
+ domain: :string,
+ instance_name: :string,
+ admin_email: :string,
+ dbhost: :string,
+ dbname: :string,
+ dbuser: :string,
+ dbpass: :string
+ ],
+ aliases: [
+ o: :output,
+ f: :force
+ ]
+ )
+
+ paths =
+ [config_path, psql_path] = [
+ Keyword.get(options, :output, "config/generated_config.exs"),
+ Keyword.get(options, :output_psql, "config/setup_db.psql")
+ ]
+
+ will_overwrite = Enum.filter(paths, &File.exists?/1)
+ proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
+
+ unless not proceed? do
+ domain =
+ Common.get_option(
+ options,
+ :domain,
+ "What domain will your instance use? (e.g pleroma.soykaf.com)"
+ )
+
+ name =
+ Common.get_option(
+ options,
+ :name,
+ "What is the name of your instance? (e.g. Pleroma/Soykaf)"
+ )
+
+ email = Common.get_option(options, :admin_email, "What is your admin email address?")
+
+ dbhost =
+ Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
+
+ dbname =
+ Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev")
+
+ dbuser =
+ Common.get_option(
+ options,
+ :dbuser,
+ "What is the user used to connect to your database?",
+ "pleroma"
+ )
+
+ dbpass =
+ Common.get_option(
+ options,
+ :dbpass,
+ "What is the password used to connect to your database?",
+ :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
+ "autogenerated"
+ )
+
+ secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
+ {web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
+
+ result_config =
+ EEx.eval_file(
+ "sample_config.eex" |> Path.expand(__DIR__),
+ domain: domain,
+ email: email,
+ name: name,
+ dbhost: dbhost,
+ dbname: dbname,
+ dbuser: dbuser,
+ dbpass: dbpass,
+ version: Pleroma.Mixfile.project() |> Keyword.get(:version),
+ secret: secret,
+ web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
+ web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
+ )
+
+ result_psql =
+ EEx.eval_file(
+ "sample_psql.eex" |> Path.expand(__DIR__),
+ dbname: dbname,
+ dbuser: dbuser,
+ dbpass: dbpass
+ )
+
+ Mix.shell().info(
+ "Writing config to #{config_path}. You should rename it to config/prod.secret.exs or config/dev.secret.exs."
+ )
+
+ File.write(config_path, result_config)
+ Mix.shell().info("Writing #{psql_path}.")
+ File.write(psql_path, result_psql)
+
+ Mix.shell().info(
+ "\n" <>
+ """
+ To get started:
+ 1. Verify the contents of the generated files.
+ 2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)}`.
+ """ <>
+ if config_path in ["config/dev.secret.exs", "config/prod.secret.exs"] do
+ ""
+ else
+ "3. Run `mv #{Common.escape_sh_path(config_path)} 'config/prod.secret.exs'`."
+ end
+ )
+ else
+ Mix.shell().error(
+ "The task would have overwritten the following files:\n" <>
+ (Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
+ "Rerun with `--force` to overwrite them."
+ )
+ end
+ end
+end
diff --git a/lib/mix/tasks/pleroma/relay.ex b/lib/mix/tasks/pleroma/relay.ex
new file mode 100644
index 000000000..03586d6c3
--- /dev/null
+++ b/lib/mix/tasks/pleroma/relay.ex
@@ -0,0 +1,43 @@
+defmodule Mix.Tasks.Pleroma.Relay do
+ use Mix.Task
+ alias Pleroma.Web.ActivityPub.Relay
+ alias Mix.Tasks.Pleroma.Common
+
+ @shortdoc "Manages remote relays"
+ @moduledoc """
+ Manages remote relays
+
+ ## Follow a remote relay
+
+ ``mix pleroma.relay follow <relay_url>``
+
+ Example: ``mix pleroma.relay follow https://example.org/relay``
+
+ ## Unfollow a remote relay
+
+ ``mix pleroma.relay unfollow <relay_url>``
+
+ Example: ``mix pleroma.relay unfollow https://example.org/relay``
+ """
+ def run(["follow", target]) do
+ Common.start_pleroma()
+
+ 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
+ {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
+ end
+ end
+
+ def run(["unfollow", target]) do
+ Common.start_pleroma()
+
+ 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
+ {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
+ end
+ end
+end
diff --git a/lib/mix/tasks/sample_config.eex b/lib/mix/tasks/pleroma/sample_config.eex
index 462c34636..0cd572797 100644
--- a/lib/mix/tasks/sample_config.eex
+++ b/lib/mix/tasks/pleroma/sample_config.eex
@@ -1,3 +1,8 @@
+# Pleroma instance configuration
+
+# NOTE: This file should not be committed to a repo or otherwise made public
+# without removing sensitive information.
+
use Mix.Config
config :pleroma, Pleroma.Web.Endpoint,
@@ -16,15 +21,20 @@ config :pleroma, :media_proxy,
redirect_on_failure: true
#base_url: "https://cache.pleroma.social"
-# Configure your database
config :pleroma, Pleroma.Repo,
adapter: Ecto.Adapters.Postgres,
- username: "pleroma",
+ username: "<%= dbuser %>",
password: "<%= dbpass %>",
- database: "pleroma_dev",
- hostname: "localhost",
+ database: "<%= dbname %>",
+ hostname: "<%= dbhost %>",
pool_size: 10
+# Configure web push notifications
+config :web_push_encryption, :vapid_details,
+ subject: "mailto:<%= email %>",
+ public_key: "<%= web_push_public_key %>",
+ private_key: "<%= web_push_private_key %>"
+
# Enable Strict-Transport-Security once SSL is working:
# config :pleroma, :http_security,
# sts: true
@@ -50,9 +60,9 @@ config :pleroma, Pleroma.Repo,
# Configure Openstack Swift support if desired.
-#
-# Many openstack deployments are different, so config is left very open with
-# no assumptions made on which provider you're using. This should allow very
+#
+# Many openstack deployments are different, so config is left very open with
+# no assumptions made on which provider you're using. This should allow very
# wide support without needing separate handlers for OVH, Rackspace, etc.
#
# config :pleroma, Pleroma.Uploaders.Swift,
diff --git a/lib/mix/tasks/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex
index b6f57948b..c89b34ef2 100644
--- a/lib/mix/tasks/sample_psql.eex
+++ b/lib/mix/tasks/pleroma/sample_psql.eex
@@ -4,3 +4,4 @@ CREATE DATABASE pleroma_dev OWNER pleroma;
--Extensions made by ecto.migrate that need superuser access
CREATE EXTENSION IF NOT EXISTS citext;
CREATE EXTENSION IF NOT EXISTS pg_trgm;
+CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
diff --git a/lib/mix/tasks/migrate_local_uploads.ex b/lib/mix/tasks/pleroma/uploads.ex
index 8f9e210c0..c6b840130 100644
--- a/lib/mix/tasks/migrate_local_uploads.ex
+++ b/lib/mix/tasks/pleroma/uploads.ex
@@ -1,16 +1,19 @@
-defmodule Mix.Tasks.MigrateLocalUploads do
+defmodule Mix.Tasks.Pleroma.Uploads do
use Mix.Task
- import Mix.Ecto
- alias Pleroma.{Upload, Uploaders.Local, Uploaders.S3}
+ alias Pleroma.{Upload, Uploaders.Local}
+ alias Mix.Tasks.Pleroma.Common
require Logger
@log_every 50
@shortdoc "Migrate uploads from local to remote storage"
+ @doc """
+ Manages uploads
+ ## Migrate uploads from local to remote storage
- def run([target_uploader | args]) do
+ """
+ def run(["migrate_local", target_uploader | args]) do
delete? = Enum.member?(args, "--delete")
- Application.ensure_all_started(:pleroma)
-
+ Common.start_pleroma()
local_path = Pleroma.Config.get!([Local, :uploads])
uploader = Module.concat(Pleroma.Uploaders, target_uploader)
@@ -24,10 +27,10 @@ defmodule Mix.Tasks.MigrateLocalUploads do
Pleroma.Config.put([Upload, :uploader], uploader)
end
- Logger.info("Migrating files from local #{local_path} to #{to_string(uploader)}")
+ Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}")
if delete? do
- Logger.warn(
+ Mix.shell().info(
"Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"
)
@@ -54,7 +57,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do
File.exists?(root_path) ->
file = Path.basename(id)
- [hash, ext] = String.split(id, ".")
+ hash = Path.rootname(id)
{%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path}
true ->
@@ -64,7 +67,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do
|> Enum.filter(& &1)
total_count = length(uploads)
- Logger.info("Found #{total_count} uploads")
+ Mix.shell().info("Found #{total_count} uploads")
uploads
|> Task.async_stream(
@@ -76,7 +79,7 @@ defmodule Mix.Tasks.MigrateLocalUploads do
:ok
error ->
- Logger.error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
+ Mix.shell().error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
end
end,
timeout: 150_000
@@ -84,14 +87,10 @@ defmodule Mix.Tasks.MigrateLocalUploads do
|> Stream.chunk_every(@log_every)
|> Enum.reduce(0, fn done, count ->
count = count + length(done)
- Logger.info("Uploaded #{count}/#{total_count} files")
+ Mix.shell().info("Uploaded #{count}/#{total_count} files")
count
end)
- Logger.info("Done!")
- end
-
- def run(_) do
- Logger.error("Usage: migrate_local_uploads S3|Swift [--delete]")
+ Mix.shell().info("Done!")
end
end
diff --git a/lib/mix/tasks/pleroma/user.ex b/lib/mix/tasks/pleroma/user.ex
new file mode 100644
index 000000000..2675b021d
--- /dev/null
+++ b/lib/mix/tasks/pleroma/user.ex
@@ -0,0 +1,293 @@
+defmodule Mix.Tasks.Pleroma.User do
+ use Mix.Task
+ import Ecto.Changeset
+ alias Pleroma.{Repo, User}
+ alias Mix.Tasks.Pleroma.Common
+
+ @shortdoc "Manages Pleroma users"
+ @moduledoc """
+ Manages Pleroma users.
+
+ ## Create a new user.
+
+ mix pleroma.user new NICKNAME EMAIL [OPTION...]
+
+ Options:
+ - `--name NAME` - the user's name (i.e., "Lain Iwakura")
+ - `--bio BIO` - the user's bio
+ - `--password PASSWORD` - the user's password
+ - `--moderator`/`--no-moderator` - whether the user is a moderator
+ - `--admin`/`--no-admin` - whether the user is an admin
+
+ ## Generate an invite link.
+
+ mix pleroma.user invite
+
+ ## Delete the user's account.
+
+ mix pleroma.user rm NICKNAME
+
+ ## Deactivate or activate the user's account.
+
+ 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.
+
+ mix pleroma.user reset_password NICKNAME
+
+ ## Set the value of the given user's settings.
+
+ mix pleroma.user set NICKNAME [OPTION...]
+
+ Options:
+ - `--locked`/`--no-locked` - whether the user's account is locked
+ - `--moderator`/`--no-moderator` - whether the user is a moderator
+ - `--admin`/`--no-admin` - whether the user is an admin
+ """
+ def run(["new", nickname, email | rest]) do
+ {options, [], []} =
+ OptionParser.parse(
+ rest,
+ strict: [
+ name: :string,
+ bio: :string,
+ password: :string,
+ moderator: :boolean,
+ admin: :boolean
+ ]
+ )
+
+ name = Keyword.get(options, :name, nickname)
+ bio = Keyword.get(options, :bio, "")
+
+ {password, generated_password?} =
+ case Keyword.get(options, :password) do
+ nil ->
+ {:crypto.strong_rand_bytes(16) |> Base.encode64(), true}
+
+ password ->
+ {password, false}
+ end
+
+ moderator? = Keyword.get(options, :moderator, false)
+ admin? = Keyword.get(options, :admin, false)
+
+ Mix.shell().info("""
+ A user will be created with the following information:
+ - nickname: #{nickname}
+ - email: #{email}
+ - password: #{
+ if(generated_password?, do: "[generated; a reset link will be created]", else: password)
+ }
+ - name: #{name}
+ - bio: #{bio}
+ - moderator: #{if(moderator?, do: "true", else: "false")}
+ - admin: #{if(admin?, do: "true", else: "false")}
+ """)
+
+ proceed? = Mix.shell().yes?("Continue?")
+
+ unless not proceed? do
+ Common.start_pleroma()
+
+ params =
+ %{
+ nickname: nickname,
+ email: email,
+ password: password,
+ password_confirmation: password,
+ name: name,
+ bio: bio
+ }
+ |> IO.inspect()
+
+ user = User.register_changeset(%User{}, params)
+ Repo.insert!(user)
+
+ Mix.shell().info("User #{nickname} created")
+
+ if moderator? do
+ run(["set", nickname, "--moderator"])
+ end
+
+ if admin? do
+ run(["set", nickname, "--admin"])
+ end
+
+ if generated_password? do
+ run(["reset_password", nickname])
+ end
+ else
+ Mix.shell().info("User will not be created.")
+ end
+ end
+
+ def run(["rm", nickname]) do
+ Common.start_pleroma()
+
+ with %User{local: true} = user <- User.get_by_nickname(nickname) do
+ User.delete(user)
+ Mix.shell().info("User #{nickname} deleted.")
+ else
+ _ ->
+ Mix.shell().error("No local user #{nickname}")
+ end
+ end
+
+ def run(["toggle_activated", nickname]) 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"]}")
+ else
+ _ ->
+ Mix.shell().error("No user #{nickname}")
+ end
+ end
+
+ def run(["reset_password", nickname]) do
+ Common.start_pleroma()
+
+ with %User{local: true} = user <- User.get_by_nickname(nickname),
+ {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
+ Mix.shell().info("Generated password reset token for #{user.nickname}")
+
+ IO.puts(
+ "URL: #{
+ Pleroma.Web.Router.Helpers.util_url(
+ Pleroma.Web.Endpoint,
+ :show_password_reset,
+ token.token
+ )
+ }"
+ )
+ else
+ _ ->
+ Mix.shell().error("No local user #{nickname}")
+ end
+ end
+
+ def run(["unsubscribe", nickname]) do
+ Common.start_pleroma()
+
+ with %User{} = user <- User.get_by_nickname(nickname) do
+ Mix.shell().info("Deactivating #{user.nickname}")
+ User.deactivate(user)
+
+ {:ok, friends} = User.get_friends(user)
+
+ Enum.each(friends, fn friend ->
+ user = Repo.get(User, user.id)
+
+ Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
+ User.unfollow(user, friend)
+ end)
+
+ :timer.sleep(500)
+
+ user = Repo.get(User, user.id)
+
+ if length(user.following) == 0 do
+ Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
+ end
+ else
+ _ ->
+ Mix.shell().error("No user #{nickname}")
+ end
+ end
+
+ def run(["set", nickname | rest]) do
+ Common.start_pleroma()
+
+ {options, [], []} =
+ OptionParser.parse(
+ rest,
+ strict: [
+ moderator: :boolean,
+ admin: :boolean,
+ locked: :boolean
+ ]
+ )
+
+ 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
+
+ case Keyword.get(options, :locked) do
+ nil -> nil
+ value -> set_locked(user, value)
+ end
+
+ case Keyword.get(options, :admin) do
+ nil -> nil
+ value -> set_admin(user, value)
+ end
+ else
+ _ ->
+ Mix.shell().error("No local user #{nickname}")
+ end
+ 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
+ end
+
+ defp set_moderator(user, value) do
+ info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
+
+ user_cng =
+ Ecto.Changeset.change(user)
+ |> put_embed(:info, info_cng)
+
+ {:ok, user} = User.update_and_set_cache(user_cng)
+
+ Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
+ end
+
+ defp set_admin(user, value) do
+ info_cng = User.Info.admin_api_update(user.info, %{is_admin: value})
+
+ user_cng =
+ Ecto.Changeset.change(user)
+ |> put_embed(:info, info_cng)
+
+ {:ok, user} = User.update_and_set_cache(user_cng)
+
+ Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_moderator}")
+ end
+
+ defp set_locked(user, value) do
+ info_cng = User.Info.user_upgrade(user.info, %{locked: value})
+
+ user_cng =
+ Ecto.Changeset.change(user)
+ |> put_embed(:info, info_cng)
+
+ {:ok, user} = User.update_and_set_cache(user_cng)
+
+ Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}")
+ end
+end
diff --git a/lib/mix/tasks/reactivate_user.ex b/lib/mix/tasks/reactivate_user.ex
deleted file mode 100644
index a30d3ac8b..000000000
--- a/lib/mix/tasks/reactivate_user.ex
+++ /dev/null
@@ -1,19 +0,0 @@
-defmodule Mix.Tasks.ReactivateUser do
- use Mix.Task
- alias Pleroma.User
-
- @moduledoc """
- Reactivate a user
-
- Usage: ``mix reactivate_user <nickname>``
-
- Example: ``mix reactivate_user lain``
- """
- def run([nickname]) do
- Mix.Task.run("app.start")
-
- with user <- User.get_by_nickname(nickname) do
- User.deactivate(user, false)
- end
- end
-end
diff --git a/lib/mix/tasks/register_user.ex b/lib/mix/tasks/register_user.ex
deleted file mode 100644
index 1f5321093..000000000
--- a/lib/mix/tasks/register_user.ex
+++ /dev/null
@@ -1,30 +0,0 @@
-defmodule Mix.Tasks.RegisterUser do
- @moduledoc """
- Manually register a local user
-
- Usage: ``mix register_user <name> <nickname> <email> <bio> <password>``
-
- Example: ``mix register_user 仮面の告白 lain lain@example.org "blushy-crushy fediverse idol + pleroma dev" pleaseDontHeckLain``
- """
-
- use Mix.Task
- alias Pleroma.{Repo, User}
-
- @shortdoc "Register user"
- def run([name, nickname, email, bio, password]) do
- Mix.Task.run("app.start")
-
- params = %{
- name: name,
- nickname: nickname,
- email: email,
- password: password,
- password_confirmation: password,
- bio: bio
- }
-
- user = User.register_changeset(%User{}, params)
-
- Repo.insert!(user)
- end
-end
diff --git a/lib/mix/tasks/relay_follow.ex b/lib/mix/tasks/relay_follow.ex
deleted file mode 100644
index 85b1c024d..000000000
--- a/lib/mix/tasks/relay_follow.ex
+++ /dev/null
@@ -1,24 +0,0 @@
-defmodule Mix.Tasks.RelayFollow do
- use Mix.Task
- require Logger
- alias Pleroma.Web.ActivityPub.Relay
-
- @shortdoc "Follows a remote relay"
- @moduledoc """
- Follows a remote relay
-
- Usage: ``mix relay_follow <relay_url>``
-
- Example: ``mix relay_follow https://example.org/relay``
- """
- def run([target]) do
- Mix.Task.run("app.start")
-
- 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
- {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
- end
- end
-end
diff --git a/lib/mix/tasks/relay_unfollow.ex b/lib/mix/tasks/relay_unfollow.ex
deleted file mode 100644
index 237fb771c..000000000
--- a/lib/mix/tasks/relay_unfollow.ex
+++ /dev/null
@@ -1,23 +0,0 @@
-defmodule Mix.Tasks.RelayUnfollow do
- use Mix.Task
- require Logger
- alias Pleroma.Web.ActivityPub.Relay
-
- @moduledoc """
- Unfollows a remote relay
-
- Usage: ``mix relay_follow <relay_url>``
-
- Example: ``mix relay_follow https://example.org/relay``
- """
- def run([target]) do
- Mix.Task.run("app.start")
-
- 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
- {:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
- end
- end
-end
diff --git a/lib/mix/tasks/rm_user.ex b/lib/mix/tasks/rm_user.ex
deleted file mode 100644
index 50463046c..000000000
--- a/lib/mix/tasks/rm_user.ex
+++ /dev/null
@@ -1,19 +0,0 @@
-defmodule Mix.Tasks.RmUser do
- use Mix.Task
- alias Pleroma.User
-
- @moduledoc """
- Permanently deletes a user
-
- Usage: ``mix rm_user [nickname]``
-
- Example: ``mix rm_user lain``
- """
- def run([nickname]) do
- Mix.Task.run("app.start")
-
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
- {:ok, _} = User.delete(user)
- end
- end
-end
diff --git a/lib/mix/tasks/set_admin.ex b/lib/mix/tasks/set_admin.ex
deleted file mode 100644
index d5ccf261b..000000000
--- a/lib/mix/tasks/set_admin.ex
+++ /dev/null
@@ -1,32 +0,0 @@
-defmodule Mix.Tasks.SetAdmin do
- use Mix.Task
- alias Pleroma.User
-
- @doc """
- Sets admin status
- Usage: set_admin nickname [true|false]
- """
- def run([nickname | rest]) do
- Application.ensure_all_started(:pleroma)
-
- status =
- case rest do
- [status] -> status == "true"
- _ -> true
- end
-
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
- info =
- user.info
- |> Map.put("is_admin", !!status)
-
- cng = User.info_changeset(user, %{info: info})
- {:ok, user} = User.update_and_set_cache(cng)
-
- IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}")
- else
- _ ->
- IO.puts("No local user #{nickname}")
- end
- end
-end
diff --git a/lib/mix/tasks/set_locked.ex b/lib/mix/tasks/set_locked.ex
deleted file mode 100644
index a154595ca..000000000
--- a/lib/mix/tasks/set_locked.ex
+++ /dev/null
@@ -1,39 +0,0 @@
-defmodule Mix.Tasks.SetLocked do
- @moduledoc """
- Lock a local user
-
- The local user will then have to manually accept/reject followers. This can also be done by the user into their settings.
-
- Usage: ``mix set_locked <username>``
-
- Example: ``mix set_locked lain``
- """
-
- use Mix.Task
- import Mix.Ecto
- alias Pleroma.{Repo, User}
-
- def run([nickname | rest]) do
- ensure_started(Repo, [])
-
- locked =
- case rest do
- [locked] -> locked == "true"
- _ -> true
- end
-
- with %User{local: true} = user <- User.get_by_nickname(nickname) do
- info =
- user.info
- |> Map.put("locked", !!locked)
-
- cng = User.info_changeset(user, %{info: info})
- user = Repo.update!(cng)
-
- IO.puts("locked status of #{nickname}: #{user.info["locked"]}")
- else
- _ ->
- IO.puts("No local user #{nickname}")
- end
- end
-end
diff --git a/lib/mix/tasks/unsubscribe_user.ex b/lib/mix/tasks/unsubscribe_user.ex
deleted file mode 100644
index 62ea61a5c..000000000
--- a/lib/mix/tasks/unsubscribe_user.ex
+++ /dev/null
@@ -1,38 +0,0 @@
-defmodule Mix.Tasks.UnsubscribeUser do
- use Mix.Task
- alias Pleroma.{User, Repo}
- require Logger
-
- @moduledoc """
- Deactivate and Unsubscribe local users from a user
-
- Usage: ``mix unsubscribe_user <nickname>``
-
- Example: ``mix unsubscribe_user lain``
- """
- def run([nickname]) do
- Mix.Task.run("app.start")
-
- with %User{} = user <- User.get_by_nickname(nickname) do
- Logger.info("Deactivating #{user.nickname}")
- User.deactivate(user)
-
- {:ok, friends} = User.get_friends(user)
-
- Enum.each(friends, fn friend ->
- user = Repo.get(User, user.id)
-
- Logger.info("Unsubscribing #{friend.nickname} from #{user.nickname}")
- User.unfollow(user, friend)
- end)
-
- :timer.sleep(500)
-
- user = Repo.get(User, user.id)
-
- if length(user.following) == 0 do
- Logger.info("Successfully unsubscribed all followers from #{user.nickname}")
- end
- end
- end
-end
diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex
index cc68d9669..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
@@ -66,7 +65,8 @@ defmodule Pleroma.Application do
),
worker(Pleroma.Web.Federator.RetryQueue, []),
worker(Pleroma.Web.Federator, []),
- worker(Pleroma.Stats, [])
+ worker(Pleroma.Stats, []),
+ worker(Pleroma.Web.Push, [])
] ++
streamer_child() ++
chat_child() ++
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 1a5c07c8a..5b03e9aeb 100644
--- a/lib/pleroma/formatter.ex
+++ b/lib/pleroma/formatter.ex
@@ -114,7 +114,7 @@ defmodule Pleroma.Formatter do
subs =
subs ++
- Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} ->
+ Enum.map(mentions, fn {match, %User{id: id, ap_id: ap_id, info: info}, uuid} ->
ap_id =
if is_binary(info.source_data["url"]) do
info.source_data["url"]
@@ -125,7 +125,7 @@ defmodule Pleroma.Formatter do
short_match = String.split(match, "@") |> tl() |> hd()
{uuid,
- "<span><a class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
+ "<span><a data-user='#{id}' class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
end)
{subs, uuid_text}
@@ -147,7 +147,11 @@ defmodule Pleroma.Formatter do
subs =
subs ++
Enum.map(tags, fn {tag_text, tag, uuid} ->
- url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag_text}</a>"
+ url =
+ "<a data-tag='#{tag}' href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{
+ tag_text
+ }</a>"
+
{uuid, url}
end)
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 1b920d7fd..8a0333461 100644
--- a/lib/pleroma/html.ex
+++ b/lib/pleroma/html.ex
@@ -45,7 +45,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
Meta.strip_comments()
# links
- Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
+ Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
# paragraphs and linebreaks
@@ -86,7 +86,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.remove_cdata_sections_before_scrub()
Meta.strip_comments()
- Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
+ Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
Meta.allow_tag_with_these_attributes("abbr", ["title"])
@@ -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
new file mode 100644
index 000000000..7b11060b2
--- /dev/null
+++ b/lib/pleroma/http/connection.ex
@@ -0,0 +1,32 @@
+defmodule Pleroma.HTTP.Connection do
+ @moduledoc """
+ Connection for http-requests.
+ """
+
+ @hackney_options [
+ pool: :default,
+ timeout: 10000,
+ recv_timeout: 20000,
+ follow_redirect: true
+ ]
+ @adapter Application.get_env(:tesla, :adapter)
+
+ @doc """
+ Configure a client connection
+
+ # Returns
+
+ Tesla.Env.client
+ """
+ @spec new(Keyword.t()) :: Tesla.Env.client()
+ def new(opts \\ []) do
+ Tesla.client([], {@adapter, hackney_options(opts)})
+ end
+
+ # fetch Hackney options
+ #
+ defp hackney_options(opts) do
+ options = Keyword.get(opts, :adapter, [])
+ @hackney_options ++ options
+ end
+end
diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http/http.ex
index e64266ae7..3c0256575 100644
--- a/lib/pleroma/http/http.ex
+++ b/lib/pleroma/http/http.ex
@@ -1,14 +1,42 @@
defmodule Pleroma.HTTP do
- require HTTPoison
+ @moduledoc """
+ """
+
+ alias Pleroma.HTTP.Connection
+ alias Pleroma.HTTP.RequestBuilder, as: Builder
+
+ @doc """
+ Builds and perform http request.
+
+ # Arguments:
+ `method` - :get, :post, :put, :delete
+ `url`
+ `body`
+ `headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
+ `options` - custom, per-request middleware or adapter options
+
+ # Returns:
+ `{:ok, %Tesla.Env{}}` or `{:error, error}`
+
+ """
def request(method, url, body \\ "", headers \\ [], options \\ []) do
options =
process_request_options(options)
|> process_sni_options(url)
- HTTPoison.request(method, url, body, headers, options)
+ %{}
+ |> Builder.method(method)
+ |> Builder.headers(headers)
+ |> Builder.opts(options)
+ |> Builder.url(url)
+ |> Builder.add_param(:body, :body, body)
+ |> Enum.into([])
+ |> (&Tesla.request(Connection.new(), &1)).()
end
+ defp process_sni_options(options, nil), do: options
+
defp process_sni_options(options, url) do
uri = URI.parse(url)
host = uri.host |> to_charlist()
@@ -22,7 +50,7 @@ defmodule Pleroma.HTTP do
def process_request_options(options) do
config = Application.get_env(:pleroma, :http, [])
proxy = Keyword.get(config, :proxy_url, nil)
- options = options ++ [hackney: [pool: :default]]
+ options = options ++ [adapter: [pool: :default]]
case proxy do
nil -> options
@@ -30,8 +58,19 @@ defmodule Pleroma.HTTP do
end
end
- def get(url, headers \\ [], options \\ []), do: request(:get, url, "", headers, options)
+ @doc """
+ Performs GET request.
+
+ See `Pleroma.HTTP.request/5`
+ """
+ def get(url, headers \\ [], options \\ []),
+ do: request(:get, url, "", headers, options)
+
+ @doc """
+ Performs POST request.
+ See `Pleroma.HTTP.request/5`
+ """
def post(url, body, headers \\ [], options \\ []),
do: request(:post, url, body, headers, options)
end
diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex
new file mode 100644
index 000000000..5aee2b8ae
--- /dev/null
+++ b/lib/pleroma/http/request_builder.ex
@@ -0,0 +1,126 @@
+defmodule Pleroma.HTTP.RequestBuilder do
+ @moduledoc """
+ Helper functions for building Tesla requests
+ """
+
+ @doc """
+ Specify the request method when building a request
+
+ ## Parameters
+
+ - request (Map) - Collected request options
+ - m (atom) - Request method
+
+ ## Returns
+
+ Map
+ """
+ @spec method(map(), atom) :: map()
+ def method(request, m) do
+ Map.put_new(request, :method, m)
+ end
+
+ @doc """
+ Specify the request method when building a request
+
+ ## Parameters
+
+ - request (Map) - Collected request options
+ - u (String) - Request URL
+
+ ## Returns
+
+ Map
+ """
+ @spec url(map(), String.t()) :: map()
+ def url(request, u) do
+ Map.put_new(request, :url, u)
+ end
+
+ @doc """
+ Add headers to the request
+ """
+ @spec headers(map(), list(tuple)) :: map()
+ def headers(request, h) do
+ Map.put_new(request, :headers, h)
+ end
+
+ @doc """
+ Add custom, per-request middleware or adapter options to the request
+ """
+ @spec opts(map(), Keyword.t()) :: map()
+ def opts(request, options) do
+ Map.put_new(request, :opts, options)
+ end
+
+ @doc """
+ Add optional parameters to the request
+
+ ## Parameters
+
+ - request (Map) - Collected request options
+ - definitions (Map) - Map of parameter name to parameter location.
+ - options (KeywordList) - The provided optional parameters
+
+ ## Returns
+
+ Map
+ """
+ @spec add_optional_params(map(), %{optional(atom) => atom}, keyword()) :: map()
+ def add_optional_params(request, _, []), do: request
+
+ def add_optional_params(request, definitions, [{key, value} | tail]) do
+ case definitions do
+ %{^key => location} ->
+ request
+ |> add_param(location, key, value)
+ |> add_optional_params(definitions, tail)
+
+ _ ->
+ add_optional_params(request, definitions, tail)
+ end
+ end
+
+ @doc """
+ Add optional parameters to the request
+
+ ## Parameters
+
+ - request (Map) - Collected request options
+ - location (atom) - Where to put the parameter
+ - key (atom) - The name of the parameter
+ - value (any) - The value of the parameter
+
+ ## Returns
+
+ Map
+ """
+ @spec add_param(map(), atom, atom, any()) :: map()
+ def add_param(request, :body, :body, value), do: Map.put(request, :body, value)
+
+ def add_param(request, :body, key, value) do
+ request
+ |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
+ |> Map.update!(
+ :body,
+ &Tesla.Multipart.add_field(&1, key, Poison.encode!(value),
+ headers: [{:"Content-Type", "application/json"}]
+ )
+ )
+ end
+
+ def add_param(request, :file, name, path) do
+ request
+ |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
+ |> Map.update!(:body, &Tesla.Multipart.add_file(&1, path, name: name))
+ end
+
+ def add_param(request, :form, name, value) do
+ request
+ |> Map.update(:body, %{name => value}, &Map.put(&1, name, value))
+ end
+
+ def add_param(request, location, key, value) do
+ Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}]))
+ end
+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 99063ded6..2cb3d8bd1 100644
--- a/lib/pleroma/mime.ex
+++ b/lib/pleroma/mime.ex
@@ -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 a3aeb1221..47f6b6ee7 100644
--- a/lib/pleroma/notification.ex
+++ b/lib/pleroma/notification.ex
@@ -110,6 +110,7 @@ defmodule Pleroma.Notification do
notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification)
Pleroma.Web.Streamer.stream("user", notification)
+ Pleroma.Web.Push.send(notification)
notification
end
end
@@ -117,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
@@ -130,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/object.ex b/lib/pleroma/object.ex
index 03a75dfbd..31c8dd5bd 100644
--- a/lib/pleroma/object.ex
+++ b/lib/pleroma/object.ex
@@ -1,6 +1,6 @@
defmodule Pleroma.Object do
use Ecto.Schema
- alias Pleroma.{Repo, Object, Activity}
+ alias Pleroma.{Repo, Object, User, Activity}
import Ecto.{Query, Changeset}
schema "objects" do
@@ -31,6 +31,13 @@ defmodule Pleroma.Object do
def normalize(ap_id) when is_binary(ap_id), do: Object.get_by_ap_id(ap_id)
def normalize(_), do: nil
+ # Owned objects can only be mutated by their owner
+ def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
+ do: actor == ap_id
+
+ # Legacy objects can be mutated by anybody
+ def authorize_mutation(%Object{}, %User{}), do: true
+
if Mix.env() == :test do
def get_cached_by_ap_id(ap_id) do
get_by_ap_id(ap_id)
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/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex
index 630f15eec..8b99a74d1 100644
--- a/lib/pleroma/plugs/oauth_plug.ex
+++ b/lib/pleroma/plugs/oauth_plug.ex
@@ -1,30 +1,70 @@
defmodule Pleroma.Plugs.OAuthPlug do
import Plug.Conn
- alias Pleroma.User
- alias Pleroma.Repo
- alias Pleroma.Web.OAuth.Token
+ import Ecto.Query
- def init(options) do
- options
- end
+ alias Pleroma.{
+ User,
+ Repo,
+ Web.OAuth.Token
+ }
+
+ @realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
+
+ def init(options), do: options
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
def call(conn, _) do
- token =
- case get_req_header(conn, "authorization") do
- ["Bearer " <> header] -> header
- _ -> get_session(conn, :oauth_token)
- end
-
- with token when not is_nil(token) <- token,
- %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
- %User{} = user <- Repo.get(User, user_id),
- false <- !!user.info.deactivated do
+ with {:ok, token} <- fetch_token(conn),
+ {:ok, user} <- fetch_user(token) do
conn
+ |> assign(:token, token)
|> assign(:user, user)
else
_ -> conn
end
end
+
+ # Gets user by token
+ #
+ @spec fetch_user(String.t()) :: {:ok, User.t()} | nil
+ defp fetch_user(token) do
+ query = from(q in Token, where: q.token == ^token, preload: [:user])
+
+ with %Token{user: %{info: %{deactivated: false} = _} = user} <- Repo.one(query) do
+ {:ok, user}
+ end
+ end
+
+ # Gets token from session by :oauth_token key
+ #
+ @spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
+ defp fetch_token_from_session(conn) do
+ case get_session(conn, :oauth_token) do
+ nil -> :no_token_found
+ token -> {:ok, token}
+ end
+ end
+
+ # Gets token from headers
+ #
+ @spec fetch_token(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
+ defp fetch_token(%Plug.Conn{} = conn) do
+ headers = get_req_header(conn, "authorization")
+
+ with :no_token_found <- fetch_token(headers),
+ do: fetch_token_from_session(conn)
+ end
+
+ @spec fetch_token(Keyword.t()) :: :no_token_found | {:ok, String.t()}
+ defp fetch_token([]), do: :no_token_found
+
+ defp fetch_token([token | tail]) do
+ trimmed_token = String.trim(token)
+
+ case Regex.run(@realm_reg, trimmed_token) do
+ [_, match] -> {:ok, String.trim(match)}
+ _ -> fetch_token(tail)
+ end
+ end
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 ad9dc82fe..7f328d00d 100644
--- a/lib/pleroma/reverse_proxy.ex
+++ b/lib/pleroma/reverse_proxy.ex
@@ -56,7 +56,7 @@ defmodule Pleroma.ReverseProxy do
@hackney Application.get_env(:pleroma, :hackney, :hackney)
@httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
- @default_hackney_options [{:follow_redirect, true}]
+ @default_hackney_options []
@inline_content_types [
"image/gif",
@@ -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 35d36d3e4..f06755056 100644
--- a/lib/pleroma/uploaders/mdii.ex
+++ b/lib/pleroma/uploaders/mdii.ex
@@ -12,15 +12,15 @@ 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)
extension = String.split(upload.name, ".") |> List.last()
query = "#{cgi}?#{extension}"
- with {:ok, %{status_code: 200, body: body}} <- @httpoison.post(query, file_data) do
+ with {:ok, %{status: 200, body: body}} <- @httpoison.post(query, file_data) do
remote_file_name = String.split(body) |> List.first()
public_url = "#{files}/#{remote_file_name}.#{extension}"
{:ok, {:url, public_url}}
diff --git a/lib/pleroma/uploaders/swift/keystone.ex b/lib/pleroma/uploaders/swift/keystone.ex
index e578b3c61..4aed977b1 100644
--- a/lib/pleroma/uploaders/swift/keystone.ex
+++ b/lib/pleroma/uploaders/swift/keystone.ex
@@ -25,10 +25,10 @@ defmodule Pleroma.Uploaders.Swift.Keystone do
["Content-Type": "application/json"],
hackney: [:insecure]
) do
- {:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
+ {:ok, %Tesla.Env{status: 200, body: body}} ->
body["access"]["token"]["id"]
- {:ok, %HTTPoison.Response{status_code: _}} ->
+ {:ok, %Tesla.Env{status: _}} ->
""
end
end
diff --git a/lib/pleroma/uploaders/swift/swift.ex b/lib/pleroma/uploaders/swift/swift.ex
index 1e865f101..d4e758bbb 100644
--- a/lib/pleroma/uploaders/swift/swift.ex
+++ b/lib/pleroma/uploaders/swift/swift.ex
@@ -9,14 +9,13 @@ 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
- {:ok, %HTTPoison.Response{status_code: 201}} ->
+ {:ok, %Tesla.Env{status: 201}} ->
{:ok, {:file, filename}}
- {:ok, %HTTPoison.Response{status_code: 401}} ->
+ {:ok, %Tesla.Env{status: 401}} ->
{:error, "Unauthorized, Bad Token"}
{:error, _} ->
diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex
index 76712b4bf..6a267ee58 100644
--- a/lib/pleroma/user.ex
+++ b/lib/pleroma/user.ex
@@ -4,9 +4,13 @@ defmodule Pleroma.User do
import Ecto.{Changeset, Query}
alias Pleroma.{Repo, User, Object, Web, Activity, Notification}
alias Comeonin.Pbkdf2
+ alias Pleroma.Formatter
+ alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
alias Pleroma.Web.{OStatus, Websub, OAuth}
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
+ @type t :: %__MODULE__{}
+
schema "users" do
field(:bio, :string)
field(:email, :string)
@@ -21,6 +25,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)
@@ -60,10 +65,6 @@ defmodule Pleroma.User do
|> validate_required([:following])
end
- def info_changeset(struct, params \\ %{}) do
- raise "NOT VALID ANYMORE"
- end
-
def user_info(%User{} = user) do
oneself = if user.local, do: 1, else: 0
@@ -175,6 +176,7 @@ defmodule Pleroma.User do
|> validate_format(:email, @email_regex)
|> validate_length(:bio, max: 1000)
|> validate_length(:name, min: 1, max: 100)
+ |> put_change(:info, %Pleroma.User.Info{})
if changeset.valid? do
hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
@@ -217,7 +219,7 @@ defmodule Pleroma.User do
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
@@ -279,6 +281,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
@@ -802,4 +805,56 @@ defmodule Pleroma.User do
:error
end
end
+
+ def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do
+ mentions = Formatter.parse_mentions(bio)
+ tags = Formatter.parse_tags(bio)
+
+ emoji =
+ (user.info.source_data["tag"] || [])
+ |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
+ |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
+ {String.trim(name, ":"), url}
+ end)
+
+ CommonUtils.format_input(bio, 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
end
diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex
index 94d403bf7..d81b45b8d 100644
--- a/lib/pleroma/user/info.ex
+++ b/lib/pleroma/user/info.ex
@@ -4,7 +4,7 @@ defmodule Pleroma.User.Info do
embedded_schema do
field(:banner, :map, default: %{})
- field(:background, :string, default: nil)
+ field(:background, :map, default: %{})
field(:source_data, :map, default: %{})
field(:note_count, :integer, default: 0)
field(:follower_count, :integer, default: 0)
@@ -24,6 +24,7 @@ defmodule Pleroma.User.Info do
field(:topic, :string, default: nil)
field(:hub, :string, default: nil)
field(:salmon, :string, default: nil)
+ field(:hide_network, :boolean, default: false)
# Found in the wild
# ap_id -> Where is this used?
@@ -134,7 +135,9 @@ defmodule Pleroma.User.Info do
:locked,
:no_rich_text,
:default_scope,
- :banner
+ :banner,
+ :hide_network,
+ :background
])
end
@@ -146,6 +149,11 @@ defmodule Pleroma.User.Info do
])
end
+ def mastodon_settings_update(info, params) do
+ info
+ |> cast(params, [:settings])
+ end
+
def set_source_data(info, source_data) do
params = %{source_data: source_data}
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 7e207c620..31455343c 100644
--- a/lib/pleroma/web/activity_pub/activity_pub.ex
+++ b/lib/pleroma/web/activity_pub/activity_pub.ex
@@ -574,7 +574,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def upload(file, opts \\ []) do
with {:ok, data} <- Upload.store(file, opts) do
- Repo.insert(%Object{data: data})
+ obj_data =
+ if opts[:actor] do
+ Map.put(data, "actor", opts[:actor])
+ else
+ data
+ end
+
+ Repo.insert(%Object{data: obj_data})
end
end
@@ -762,13 +769,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Logger.info("Fetching #{id} via AP")
with true <- String.starts_with?(id, "http"),
- {:ok, %{body: body, status_code: code}} when code in 200..299 <-
+ {:ok, %{body: body, status: code}} when code in 200..299 <-
@httpoison.get(
id,
- [Accept: "application/activity+json"],
- follow_redirect: true,
- timeout: 10000,
- recv_timeout: 20000
+ [{:Accept, "application/activity+json"}]
),
{:ok, data} <- Jason.decode(body),
:ok <- Transmogrifier.contain_origin_from_id(id, data) do
@@ -795,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
new file mode 100644
index 000000000..6fa48454a
--- /dev/null
+++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex
@@ -0,0 +1,40 @@
+defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
+ alias Pleroma.Object
+
+ @behaviour Pleroma.Web.ActivityPub.MRF
+
+ @reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
+ def filter_by_summary(
+ %{"summary" => parent_summary} = _parent,
+ %{"summary" => child_summary} = child
+ )
+ when not is_nil(child_summary) and byte_size(child_summary) > 0 and
+ not is_nil(parent_summary) and byte_size(parent_summary) > 0 do
+ if (child_summary == parent_summary and not Regex.match?(@reply_prefix, child_summary)) or
+ (Regex.match?(@reply_prefix, parent_summary) &&
+ Regex.replace(@reply_prefix, parent_summary, "") == child_summary) do
+ Map.put(child, "summary", "re: " <> child_summary)
+ else
+ child
+ end
+ end
+
+ def filter_by_summary(_parent, child), do: child
+
+ def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
+ child = object["object"]
+ in_reply_to = Object.normalize(child["inReplyTo"])
+
+ child =
+ if(in_reply_to,
+ do: filter_by_summary(in_reply_to.data, child),
+ else: child
+ )
+
+ object = Map.put(object, "object", child)
+
+ {:ok, object}
+ end
+
+ def filter(object), do: {:ok, object}
+end
diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
index 86dcf5080..12fc3b181 100644
--- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
+++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex
@@ -23,7 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
defp check_media_removal(
%{host: actor_host} = _actor_info,
- %{"type" => "Create", "object" => %{"attachement" => child_attachment}} = object
+ %{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object
)
when length(child_attachment) > 0 do
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/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex
index aaa777602..869934172 100644
--- a/lib/pleroma/web/activity_pub/views/user_view.ex
+++ b/lib/pleroma/web/activity_pub/views/user_view.ex
@@ -82,7 +82,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
query = from(user in query, select: [:ap_id])
following = Repo.all(query)
- collection(following, "#{user.ap_id}/following", page)
+ collection(following, "#{user.ap_id}/following", page, !user.info.hide_network)
|> Map.merge(Utils.make_json_ld_header())
end
@@ -95,7 +95,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"id" => "#{user.ap_id}/following",
"type" => "OrderedCollection",
"totalItems" => length(following),
- "first" => collection(following, "#{user.ap_id}/following", 1)
+ "first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_network)
}
|> Map.merge(Utils.make_json_ld_header())
end
@@ -105,7 +105,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
query = from(user in query, select: [:ap_id])
followers = Repo.all(query)
- collection(followers, "#{user.ap_id}/followers", page)
+ collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_network)
|> Map.merge(Utils.make_json_ld_header())
end
@@ -118,7 +118,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"id" => "#{user.ap_id}/followers",
"type" => "OrderedCollection",
"totalItems" => length(followers),
- "first" => collection(followers, "#{user.ap_id}/followers", 1)
+ "first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_network)
}
|> Map.merge(Utils.make_json_ld_header())
end
@@ -172,7 +172,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end
end
- def collection(collection, iri, page, total \\ nil) do
+ def collection(collection, iri, page, show_items \\ true, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
@@ -183,7 +183,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => total,
- "orderedItems" => items
+ "orderedItems" => if(show_items, do: items, else: [])
}
if offset < total do
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/utils.ex b/lib/pleroma/web/common_api/utils.ex
index 728f24c7e..8f2625cef 100644
--- a/lib/pleroma/web/common_api/utils.ex
+++ b/lib/pleroma/web/common_api/utils.ex
@@ -122,7 +122,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>")
@@ -236,7 +236,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 06c094f26..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
@@ -17,7 +12,15 @@ defmodule Pleroma.Web.Federator.RetryQueue do
end
def start_link() do
- GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__)
+ enabled = Pleroma.Config.get([:retry_queue, :enabled], false)
+
+ if enabled do
+ Logger.info("Starting retry queue")
+ GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__)
+ else
+ Logger.info("Retry queue disabled")
+ :ignore
+ end
end
def enqueue(data, transport, retries \\ 0) do
@@ -38,7 +41,7 @@ defmodule Pleroma.Web.Federator.RetryQueue do
Process.send_after(
__MODULE__,
{:send, data, transport, retries},
- growth_function(retries)
+ timeout
)
{:noreply, state}
@@ -54,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 d19d55044..5c8602322 100644
--- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
+++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex
@@ -2,13 +2,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
use Pleroma.Web, :controller
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
alias Pleroma.Web
- alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView}
+
+ alias Pleroma.Web.MastodonAPI.{
+ StatusView,
+ AccountView,
+ MastodonView,
+ ListView,
+ FilterView,
+ PushSubscriptionView
+ }
+
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth.{Authorization, Token, App}
alias Pleroma.Web.MediaProxy
- alias Comeonin.Pbkdf2
+
import Ecto.Query
require Logger
@@ -55,7 +64,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
user_params =
%{}
|> add_if_present(params, "display_name", :name)
- |> add_if_present(params, "note", :bio)
+ |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value)} end)
|> add_if_present(params, "avatar", :avatar, fn value ->
with %Plug.Upload{} <- value,
{:ok, object} <- ActivityPub.upload(value, type: :avatar) do
@@ -428,38 +437,33 @@ 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: _}} = conn, data) do
+ def update_media(%{assigns: %{user: user}} = conn, data) do
with %Object{} = object <- Repo.get(Object, data["id"]),
+ true <- Object.authorize_mutation(object, user),
true <- is_binary(data["description"]),
description <- data["description"] do
new_data = %{object.data | "name" => description}
- change = Object.change(object, %{data: new_data})
- {:ok, _} = Repo.update(change)
-
- data =
- new_data
- |> Map.put("id", object.id)
+ {:ok, _} =
+ object
+ |> Object.change(%{data: new_data})
+ |> Repo.update()
- render(conn, StatusView, "attachment.json", %{attachment: data})
+ attachment_data = Map.put(new_data, "id", object.id)
+ render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
end
end
- def upload(%{assigns: %{user: _}} = conn, %{"file" => file} = data) do
- with {:ok, object} <- ActivityPub.upload(file, description: Map.get(data, "description")) do
- change = Object.change(object, %{data: object.data})
- {:ok, object} = Repo.update(change)
-
- objdata =
- object.data
- |> Map.put("id", object.id)
-
- render(conn, StatusView, "attachment.json", %{attachment: objdata})
+ def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
+ with {:ok, object} <-
+ ActivityPub.upload(file,
+ actor: User.ap_id(user),
+ description: Map.get(data, "description")
+ ) do
+ attachment_data = Map.put(object.data, "id", object.id)
+ render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
end
end
@@ -502,17 +506,30 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
end
- # TODO: Pagination
- def followers(conn, %{"id" => id}) do
+ def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
with %User{} = user <- Repo.get(User, id),
{:ok, followers} <- User.get_followers(user) do
+ followers =
+ cond do
+ for_user && user.id == for_user.id -> followers
+ user.info.hide_network -> []
+ true -> followers
+ end
+
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
end
end
- def following(conn, %{"id" => id}) do
+ def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
with %User{} = user <- Repo.get(User, id),
{:ok, followers} <- User.get_friends(user) do
+ followers =
+ cond do
+ for_user && user.id == for_user.id -> followers
+ user.info.hide_network -> []
+ true -> followers
+ end
+
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
end
end
@@ -830,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")
@@ -959,15 +976,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
- with new_info <- Map.put(user.info, "settings", settings),
- change <- User.info_changeset(user, %{info: new_info}),
- {:ok, _user} <- User.update_and_set_cache(change) do
- conn
- |> json(%{})
+ info_cng = User.Info.mastodon_settings_update(user.info, settings)
+
+ with changeset <- User.update_changeset(user),
+ changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
+ {: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
@@ -1149,6 +1166,33 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
json(conn, %{})
end
+ def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
+ Pleroma.Web.Push.Subscription.delete_if_exists(user, token)
+ {:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
+ subscription = Pleroma.Web.Push.Subscription.get(user, token)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def update_push_subscription(
+ %{assigns: %{user: user, token: token}} = conn,
+ params
+ ) do
+ {:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params)
+ view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
+ json(conn, view)
+ end
+
+ def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
+ {:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token)
+ json(conn, %{})
+ end
+
def errors(conn, _) do
conn
|> put_status(500)
@@ -1168,8 +1212,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
user = user.nickname
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
- with {:ok, %{status_code: 200, body: body}} <-
- @httpoison.get(url, [], timeout: timeout, recv_timeout: timeout),
+ with {:ok, %{status: 200, body: body}} <-
+ @httpoison.get(
+ url,
+ [],
+ adapter: [
+ timeout: timeout,
+ recv_timeout: timeout
+ ]
+ ),
{:ok, data} <- Jason.decode(body) do
data2 =
Enum.slice(data, 0, limit)
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
new file mode 100644
index 000000000..c8b95d14c
--- /dev/null
+++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex
@@ -0,0 +1,11 @@
+defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
+ use Pleroma.Web, :view
+
+ def render("push_subscription.json", %{subscription: subscription}) do
+ %{
+ id: to_string(subscription.id),
+ endpoint: subscription.endpoint,
+ alerts: Map.get(subscription.data, "alerts")
+ }
+ end
+end
diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex
index 81ea5d510..63140feb9 100644
--- a/lib/pleroma/web/media_proxy/controller.ex
+++ b/lib/pleroma/web/media_proxy/controller.ex
@@ -2,15 +2,14 @@ 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]),
+ 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_length))
+ ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts))
else
false ->
send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404))
@@ -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 6a27f1730..c6440c20e 100644
--- a/lib/pleroma/web/ostatus/ostatus.ex
+++ b/lib/pleroma/web/ostatus/ostatus.ex
@@ -346,13 +346,10 @@ defmodule Pleroma.Web.OStatus do
def fetch_activity_from_atom_url(url) do
with true <- String.starts_with?(url, "http"),
- {:ok, %{body: body, status_code: code}} when code in 200..299 <-
+ {:ok, %{body: body, status: code}} when code in 200..299 <-
@httpoison.get(
url,
- [Accept: "application/atom+xml"],
- follow_redirect: true,
- timeout: 10000,
- recv_timeout: 20000
+ [{:Accept, "application/atom+xml"}]
) do
Logger.debug("Got document from #{url}, handling...")
handle_incoming(body)
@@ -367,8 +364,7 @@ defmodule Pleroma.Web.OStatus do
Logger.debug("Trying to fetch #{url}")
with true <- String.starts_with?(url, "http"),
- {:ok, %{body: body}} <-
- @httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000),
+ {:ok, %{body: body}} <- @httpoison.get(url, []),
{:ok, atom_url} <- get_atom_url(body) do
fetch_activity_from_atom_url(atom_url)
else
@@ -379,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/push/push.ex b/lib/pleroma/web/push/push.ex
new file mode 100644
index 000000000..5a873ec19
--- /dev/null
+++ b/lib/pleroma/web/push/push.ex
@@ -0,0 +1,116 @@
+defmodule Pleroma.Web.Push do
+ use GenServer
+
+ alias Pleroma.{Repo, User}
+ alias Pleroma.Web.Push.Subscription
+
+ require Logger
+ import Ecto.Query
+
+ @types ["Create", "Follow", "Announce", "Like"]
+
+ @gcm_api_key nil
+
+ def start_link() do
+ GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
+ end
+
+ def init(:ok) do
+ case Application.get_env(:web_push_encryption, :vapid_details) do
+ nil ->
+ Logger.warn(
+ "VAPID key pair is not found. Please, add VAPID configuration to config. Run `mix web_push.gen.keypair` mix task to create a key pair"
+ )
+
+ :ignore
+
+ _ ->
+ {:ok, %{}}
+ end
+ end
+
+ def send(notification) do
+ if Application.get_env(:web_push_encryption, :vapid_details) do
+ GenServer.cast(Pleroma.Web.Push, {:send, notification})
+ end
+ end
+
+ def handle_cast(
+ {:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
+ state
+ )
+ when type in @types do
+ actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
+ body = notification |> format(actor) |> Jason.encode!()
+
+ Subscription
+ |> where(user_id: ^user_id)
+ |> Repo.all()
+ |> Enum.each(fn record ->
+ subscription = %{
+ keys: %{
+ p256dh: record.key_p256dh,
+ auth: record.key_auth
+ },
+ endpoint: record.endpoint
+ }
+
+ case WebPushEncryption.send_web_push(body, subscription, @gcm_api_key) do
+ {:ok, %{status_code: code}} when 400 <= code and code < 500 ->
+ Logger.debug("Removing subscription record")
+ Repo.delete!(record)
+ :ok
+
+ {:ok, %{status_code: code}} when 200 <= code and code < 300 ->
+ :ok
+
+ {:ok, %{status_code: code}} ->
+ Logger.error("Web Push Nonification failed with code: #{code}")
+ :error
+
+ _ ->
+ Logger.error("Web Push Nonification failed with unknown error")
+ :error
+ end
+ end)
+
+ {:noreply, state}
+ end
+
+ def handle_cast({:send, _}, state) do
+ Logger.warn("Unknown notification type")
+ {:noreply, state}
+ end
+
+ def format(%{activity: %{data: %{"type" => "Create"}}}, actor) do
+ %{
+ title: "New Mention",
+ body: "@#{actor.nickname} has mentiond you",
+ icon: User.avatar_url(actor)
+ }
+ end
+
+ def format(%{activity: %{data: %{"type" => "Follow"}}}, actor) do
+ %{
+ title: "New Follower",
+ body: "@#{actor.nickname} has followed you",
+ icon: User.avatar_url(actor)
+ }
+ end
+
+ def format(%{activity: %{data: %{"type" => "Announce"}}}, actor) do
+ %{
+ title: "New Announce",
+ body: "@#{actor.nickname} has announced your post",
+ icon: User.avatar_url(actor)
+ }
+ end
+
+ def format(%{activity: %{data: %{"type" => "Like"}}}, actor) do
+ %{
+ title: "New Like",
+ body: "@#{actor.nickname} has liked your post",
+ icon: User.avatar_url(actor)
+ }
+ end
+end
diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex
new file mode 100644
index 000000000..cfab7a98e
--- /dev/null
+++ b/lib/pleroma/web/push/subscription.ex
@@ -0,0 +1,66 @@
+defmodule Pleroma.Web.Push.Subscription do
+ use Ecto.Schema
+ import Ecto.Changeset
+ alias Pleroma.{Repo, User}
+ alias Pleroma.Web.OAuth.Token
+ alias Pleroma.Web.Push.Subscription
+
+ schema "push_subscriptions" do
+ belongs_to(:user, User)
+ belongs_to(:token, Token)
+ field(:endpoint, :string)
+ field(:key_p256dh, :string)
+ field(:key_auth, :string)
+ field(:data, :map, default: %{})
+
+ timestamps()
+ end
+
+ @supported_alert_types ~w[follow favourite mention reblog]
+
+ defp alerts(%{"data" => %{"alerts" => alerts}}) do
+ alerts = Map.take(alerts, @supported_alert_types)
+ %{"alerts" => alerts}
+ end
+
+ def create(
+ %User{} = user,
+ %Token{} = token,
+ %{
+ "subscription" => %{
+ "endpoint" => endpoint,
+ "keys" => %{"auth" => key_auth, "p256dh" => key_p256dh}
+ }
+ } = params
+ ) do
+ Repo.insert(%Subscription{
+ user_id: user.id,
+ token_id: token.id,
+ endpoint: endpoint,
+ key_auth: key_auth,
+ key_p256dh: key_p256dh,
+ data: alerts(params)
+ })
+ end
+
+ def get(%User{id: user_id}, %Token{id: token_id}) do
+ Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
+ end
+
+ def update(user, token, params) do
+ get(user, token)
+ |> change(data: alerts(params))
+ |> Repo.update()
+ end
+
+ def delete(user, token) do
+ Repo.delete(get(user, token))
+ end
+
+ def delete_if_exists(user, token) do
+ case get(user, token) do
+ nil -> {:ok, nil}
+ sub -> Repo.delete(sub)
+ end
+ end
+end
diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex
index d6a9d5779..9c06fac4f 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)
@@ -98,6 +96,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)
@@ -198,6 +198,11 @@ defmodule Pleroma.Web.Router do
put("/filters/:id", MastodonAPIController, :update_filter)
delete("/filters/:id", MastodonAPIController, :delete_filter)
+ post("/push/subscription", MastodonAPIController, :create_push_subscription)
+ get("/push/subscription", MastodonAPIController, :get_push_subscription)
+ put("/push/subscription", MastodonAPIController, :update_push_subscription)
+ delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
+
get("/suggestions", MastodonAPIController, :suggestions)
get("/endorsements", MastodonAPIController, :empty_array)
@@ -324,6 +329,7 @@ defmodule Pleroma.Web.Router do
post("/statusnet/media/upload", TwitterAPI.Controller, :upload)
post("/media/upload", TwitterAPI.Controller, :upload_json)
+ post("/media/metadata/create", TwitterAPI.Controller, :update_media)
post("/favorites/create/:id", TwitterAPI.Controller, :favorite)
post("/favorites/create", TwitterAPI.Controller, :favorite)
diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex
index b98ece6c9..0e2cfddd0 100644
--- a/lib/pleroma/web/salmon/salmon.ex
+++ b/lib/pleroma/web/salmon/salmon.ex
@@ -158,14 +158,11 @@ defmodule Pleroma.Web.Salmon do
end
defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do
- with {:ok, %{status_code: code}} <-
+ with {:ok, %{status: code}} <-
poster.(
salmon,
feed,
- [{"Content-Type", "application/magic-envelope+xml"}],
- timeout: 10000,
- recv_timeout: 20000,
- hackney: [pool: :default]
+ [{"Content-Type", "application/magic-envelope+xml"}]
) do
Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
else
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/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex
index b0ed8387e..0ed519ea6 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}),
@@ -157,13 +156,17 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|> send_resp(200, response)
_ ->
+ vapid_public_key =
+ Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
+
data = %{
name: Keyword.get(instance, :name),
description: Keyword.get(instance, :description),
server: Web.base_url(),
textlimit: to_string(Keyword.get(instance, :limit)),
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
- private: if(Keyword.get(instance, :public, true), do: "0", else: "1")
+ private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
+ vapidPublicKey: vapid_public_key
}
pleroma_fe = %{
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 39a2974bb..79ea48d86 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
@@ -93,8 +90,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end
end
- def upload(%Plug.Upload{} = file, format \\ "xml") do
- {:ok, object} = ActivityPub.upload(file)
+ def upload(%Plug.Upload{} = file, %User{} = user, format \\ "xml") do
+ {:ok, object} = ActivityPub.upload(file, actor: User.ap_id(user))
url = List.first(object.data["url"])
href = url["href"]
@@ -132,7 +129,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
params = %{
nickname: params["nickname"],
name: params["fullname"],
- bio: params["bio"],
+ bio: User.parse_bio(params["bio"]),
email: params["email"],
password: params["password"],
password_confirmation: params["confirm"]
@@ -244,10 +241,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 ff644dd79..786849aa3 100644
--- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex
+++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex
@@ -1,10 +1,8 @@
defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller
- alias Pleroma.Formatter
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
alias Pleroma.Web.CommonAPI
- alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
- alias Pleroma.{Repo, Activity, User, Notification}
+ alias Pleroma.{Repo, Activity, Object, User, Notification}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Ecto.Changeset
@@ -155,7 +153,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
@@ -226,16 +224,51 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
- def upload(conn, %{"media" => media}) do
- response = TwitterAPI.upload(media)
+ @doc """
+ Updates metadata of uploaded media object.
+ Derived from [Twitter API endpoint](https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-metadata-create).
+ """
+ def update_media(%{assigns: %{user: user}} = conn, %{"media_id" => id} = data) do
+ object = Repo.get(Object, id)
+ description = get_in(data, ["alt_text", "text"]) || data["name"] || data["description"]
+
+ {conn, status, response_body} =
+ cond do
+ !object ->
+ {halt(conn), :not_found, ""}
+
+ !Object.authorize_mutation(object, user) ->
+ {halt(conn), :forbidden, "You can only update your own uploads."}
+
+ !is_binary(description) ->
+ {conn, :not_modified, ""}
+
+ true ->
+ new_data = Map.put(object.data, "name", description)
+
+ {:ok, _} =
+ object
+ |> Object.change(%{data: new_data})
+ |> Repo.update()
+
+ {conn, :no_content, ""}
+ end
+
+ conn
+ |> put_status(status)
+ |> json(response_body)
+ end
+
+ def upload(%{assigns: %{user: user}} = conn, %{"media" => media}) do
+ response = TwitterAPI.upload(media, user)
conn
|> put_resp_content_type("application/atom+xml")
|> send_resp(200, response)
end
- def upload_json(conn, %{"media" => media}) do
- response = TwitterAPI.upload(media, "json")
+ def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
+ response = TwitterAPI.upload(media, user, "json")
conn
|> json_reply(200, response)
@@ -340,18 +373,32 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end
end
- def followers(conn, params) do
- with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
+ def followers(%{assigns: %{user: for_user}} = conn, params) do
+ with {:ok, user} <- TwitterAPI.get_user(for_user, params),
{:ok, followers} <- User.get_followers(user) do
+ followers =
+ cond do
+ for_user && user.id == for_user.id -> followers
+ user.info.hide_network -> []
+ true -> followers
+ end
+
render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]})
else
_e -> bad_request_reply(conn, "Can't get followers")
end
end
- def friends(conn, params) do
+ def friends(%{assigns: %{user: for_user}} = conn, params) do
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
{:ok, friends} <- User.get_friends(user) do
+ friends =
+ cond do
+ for_user && user.id == for_user.id -> friends
+ user.info.hide_network -> []
+ true -> friends
+ end
+
render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]})
else
_e -> bad_request_reply(conn, "Can't get friends")
@@ -367,7 +414,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),
@@ -387,7 +434,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),
@@ -429,7 +476,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
defp build_info_cng(user, params) do
info_params =
- ["no_rich_text", "locked"]
+ ["no_rich_text", "locked", "hide_network"]
|> Enum.reduce(%{}, fn key, res ->
if value = params[key] do
Map.put(res, key, value == "true")
@@ -448,27 +495,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
User.Info.profile_update(user.info, info_params)
end
- defp add_profile_emoji(user, params) do
+ defp parse_profile_bio(user, params) do
if bio = params["description"] do
- mentions = Formatter.parse_mentions(bio)
- tags = Formatter.parse_tags(bio)
-
- emoji =
- (user.info.source_data["tag"] || [])
- |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
- |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
- {String.trim(name, ":"), url}
- end)
-
- bio_html = CommonUtils.format_input(bio, mentions, tags, "text/plain")
- Map.put(params, "bio", bio_html |> Formatter.emojify(emoji))
+ Map.put(params, "bio", User.parse_bio(bio, user))
else
params
end
end
def update_profile(%{assigns: %{user: user}} = conn, params) do
- params = add_profile_emoji(user, params)
+ params = parse_profile_bio(user, params)
info_cng = build_info_cng(user, params)
with changeset <- User.update_changeset(user, params),
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 eaee3a8c6..0ff3b8b5f 100644
--- a/lib/pleroma/web/web_finger/web_finger.ex
+++ b/lib/pleroma/web/web_finger/web_finger.ex
@@ -220,8 +220,8 @@ defmodule Pleroma.Web.WebFinger do
end
def find_lrdd_template(domain) do
- with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <-
- @httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do
+ with {:ok, %{status: status, body: body}} when status in 200..299 <-
+ @httpoison.get("http://#{domain}/.well-known/host-meta", []) do
get_template_from_xml(body)
else
_ ->
@@ -259,7 +259,7 @@ defmodule Pleroma.Web.WebFinger do
[Accept: "application/xrd+xml,application/jrd+json"],
follow_redirect: true
),
- {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response do
+ {:ok, %{status: status, body: body}} when status in 200..299 <- response do
doc = XML.parse_document(body)
if doc != :error do
diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex
index 905d8d658..8cb07006f 100644
--- a/lib/pleroma/web/websub/websub.ex
+++ b/lib/pleroma/web/websub/websub.ex
@@ -173,7 +173,7 @@ defmodule Pleroma.Web.Websub do
def gather_feed_data(topic, getter \\ &@httpoison.get/1) do
with {:ok, response} <- getter.(topic),
- status_code when status_code in 200..299 <- response.status_code,
+ status when status in 200..299 <- response.status,
body <- response.body,
doc <- XML.parse_document(body),
uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc),
@@ -221,7 +221,7 @@ defmodule Pleroma.Web.Websub do
task = Task.async(websub_checker)
- with {:ok, %{status_code: 202}} <-
+ with {:ok, %{status: 202}} <-
poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"),
{:ok, websub} <- Task.yield(task, timeout) do
{:ok, websub}
@@ -257,17 +257,14 @@ defmodule Pleroma.Web.Websub do
signature = sign(secret || "", xml)
Logger.info(fn -> "Pushing #{topic} to #{callback}" end)
- with {:ok, %{status_code: code}} <-
+ with {:ok, %{status: code}} <-
@httpoison.post(
callback,
xml,
[
{"Content-Type", "application/atom+xml"},
{"X-Hub-Signature", "sha1=#{signature}"}
- ],
- timeout: 10000,
- recv_timeout: 20000,
- hackney: [pool: :default]
+ ]
) do
Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
{:ok, code}
diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml/xml.ex
index da3f68ecb..63d3302e0 100644
--- a/lib/pleroma/web/xml/xml.ex
+++ b/lib/pleroma/web/xml/xml.ex
@@ -28,12 +28,12 @@ defmodule Pleroma.Web.XML do
|> :xmerl_scan.string()
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