From 4afbef39f49948ddd3b1cd1bbda58ff7e3ac2785 Mon Sep 17 00:00:00 2001 From: lain Date: Fri, 30 Mar 2018 15:01:53 +0200 Subject: [PATCH] Format the code. --- config/config.exs | 37 +- config/dev.exs | 10 +- config/test.exs | 4 +- lib/jason_types.ex | 6 +- lib/mix/tasks/fix_ap_users.ex | 12 +- lib/mix/tasks/generate_config.ex | 60 +- lib/mix/tasks/generate_password_reset.ex | 15 +- lib/mix/tasks/make_moderator.ex | 19 +- lib/pleroma/PasswordResetToken.ex | 8 +- lib/pleroma/activity.ex | 56 +- lib/pleroma/application.ex | 45 +- lib/pleroma/formatter.ex | 120 ++- lib/pleroma/http/http.ex | 3 +- lib/pleroma/notification.ex | 55 +- lib/pleroma/object.ex | 30 +- lib/pleroma/plugs/authentication_plug.ex | 8 +- lib/pleroma/plugs/oauth_plug.ex | 11 +- lib/pleroma/stats.ex | 25 +- lib/pleroma/upload.ex | 93 +- lib/pleroma/user.ex | 264 +++--- lib/pleroma/web/activity_pub/activity_pub.ex | 259 ++++-- .../activity_pub/activity_pub_controller.ex | 11 +- .../web/activity_pub/transmogrifier.ex | 250 +++-- lib/pleroma/web/activity_pub/utils.ex | 93 +- .../web/activity_pub/views/user_view.ex | 25 +- lib/pleroma/web/channels/user_socket.ex | 4 +- lib/pleroma/web/chat_channel.ex | 8 +- lib/pleroma/web/common_api/common_api.ex | 53 +- lib/pleroma/web/common_api/utils.ex | 92 +- lib/pleroma/web/endpoint.ex | 38 +- lib/pleroma/web/federator/federator.ex | 63 +- lib/pleroma/web/mastodon_api/mastodon_api.ex | 1 + .../mastodon_api/mastodon_api_controller.ex | 544 ++++++----- .../web/mastodon_api/mastodon_socket.ex | 26 +- .../web/mastodon_api/views/status_view.ex | 87 +- lib/pleroma/web/media_proxy/controller.ex | 57 +- lib/pleroma/web/media_proxy/media_proxy.ex | 7 +- lib/pleroma/web/oauth/app.ex | 23 +- lib/pleroma/web/oauth/authorization.ex | 17 +- lib/pleroma/web/oauth/fallback_controller.ex | 19 +- lib/pleroma/web/oauth/oauth_controller.ex | 60 +- lib/pleroma/web/oauth/token.ex | 16 +- .../web/ostatus/activity_representer.ex | 185 ++-- lib/pleroma/web/ostatus/feed_representer.ex | 81 +- .../web/ostatus/handlers/follow_handler.ex | 3 +- .../web/ostatus/handlers/note_handler.ex | 82 +- lib/pleroma/web/ostatus/ostatus.ex | 184 ++-- lib/pleroma/web/ostatus/ostatus_controller.ex | 42 +- lib/pleroma/web/ostatus/user_representer.ex | 24 +- lib/pleroma/web/router.ex | 329 +++---- lib/pleroma/web/salmon/salmon.ex | 90 +- lib/pleroma/web/streamer.ex | 65 +- .../controllers/util_controller.ex | 98 +- .../representers/activity_representer.ex | 86 +- .../representers/base_representer.ex | 16 +- .../representers/object_representer.ex | 1 + lib/pleroma/web/twitter_api/twitter_api.ex | 173 ++-- .../web/twitter_api/twitter_api_controller.ex | 81 +- .../web/twitter_api/views/activity_view.ex | 44 +- .../web/twitter_api/views/user_view.ex | 35 +- lib/pleroma/web/views/error_view.ex | 2 +- lib/pleroma/web/web.ex | 7 +- lib/pleroma/web/web_finger/web_finger.ex | 206 +++-- .../web/web_finger/web_finger_controller.ex | 4 +- lib/pleroma/web/websub/websub.ex | 158 ++-- .../web/websub/websub_client_subscription.ex | 14 +- lib/pleroma/web/websub/websub_controller.ex | 58 +- .../web/websub/websub_server_subscription.ex | 10 +- lib/pleroma/web/xml/xml.ex | 15 +- lib/transports.ex | 28 +- lib/xml_builder.ex | 12 +- mix.exs | 65 +- test/activity_test.exs | 4 +- test/formatter_test.exs | 51 +- test/notification_test.exs | 34 +- test/plugs/authentication_plug_test.exs | 44 +- test/support/builders/activity_builder.ex | 8 +- test/support/builders/user_builder.ex | 1 + test/support/channel_case.ex | 4 +- test/support/conn_case.ex | 4 +- test/support/factory.ex | 28 +- test/support/httpoison_mock.ex | 873 +++++++++++------- test/support/ostatus_mock.ex | 1 + test/test_helper.exs | 1 - test/upload_test.exs | 25 +- test/user_test.exs | 52 +- .../activity_pub_controller_test.exs | 27 +- test/web/activity_pub/activity_pub_test.exs | 52 +- test/web/activity_pub/transmogrifier_test.exs | 192 ++-- test/web/common_api/common_api_utils_test.exs | 6 +- test/web/mastodon_api/account_view_test.exs | 7 +- .../mastodon_api_controller_test.exs | 410 ++++---- test/web/mastodon_api/status_view_test.exs | 9 +- test/web/oauth/authorization_test.exs | 22 +- test/web/oauth/token_test.exs | 10 +- .../web/ostatus/activity_representer_test.exs | 80 +- test/web/ostatus/feed_representer_test.exs | 17 +- .../delete_handling_test.exs | 9 +- test/web/ostatus/ostatus_controller_test.exs | 66 +- test/web/ostatus/ostatus_test.exs | 126 ++- test/web/salmon/salmon_test.exs | 13 +- .../activity_representer_test.exs | 44 +- .../representers/object_representer_test.exs | 9 +- .../twitter_api_controller_test.exs | 350 ++++--- test/web/twitter_api/twitter_api_test.exs | 110 ++- test/web/twitter_api/views/user_view_test.exs | 10 +- test/web/views/error_view_test.exs | 7 +- test/web/web_finger/web_finger_test.exs | 14 +- test/web/websub/websub_controller_test.exs | 30 +- test/web/websub/websub_test.exs | 85 +- test/xml_builder_test.exs | 14 +- 111 files changed, 4912 insertions(+), 2769 deletions(-) diff --git a/config/config.exs b/config/config.exs index b41551895..fc8067b49 100644 --- a/config/config.exs +++ b/config/config.exs @@ -6,14 +6,11 @@ use Mix.Config # General application configuration -config :pleroma, - ecto_repos: [Pleroma.Repo] +config :pleroma, ecto_repos: [Pleroma.Repo] -config :pleroma, Pleroma.Repo, - types: Pleroma.PostgresTypes +config :pleroma, Pleroma.Repo, types: Pleroma.PostgresTypes -config :pleroma, Pleroma.Upload, - uploads: "uploads" +config :pleroma, Pleroma.Upload, uploads: "uploads" # Configures the endpoint config :pleroma, Pleroma.Web.Endpoint, @@ -21,8 +18,7 @@ protocol: "https", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)], - pubsub: [name: Pleroma.PubSub, - adapter: Phoenix.PubSub.PG2] + pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2] # Configures Elixir's Logger config :logger, :console, @@ -38,15 +34,15 @@ config :pleroma, :ostatus, Pleroma.Web.OStatus config :pleroma, :httpoison, Pleroma.HTTP -version = with {version, 0} <- System.cmd("git", ["rev-parse", "HEAD"]) do - "Pleroma #{Mix.Project.config[:version]} #{String.trim(version)}" - else - _ -> "Pleroma #{Mix.Project.config[:version]} dev" - end +version = + with {version, 0} <- System.cmd("git", ["rev-parse", "HEAD"]) do + "Pleroma #{Mix.Project.config()[:version]} #{String.trim(version)}" + else + _ -> "Pleroma #{Mix.Project.config()[:version]} dev" + end # Configures http settings, upstream proxy etc. -config :pleroma, :http, - proxy_url: nil +config :pleroma, :http, proxy_url: nil config :pleroma, :instance, version: version, @@ -59,16 +55,15 @@ config :pleroma, :media_proxy, enabled: false, redirect_on_failure: true - #base_url: "https://cache.pleroma.social" -config :pleroma, :chat, - enabled: true +# base_url: "https://cache.pleroma.social" + +config :pleroma, :chat, enabled: true config :ecto, json_library: Jason -config :phoenix, :format_encoders, - json: Jason +config :phoenix, :format_encoders, json: Jason # Import environment specific config. This must remain at the bottom # of this file so it overrides the configuration defined above. -import_config "#{Mix.env}.exs" +import_config "#{Mix.env()}.exs" diff --git a/config/dev.exs b/config/dev.exs index a697d3a24..7b06ad67e 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -7,7 +7,10 @@ # watchers to your application. For example, we use it # with brunch.io to recompile .js and .css sources. config :pleroma, Pleroma.Web.Endpoint, - http: [port: 4000, protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]], + http: [ + port: 4000, + protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192] + ], protocol: "http", debug_errors: true, code_reloader: true, @@ -49,5 +52,8 @@ try do import_config "dev.secret.exs" rescue - _-> IO.puts("!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs") + _ -> + IO.puts( + "!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs" + ) end diff --git a/config/test.exs b/config/test.exs index 26b5410fe..e0461e93d 100644 --- a/config/test.exs +++ b/config/test.exs @@ -9,8 +9,7 @@ # Print only warnings and errors during test config :logger, level: :warn -config :pleroma, Pleroma.Upload, - uploads: "test/uploads" +config :pleroma, Pleroma.Upload, uploads: "test/uploads" # Configure your database config :pleroma, Pleroma.Repo, @@ -21,7 +20,6 @@ hostname: System.get_env("DB_HOST") || "localhost", pool: Ecto.Adapters.SQL.Sandbox - # Reduce hash rounds for testing config :comeonin, :pbkdf2_rounds, 1 diff --git a/lib/jason_types.ex b/lib/jason_types.ex index 4c89664ff..d1a7bc7ac 100644 --- a/lib/jason_types.ex +++ b/lib/jason_types.ex @@ -1 +1,5 @@ -Postgrex.Types.define(Pleroma.PostgresTypes, [] ++ Ecto.Adapters.Postgres.extensions(), json: Jason) +Postgrex.Types.define( + Pleroma.PostgresTypes, + [] ++ Ecto.Adapters.Postgres.extensions(), + json: Jason +) diff --git a/lib/mix/tasks/fix_ap_users.ex b/lib/mix/tasks/fix_ap_users.ex index ff09074c3..2523cdbcb 100644 --- a/lib/mix/tasks/fix_ap_users.ex +++ b/lib/mix/tasks/fix_ap_users.ex @@ -8,12 +8,16 @@ defmodule Mix.Tasks.FixApUsers do def run([]) do Mix.Task.run("app.start") - q = from u in User, - where: fragment("? @> ?", u.info, ^%{"ap_enabled" => true}), - where: u.local == false + q = + from( + u in User, + where: fragment("? @> ?", u.info, ^%{"ap_enabled" => true}), + where: u.local == false + ) + users = Repo.all(q) - Enum.each(users, fn(user) -> + Enum.each(users, fn user -> try do IO.puts("Fetching #{user.nickname}") Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(user.ap_id, false) diff --git a/lib/mix/tasks/generate_config.ex b/lib/mix/tasks/generate_config.ex index 2d962124f..ae9ac3b75 100644 --- a/lib/mix/tasks/generate_config.ex +++ b/lib/mix/tasks/generate_config.ex @@ -5,27 +5,51 @@ defmodule Mix.Tasks.GenerateConfig do 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 - mediaproxy = IO.gets("Do you want to activate the mediaproxy? (y/N): ") - |> String.trim() - |> String.downcase() - |> String.starts_with?("y") - proxy_url = if mediaproxy do - IO.gets("What is the mediaproxy's URL? (e.g. https://cache.example.com): ") |> String.trim - else - "https://cache.example.com" - end - secret = :crypto.strong_rand_bytes(64) |> Base.encode64 |> binary_part(0, 64) - dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64 |> binary_part(0, 64) + 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() - 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, mediaproxy: mediaproxy, proxy_url: proxy_url, dbpass: dbpass]) + mediaproxy = + IO.gets("Do you want to activate the mediaproxy? (y/N): ") + |> String.trim() + |> String.downcase() + |> String.starts_with?("y") + + proxy_url = + if mediaproxy do + IO.gets("What is the mediaproxy's URL? (e.g. https://cache.example.com): ") + |> String.trim() + else + "https://cache.example.com" + end + + 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, + mediaproxy: mediaproxy, + proxy_url: proxy_url, + 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" + ) - 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'") + + 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_password_reset.ex b/lib/mix/tasks/generate_password_reset.ex index b968b1f98..e39134007 100644 --- a/lib/mix/tasks/generate_password_reset.ex +++ b/lib/mix/tasks/generate_password_reset.ex @@ -9,11 +9,20 @@ def run([nickname]) do 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)}" + 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}" + 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 index a76b54f40..20f04c54c 100644 --- a/lib/mix/tasks/make_moderator.ex +++ b/lib/mix/tasks/make_moderator.ex @@ -7,21 +7,24 @@ defmodule Mix.Tasks.SetModerator do def run([nickname | rest]) do ensure_started(Repo, []) - moderator = case rest do - [moderator] -> moderator == "true" - _ -> true - end + 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) + info = + user.info + |> Map.put("is_moderator", !!moderator) + cng = User.info_changeset(user, %{info: info}) user = Repo.update!(cng) - IO.puts "Moderator status of #{nickname}: #{user.info["is_moderator"]}" + IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}") else _ -> - IO.puts "No local user #{nickname}" + IO.puts("No local user #{nickname}") end end end diff --git a/lib/pleroma/PasswordResetToken.ex b/lib/pleroma/PasswordResetToken.ex index 52b1fcd50..15750565b 100644 --- a/lib/pleroma/PasswordResetToken.ex +++ b/lib/pleroma/PasswordResetToken.ex @@ -6,15 +6,15 @@ defmodule Pleroma.PasswordResetToken do alias Pleroma.{User, PasswordResetToken, Repo} schema "password_reset_tokens" do - belongs_to :user, User - field :token, :string - field :used, :boolean, default: false + belongs_to(:user, User) + field(:token, :string) + field(:used, :boolean, default: false) timestamps() end def create_token(%User{} = user) do - token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 + token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() token = %PasswordResetToken{ user_id: user.id, diff --git a/lib/pleroma/activity.ex b/lib/pleroma/activity.ex index 96ddcc480..c7502981e 100644 --- a/lib/pleroma/activity.ex +++ b/lib/pleroma/activity.ex @@ -4,33 +4,53 @@ defmodule Pleroma.Activity do import Ecto.Query schema "activities" do - field :data, :map - field :local, :boolean, default: true - field :actor, :string - field :recipients, {:array, :string} - has_many :notifications, Notification, on_delete: :delete_all + field(:data, :map) + field(:local, :boolean, default: true) + field(:actor, :string) + field(:recipients, {:array, :string}) + has_many(:notifications, Notification, on_delete: :delete_all) timestamps() end def get_by_ap_id(ap_id) do - Repo.one(from activity in Activity, - where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id))) + Repo.one( + from( + activity in Activity, + where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)) + ) + ) end # TODO: # Go through these and fix them everywhere. # Wrong name, only returns create activities def all_by_object_ap_id_q(ap_id) do - from activity in Activity, - where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^to_string(ap_id)), + from( + activity in Activity, + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^to_string(ap_id) + ), where: fragment("(?)->>'type' = 'Create'", activity.data) + ) end # Wrong name, returns all. def all_non_create_by_object_ap_id_q(ap_id) do - from activity in Activity, - where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^to_string(ap_id)) + from( + activity in Activity, + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^to_string(ap_id) + ) + ) end # Wrong name plz fix thx @@ -39,13 +59,21 @@ def all_by_object_ap_id(ap_id) do end def create_activity_by_object_id_query(ap_ids) do - from activity in Activity, - where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", activity.data, activity.data, ^ap_ids), + from( + activity in Activity, + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", + activity.data, + activity.data, + ^ap_ids + ), where: fragment("(?)->>'type' = 'Create'", activity.data) + ) end def get_create_activity_by_object_ap_id(ap_id) do create_activity_by_object_id_query([ap_id]) - |> Repo.one + |> Repo.one() end end diff --git a/lib/pleroma/application.ex b/lib/pleroma/application.ex index 79b9dee9d..5e29a5d65 100644 --- a/lib/pleroma/application.ex +++ b/lib/pleroma/application.ex @@ -7,23 +7,34 @@ def start(_type, _args) do import Supervisor.Spec # Define workers and child supervisors to be supervised - children = [ - # Start the Ecto repository - supervisor(Pleroma.Repo, []), - # Start the endpoint when the application starts - supervisor(Pleroma.Web.Endpoint, []), - # Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3) - # worker(Pleroma.Worker, [arg1, arg2, arg3]), - worker(Cachex, [:user_cache, [ - default_ttl: 25000, - ttl_interval: 1000, - limit: 2500 - ]]), - worker(Pleroma.Web.Federator, []), - worker(Pleroma.Stats, []), - ] - ++ if Mix.env == :test, do: [], else: [worker(Pleroma.Web.Streamer, [])] - ++ if !chat_enabled(), do: [], else: [worker(Pleroma.Web.ChatChannel.ChatChannelState, [])] + children = + [ + # Start the Ecto repository + supervisor(Pleroma.Repo, []), + # Start the endpoint when the application starts + supervisor(Pleroma.Web.Endpoint, []), + # Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3) + # worker(Pleroma.Worker, [arg1, arg2, arg3]), + worker(Cachex, [ + :user_cache, + [ + default_ttl: 25000, + ttl_interval: 1000, + limit: 2500 + ] + ]), + worker(Pleroma.Web.Federator, []), + worker(Pleroma.Stats, []) + ] ++ + if Mix.env() == :test, + do: [], + else: + [worker(Pleroma.Web.Streamer, [])] ++ + if( + !chat_enabled(), + do: [], + else: [worker(Pleroma.Web.ChatChannel.ChatChannelState, [])] + ) # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html # for other strategies and supported options diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 66ca92f27..9396b1826 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -5,19 +5,26 @@ defmodule Pleroma.Formatter do @tag_regex ~r/\#\w+/u def parse_tags(text, data \\ %{}) do Regex.scan(@tag_regex, text) - |> Enum.map(fn (["#" <> tag = full_tag]) -> {full_tag, String.downcase(tag)} end) - |> (fn map -> if data["sensitive"] in [true, "True", "true", "1"], do: [{"#nsfw", "nsfw"}] ++ map, else: map end).() + |> Enum.map(fn ["#" <> tag = full_tag] -> {full_tag, String.downcase(tag)} end) + |> (fn map -> + if data["sensitive"] in [true, "True", "true", "1"], + do: [{"#nsfw", "nsfw"}] ++ map, + else: map + end).() end def parse_mentions(text) do # Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address - regex = ~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u + regex = + ~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u Regex.scan(regex, text) - |> List.flatten - |> Enum.uniq - |> Enum.map(fn ("@" <> match = full_match) -> {full_match, User.get_cached_by_nickname(match)} end) - |> Enum.filter(fn ({_match, user}) -> user end) + |> List.flatten() + |> Enum.uniq() + |> Enum.map(fn "@" <> match = full_match -> + {full_match, User.get_cached_by_nickname(match)} + end) + |> Enum.filter(fn {_match, user} -> user end) end @finmoji [ @@ -86,9 +93,9 @@ def parse_mentions(text) do "woollysocks" ] - @finmoji_with_filenames Enum.map(@finmoji, fn (finmoji) -> - {finmoji, "/finmoji/128px/#{finmoji}-128.png"} - end) + @finmoji_with_filenames Enum.map(@finmoji, fn finmoji -> + {finmoji, "/finmoji/128px/#{finmoji}-128.png"} + end) @emoji_from_file (with {:ok, default} <- File.read("config/emoji.txt") do custom = @@ -97,31 +104,40 @@ def parse_mentions(text) do else _e -> "" end + (default <> "\n" <> custom) |> String.trim() |> String.split(~r/\n+/) - |> Enum.map(fn(line) -> + |> Enum.map(fn line -> [name, file] = String.split(line, ~r/,\s*/) {name, file} - end) + end) else _ -> [] - end) + end) @emoji @finmoji_with_filenames ++ @emoji_from_file def emojify(text, emoji \\ @emoji) def emojify(text, nil), do: text + def emojify(text, emoji) do - Enum.reduce(emoji, text, fn ({emoji, file}, text) -> + Enum.reduce(emoji, text, fn {emoji, file}, text -> emoji = HtmlSanitizeEx.strip_tags(emoji) file = HtmlSanitizeEx.strip_tags(file) - String.replace(text, ":#{emoji}:", "#{emoji}") + + String.replace( + text, + ":#{emoji}:", + "#{emoji}" + ) end) end def get_emoji(text) do - Enum.filter(@emoji, fn ({emoji, _}) -> String.contains?(text, ":#{emoji}:") end) + Enum.filter(@emoji, fn {emoji, _} -> String.contains?(text, ":#{emoji}:") end) end def get_custom_emoji() do @@ -141,59 +157,71 @@ def html_escape(text) do @doc "changes http:... links to html links" def add_links({subs, text}) do - links = Regex.scan(@link_regex, text) - |> Enum.map(fn ([url]) -> {Ecto.UUID.generate, url} end) + links = + Regex.scan(@link_regex, text) + |> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end) - uuid_text = links - |> Enum.reduce(text, fn({uuid, url}, acc) -> String.replace(acc, url, uuid) end) + uuid_text = + links + |> Enum.reduce(text, fn {uuid, url}, acc -> String.replace(acc, url, uuid) end) - subs = subs ++ Enum.map(links, fn({uuid, url}) -> - {uuid, "#{url}"} - end) + subs = + subs ++ + Enum.map(links, fn {uuid, url} -> + {uuid, "#{url}"} + end) {subs, uuid_text} end @doc "Adds the links to mentioned users" def add_user_links({subs, text}, mentions) do - mentions = mentions - |> Enum.sort_by(fn ({name, _}) -> -String.length(name) end) - |> Enum.map(fn({name, user}) -> {name, user, Ecto.UUID.generate} end) + mentions = + mentions + |> Enum.sort_by(fn {name, _} -> -String.length(name) end) + |> Enum.map(fn {name, user} -> {name, user, Ecto.UUID.generate()} end) - uuid_text = mentions - |> Enum.reduce(text, fn ({match, _user, uuid}, text) -> - String.replace(text, match, uuid) - end) + uuid_text = + mentions + |> Enum.reduce(text, fn {match, _user, uuid}, text -> + String.replace(text, match, uuid) + end) - subs = subs ++ Enum.map(mentions, fn ({match, %User{ap_id: ap_id}, uuid}) -> - short_match = String.split(match, "@") |> tl() |> hd() - {uuid, "@#{short_match}"} - end) + subs = + subs ++ + Enum.map(mentions, fn {match, %User{ap_id: ap_id}, uuid} -> + short_match = String.split(match, "@") |> tl() |> hd() + {uuid, "@#{short_match}"} + end) {subs, uuid_text} end @doc "Adds the hashtag links" def add_hashtag_links({subs, text}, tags) do - tags = tags - |> Enum.sort_by(fn ({name, _}) -> -String.length(name) end) - |> Enum.map(fn({name, short}) -> {name, short, Ecto.UUID.generate} end) + tags = + tags + |> Enum.sort_by(fn {name, _} -> -String.length(name) end) + |> Enum.map(fn {name, short} -> {name, short, Ecto.UUID.generate()} end) - uuid_text = tags - |> Enum.reduce(text, fn ({match, _short, uuid}, text) -> - String.replace(text, match, uuid) - end) + uuid_text = + tags + |> Enum.reduce(text, fn {match, _short, uuid}, text -> + String.replace(text, match, uuid) + end) - subs = subs ++ Enum.map(tags, fn ({_, tag, uuid}) -> - url = "" - {uuid, url} - end) + subs = + subs ++ + Enum.map(tags, fn {_, tag, uuid} -> + url = "" + {uuid, url} + end) {subs, uuid_text} end def finalize({subs, text}) do - Enum.reduce(subs, text, fn({uuid, replacement}, result_text) -> + Enum.reduce(subs, text, fn {uuid, replacement}, result_text -> String.replace(result_text, uuid, replacement) end) end diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http/http.ex index 8b8a82353..84f34eb4a 100644 --- a/lib/pleroma/http/http.ex +++ b/lib/pleroma/http/http.ex @@ -1,14 +1,13 @@ - defmodule Pleroma.HTTP do use HTTPoison.Base def process_request_options(options) do config = Application.get_env(:pleroma, :http, []) proxy = Keyword.get(config, :proxy_url, nil) + case proxy do nil -> options _ -> options ++ [proxy: proxy] end end - end diff --git a/lib/pleroma/notification.ex b/lib/pleroma/notification.ex index 241d6a9e0..e26e49c8c 100644 --- a/lib/pleroma/notification.ex +++ b/lib/pleroma/notification.ex @@ -4,75 +4,89 @@ defmodule Pleroma.Notification do import Ecto.Query schema "notifications" do - field :seen, :boolean, default: false - belongs_to :user, Pleroma.User - belongs_to :activity, Pleroma.Activity + field(:seen, :boolean, default: false) + belongs_to(:user, Pleroma.User) + belongs_to(:activity, Pleroma.Activity) timestamps() end # TODO: Make generic and unify (see activity_pub.ex) defp restrict_max(query, %{"max_id" => max_id}) do - from activity in query, where: activity.id < ^max_id + from(activity in query, where: activity.id < ^max_id) end + defp restrict_max(query, _), do: query defp restrict_since(query, %{"since_id" => since_id}) do - from activity in query, where: activity.id > ^since_id + from(activity in query, where: activity.id > ^since_id) end + defp restrict_since(query, _), do: query def for_user(user, opts \\ %{}) do - query = from n in Notification, - where: n.user_id == ^user.id, - order_by: [desc: n.id], - preload: [:activity], - limit: 20 + query = + from( + n in Notification, + where: n.user_id == ^user.id, + order_by: [desc: n.id], + preload: [:activity], + limit: 20 + ) - query = query - |> restrict_since(opts) - |> restrict_max(opts) + query = + query + |> restrict_since(opts) + |> restrict_max(opts) Repo.all(query) end def get(%{id: user_id} = _user, id) do - query = from n in Notification, - where: n.id == ^id, - preload: [:activity] + query = + from( + n in Notification, + where: n.id == ^id, + preload: [:activity] + ) notification = Repo.one(query) + case notification do %{user_id: ^user_id} -> {:ok, notification} + _ -> {:error, "Cannot get notification"} end end def clear(user) do - query = from n in Notification, - where: n.user_id == ^user.id + query = from(n in Notification, where: n.user_id == ^user.id) Repo.delete_all(query) end def dismiss(%{id: user_id} = _user, id) do notification = Repo.get(Notification, id) + case notification do %{user_id: ^user_id} -> Repo.delete(notification) + _ -> {:error, "Cannot dismiss notification"} end end - def create_notifications(%Activity{id: _, data: %{"to" => _, "type" => type}} = activity) when type in ["Create", "Like", "Announce", "Follow"] do + def create_notifications(%Activity{id: _, data: %{"to" => _, "type" => type}} = activity) + when type in ["Create", "Like", "Announce", "Follow"] do users = User.get_notified_from_activity(activity) - notifications = Enum.map(users, fn (user) -> create_notification(activity, user) end) + notifications = Enum.map(users, fn user -> create_notification(activity, user) end) {:ok, notifications} end + def create_notifications(_), do: {:ok, []} # TODO move to sql, too. @@ -85,4 +99,3 @@ def create_notification(%Activity{} = activity, %User{} = user) do end end end - diff --git a/lib/pleroma/object.ex b/lib/pleroma/object.ex index 30ba7b57a..558e151b0 100644 --- a/lib/pleroma/object.ex +++ b/lib/pleroma/object.ex @@ -4,14 +4,14 @@ defmodule Pleroma.Object do import Ecto.{Query, Changeset} schema "objects" do - field :data, :map + field(:data, :map) timestamps() end def create(data) do Object.change(%Object{}, %{data: data}) - |> Repo.insert + |> Repo.insert() end def change(struct, params \\ %{}) do @@ -22,24 +22,30 @@ def change(struct, params \\ %{}) do end def get_by_ap_id(nil), do: nil + def get_by_ap_id(ap_id) do - Repo.one(from object in Object, - where: fragment("(?)->>'id' = ?", object.data, ^ap_id)) + Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id))) end def get_cached_by_ap_id(ap_id) do - if Mix.env == :test do + if Mix.env() == :test do get_by_ap_id(ap_id) else key = "object:#{ap_id}" - Cachex.get!(:user_cache, key, fallback: fn(_) -> - object = get_by_ap_id(ap_id) - if object do - {:commit, object} - else - {:ignore, object} + + Cachex.get!( + :user_cache, + key, + fallback: fn _ -> + object = get_by_ap_id(ap_id) + + if object do + {:commit, object} + else + {:ignore, object} + end end - end) + ) end end diff --git a/lib/pleroma/plugs/authentication_plug.ex b/lib/pleroma/plugs/authentication_plug.ex index 4794e625a..86a514541 100644 --- a/lib/pleroma/plugs/authentication_plug.ex +++ b/lib/pleroma/plugs/authentication_plug.ex @@ -14,8 +14,7 @@ def call(conn, opts) do {:ok, user} <- opts[:fetcher].(username), false <- !!user.info["deactivated"], saved_user_id <- get_session(conn, :user_id), - {:ok, verified_user} <- verify(user, password, saved_user_id) - do + {:ok, verified_user} <- verify(user, password, saved_user_id) do conn |> assign(:user, verified_user) |> put_session(:user_id, verified_user.id) @@ -30,7 +29,7 @@ defp verify(%{id: id} = user, _password, id) do end defp verify(nil, _password, _user_id) do - Pbkdf2.dummy_checkpw + Pbkdf2.dummy_checkpw() :error end @@ -45,8 +44,7 @@ defp verify(user, password, _user_id) do defp decode_header(conn) do with ["Basic " <> header] <- get_req_header(conn, "authorization"), {:ok, userinfo} <- Base.decode64(header), - [username, password] <- String.split(userinfo, ":", parts: 2) - do + [username, password] <- String.split(userinfo, ":", parts: 2) do {:ok, username, password} end end diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index be737dc9a..0380ce14d 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -9,11 +9,14 @@ def init(options) do end 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 + 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), diff --git a/lib/pleroma/stats.ex b/lib/pleroma/stats.ex index 5f8130ff8..83b896a16 100644 --- a/lib/pleroma/stats.ex +++ b/lib/pleroma/stats.ex @@ -18,22 +18,31 @@ def get_peers do def schedule_update do spawn(fn -> - Process.sleep(1000 * 60 * 60 * 1) # 1 hour + # 1 hour + Process.sleep(1000 * 60 * 60 * 1) schedule_update() end) + update_stats() end def update_stats do - peers = from(u in Pleroma.User, - select: fragment("distinct ?->'host'", u.info), - where: u.local != ^true) - |> Repo.all() + peers = + from( + u in Pleroma.User, + select: fragment("distinct ?->'host'", u.info), + where: u.local != ^true + ) + |> Repo.all() + domain_count = Enum.count(peers) - status_query = from(u in User.local_user_query, - select: fragment("sum((?->>'note_count')::int)", u.info)) + + status_query = + from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info)) + status_count = Repo.one(status_query) - user_count = Repo.aggregate(User.local_user_query, :count, :id) + user_count = Repo.aggregate(User.local_user_query(), :count, :id) + Agent.update(__MODULE__, fn _ -> {peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} end) diff --git a/lib/pleroma/upload.ex b/lib/pleroma/upload.ex index c41617c68..8a9dc612a 100644 --- a/lib/pleroma/upload.ex +++ b/lib/pleroma/upload.ex @@ -1,27 +1,31 @@ defmodule Pleroma.Upload do alias Ecto.UUID alias Pleroma.Web + def store(%Plug.Upload{} = file) do - uuid = UUID.generate + uuid = UUID.generate() upload_folder = Path.join(upload_path(), uuid) File.mkdir_p!(upload_folder) result_file = Path.join(upload_folder, file.filename) File.cp!(file.path, result_file) # fix content type on some image uploads - content_type = if file.content_type in [nil, "application/octet-stream"] do - get_content_type(file.path) - else - file.content_type - end + content_type = + if file.content_type in [nil, "application/octet-stream"] do + get_content_type(file.path) + else + file.content_type + end %{ "type" => "Image", - "url" => [%{ - "type" => "Link", - "mediaType" => content_type, - "href" => url_for(Path.join(uuid, :cow_uri.urlencode(file.filename))) - }], + "url" => [ + %{ + "type" => "Link", + "mediaType" => content_type, + "href" => url_for(Path.join(uuid, :cow_uri.urlencode(file.filename))) + } + ], "name" => file.filename, "uuid" => uuid } @@ -30,7 +34,7 @@ def store(%Plug.Upload{} = file) do def store(%{"img" => "data:image/" <> image_data}) do parsed = Regex.named_captures(~r/(?jpeg|png|gif);base64,(?.*)/, image_data) data = Base.decode64!(parsed["data"]) - uuid = UUID.generate + uuid = UUID.generate() upload_folder = Path.join(upload_path(), uuid) File.mkdir_p!(upload_folder) filename = Base.encode16(:crypto.hash(:sha256, data)) <> ".#{parsed["filetype"]}" @@ -42,11 +46,13 @@ def store(%{"img" => "data:image/" <> image_data}) do %{ "type" => "Image", - "url" => [%{ - "type" => "Link", - "mediaType" => content_type, - "href" => url_for(Path.join(uuid, :cow_uri.urlencode(filename))) - }], + "url" => [ + %{ + "type" => "Link", + "mediaType" => content_type, + "href" => url_for(Path.join(uuid, :cow_uri.urlencode(filename))) + } + ], "name" => filename, "uuid" => uuid } @@ -62,28 +68,37 @@ defp url_for(file) do end def get_content_type(file) do - match = File.open(file, [:read], fn(f) -> - case IO.binread(f, 8) do - <<0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a>> -> - "image/png" - <<0x47, 0x49, 0x46, 0x38, _, 0x61, _, _>> -> - "image/gif" - <<0xff, 0xd8, 0xff, _, _, _, _, _>> -> - "image/jpeg" - <<0x1a, 0x45, 0xdf, 0xa3, _, _, _, _>> -> - "video/webm" - <<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70>> -> - "video/mp4" - <<0x49, 0x44, 0x33, _, _, _, _, _>> -> - "audio/mpeg" - <<0x4f, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> -> - "audio/ogg" - <<0x52, 0x49, 0x46, 0x46, _, _, _, _>> -> - "audio/wav" - _ -> - "application/octet-stream" - end - end) + match = + File.open(file, [:read], fn f -> + case IO.binread(f, 8) do + <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A>> -> + "image/png" + + <<0x47, 0x49, 0x46, 0x38, _, 0x61, _, _>> -> + "image/gif" + + <<0xFF, 0xD8, 0xFF, _, _, _, _, _>> -> + "image/jpeg" + + <<0x1A, 0x45, 0xDF, 0xA3, _, _, _, _>> -> + "video/webm" + + <<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70>> -> + "video/mp4" + + <<0x49, 0x44, 0x33, _, _, _, _, _>> -> + "audio/mpeg" + + <<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> -> + "audio/ogg" + + <<0x52, 0x49, 0x46, 0x46, _, _, _, _>> -> + "audio/wav" + + _ -> + "application/octet-stream" + end + end) case match do {:ok, type} -> type diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 0594afb38..55b290309 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -8,20 +8,20 @@ defmodule Pleroma.User do alias Pleroma.Web.ActivityPub.{Utils, ActivityPub} schema "users" do - field :bio, :string - field :email, :string - field :name, :string - field :nickname, :string - field :password_hash, :string - field :password, :string, virtual: true - field :password_confirmation, :string, virtual: true - field :following, {:array, :string}, default: [] - field :ap_id, :string - field :avatar, :map - field :local, :boolean, default: true - field :info, :map, default: %{} - field :follower_address, :string - has_many :notifications, Notification + field(:bio, :string) + field(:email, :string) + field(:name, :string) + field(:nickname, :string) + field(:password_hash, :string) + field(:password, :string, virtual: true) + field(:password_confirmation, :string, virtual: true) + field(:following, {:array, :string}, default: []) + field(:ap_id, :string) + field(:avatar, :map) + field(:local, :boolean, default: true) + field(:info, :map, default: %{}) + field(:follower_address, :string) + has_many(:notifications, Notification) timestamps() end @@ -41,7 +41,7 @@ def banner_url(user) do end def ap_id(%User{nickname: nickname}) do - "#{Web.base_url}/users/#{nickname}" + "#{Web.base_url()}/users/#{nickname}" end def ap_followers(%User{} = user) do @@ -62,6 +62,7 @@ def info_changeset(struct, params \\ %{}) do def user_info(%User{} = user) do oneself = if user.local, do: 1, else: 0 + %{ following_count: length(user.following) - oneself, note_count: user.info["note_count"] || 0, @@ -71,21 +72,25 @@ def user_info(%User{} = user) do @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ def remote_user_creation(params) do - changes = %User{} - |> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar]) - |> validate_required([:name, :ap_id, :nickname]) - |> unique_constraint(:nickname) - |> validate_format(:nickname, @email_regex) - |> validate_length(:bio, max: 5000) - |> validate_length(:name, max: 100) - |> put_change(:local, false) + changes = + %User{} + |> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar]) + |> validate_required([:name, :ap_id, :nickname]) + |> unique_constraint(:nickname) + |> validate_format(:nickname, @email_regex) + |> validate_length(:bio, max: 5000) + |> validate_length(:name, max: 100) + |> put_change(:local, false) + if changes.valid? do case changes.changes[:info]["source_data"] do %{"followers" => followers} -> changes |> put_change(:follower_address, followers) + _ -> followers = User.ap_followers(%User{nickname: changes.changes[:nickname]}) + changes |> put_change(:follower_address, followers) end @@ -113,13 +118,15 @@ def upgrade_changeset(struct, params \\ %{}) do end def password_update_changeset(struct, params) do - changeset = struct - |> cast(params, [:password, :password_confirmation]) - |> validate_required([:password, :password_confirmation]) - |> validate_confirmation(:password) + changeset = + struct + |> cast(params, [:password, :password_confirmation]) + |> validate_required([:password, :password_confirmation]) + |> validate_confirmation(:password) if changeset.valid? do hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) + changeset |> put_change(:password_hash, hashed) else @@ -132,21 +139,23 @@ def reset_password(user, data) do end def register_changeset(struct, params \\ %{}) do - changeset = struct - |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) - |> validate_required([:email, :name, :nickname, :password, :password_confirmation]) - |> validate_confirmation(:password) - |> unique_constraint(:email) - |> unique_constraint(:nickname) - |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) - |> validate_format(:email, @email_regex) - |> validate_length(:bio, max: 1000) - |> validate_length(:name, min: 1, max: 100) + changeset = + struct + |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) + |> validate_required([:email, :name, :nickname, :password, :password_confirmation]) + |> validate_confirmation(:password) + |> unique_constraint(:email) + |> unique_constraint(:nickname) + |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) + |> validate_format(:email, @email_regex) + |> validate_length(:bio, max: 1000) + |> validate_length(:name, min: 1, max: 100) if changeset.valid? do hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]}) followers = User.ap_followers(%User{nickname: changeset.changes[:nickname]}) + changeset |> put_change(:password_hash, hashed) |> put_change(:ap_id, ap_id) @@ -161,19 +170,20 @@ def follow(%User{} = follower, %User{info: info} = followed) do ap_followers = followed.follower_address if following?(follower, followed) or info["deactivated"] do - {:error, - "Could not follow user: #{followed.nickname} is already on your list."} + {:error, "Could not follow user: #{followed.nickname} is already on your list."} else if !followed.local && follower.local && !ap_enabled?(followed) do Websub.subscribe(follower, followed) end - following = [ap_followers | follower.following] - |> Enum.uniq + following = + [ap_followers | follower.following] + |> Enum.uniq() - follower = follower - |> follow_changeset(%{following: following}) - |> update_and_set_cache + follower = + follower + |> follow_changeset(%{following: following}) + |> update_and_set_cache {:ok, _} = update_follower_count(followed) @@ -183,13 +193,16 @@ def follow(%User{} = follower, %User{info: info} = followed) do def unfollow(%User{} = follower, %User{} = followed) do ap_followers = followed.follower_address - if following?(follower, followed) and follower.ap_id != followed.ap_id do - following = follower.following - |> List.delete(ap_followers) - { :ok, follower } = follower - |> follow_changeset(%{following: following}) - |> update_and_set_cache + if following?(follower, followed) and follower.ap_id != followed.ap_id do + following = + follower.following + |> List.delete(ap_followers) + + {:ok, follower} = + follower + |> follow_changeset(%{following: following}) + |> update_and_set_cache {:ok, followed} = update_follower_count(followed) @@ -225,12 +238,12 @@ def invalidate_cache(user) do def get_cached_by_ap_id(ap_id) do key = "ap_id:#{ap_id}" - Cachex.get!(:user_cache, key, fallback: fn(_) -> get_by_ap_id(ap_id) end) + Cachex.get!(:user_cache, key, fallback: fn _ -> get_by_ap_id(ap_id) end) end def get_cached_by_nickname(nickname) do key = "nickname:#{nickname}" - Cachex.get!(:user_cache, key, fallback: fn(_) -> get_or_fetch_by_nickname(nickname) end) + Cachex.get!(:user_cache, key, fallback: fn _ -> get_or_fetch_by_nickname(nickname) end) end def get_by_nickname(nickname) do @@ -239,7 +252,7 @@ def get_by_nickname(nickname) do def get_cached_user_info(user) do key = "user_info:#{user.id}" - Cachex.get!(:user_cache, key, fallback: fn(_) -> user_info(user) end) + Cachex.get!(:user_cache, key, fallback: fn _ -> user_info(user) end) end def fetch_by_nickname(nickname) do @@ -252,29 +265,37 @@ def fetch_by_nickname(nickname) do end def get_or_fetch_by_nickname(nickname) do - with %User{} = user <- get_by_nickname(nickname) do + with %User{} = user <- get_by_nickname(nickname) do user - else _e -> - with [_nick, _domain] <- String.split(nickname, "@"), - {:ok, user} <- fetch_by_nickname(nickname) do - user - else _e -> nil - end + else + _e -> + with [_nick, _domain] <- String.split(nickname, "@"), + {:ok, user} <- fetch_by_nickname(nickname) do + user + else + _e -> nil + end end end def get_followers(%User{id: id, follower_address: follower_address}) do - q = from u in User, - where: fragment("? <@ ?", ^[follower_address], u.following), - where: u.id != ^id + q = + from( + u in User, + where: fragment("? <@ ?", ^[follower_address], u.following), + where: u.id != ^id + ) {:ok, Repo.all(q)} end def get_friends(%User{id: id, following: following}) do - q = from u in User, - where: u.follower_address in ^following, - where: u.id != ^id + q = + from( + u in User, + where: u.follower_address in ^following, + where: u.id != ^id + ) {:ok, Repo.all(q)} end @@ -289,9 +310,12 @@ def increase_note_count(%User{} = user) do end def update_note_count(%User{} = user) do - note_count_query = from a in Object, - where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data), - select: count(a.id) + note_count_query = + from( + a in Object, + where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data), + select: count(a.id) + ) note_count = Repo.one(note_count_query) @@ -303,10 +327,13 @@ def update_note_count(%User{} = user) do end def update_follower_count(%User{} = user) do - follower_count_query = from u in User, - where: ^user.follower_address in u.following, - where: u.id != ^user.id, - select: count(u.id) + follower_count_query = + from( + u in User, + where: ^user.follower_address in u.following, + where: u.id != ^user.id, + select: count(u.id) + ) follower_count = Repo.one(follower_count_query) @@ -318,20 +345,25 @@ def update_follower_count(%User{} = user) do end def get_notified_from_activity(%Activity{recipients: to}) do - query = from u in User, - where: u.ap_id in ^to, - where: u.local == true + query = + from( + u in User, + where: u.ap_id in ^to, + where: u.local == true + ) Repo.all(query) end def get_recipients_from_activity(%Activity{recipients: to}) do - query = from u in User, - where: u.ap_id in ^to, - or_where: fragment("? && ?", u.following, ^to) + query = + from( + u in User, + where: u.ap_id in ^to, + or_where: fragment("? && ?", u.following, ^to) + ) - query = from u in query, - where: u.local == true + query = from(u in query, where: u.local == true) Repo.all(query) end @@ -340,9 +372,20 @@ def search(query, resolve) do if resolve do User.get_or_fetch_by_nickname(query) end - q = from u in User, - where: fragment("(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", u.nickname, u.name, ^query), - limit: 20 + + q = + from( + u in User, + where: + fragment( + "(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", + u.nickname, + u.name, + ^query + ), + limit: 20 + ) + Repo.all(q) end @@ -370,36 +413,40 @@ def blocks?(user, %{ap_id: ap_id}) do end def local_user_query() do - from u in User, - where: u.local == true + from(u in User, where: u.local == true) end - def deactivate (%User{} = user) do + def deactivate(%User{} = user) do new_info = Map.put(user.info, "deactivated", true) cs = User.info_changeset(user, %{info: new_info}) update_and_set_cache(cs) end - def delete (%User{} = user) do + def delete(%User{} = user) do {:ok, user} = User.deactivate(user) # Remove all relationships - {:ok, followers } = User.get_followers(user) + {:ok, followers} = User.get_followers(user) + followers - |> Enum.each(fn (follower) -> User.unfollow(follower, user) end) + |> Enum.each(fn follower -> User.unfollow(follower, user) end) {:ok, friends} = User.get_friends(user) - friends - |> Enum.each(fn (followed) -> User.unfollow(user, followed) end) - query = from a in Activity, - where: a.actor == ^user.ap_id + friends + |> Enum.each(fn followed -> User.unfollow(user, followed) end) + + query = from(a in Activity, where: a.actor == ^user.ap_id) Repo.all(query) - |> Enum.each(fn (activity) -> + |> Enum.each(fn activity -> case activity.data["type"] do - "Create" -> ActivityPub.delete(Object.get_by_ap_id(activity.data["object"]["id"])) - _ -> "Doing nothing" # TODO: Do something with likes, follows, repeats. + "Create" -> + ActivityPub.delete(Object.get_by_ap_id(activity.data["object"]["id"])) + + # TODO: Do something with likes, follows, repeats. + _ -> + "Doing nothing" end end) @@ -413,7 +460,9 @@ def get_or_fetch_by_ap_id(ap_id) do ap_try = ActivityPub.make_user_from_ap_id(ap_id) case ap_try do - {:ok, user} -> user + {:ok, user} -> + user + _ -> case OStatus.make_user(ap_id) do {:ok, user} -> user @@ -424,12 +473,15 @@ def get_or_fetch_by_ap_id(ap_id) do end # AP style - def public_key_from_info(%{"source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do - key = :public_key.pem_decode(public_key_pem) - |> hd() - |> :public_key.pem_entry_decode() + def public_key_from_info(%{ + "source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}} + }) do + key = + :public_key.pem_decode(public_key_pem) + |> hd() + |> :public_key.pem_entry_decode() - {:ok, key} + {:ok, key} end # OStatus Magic Key @@ -450,8 +502,10 @@ defp blank?(""), do: nil defp blank?(n), do: n def insert_or_update_user(data) do - data = data - |> Map.put(:name, blank?(data[:name]) || data[:nickname]) + data = + data + |> Map.put(:name, blank?(data[:name]) || data[:nickname]) + cs = User.remote_user_creation(data) Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname) end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 3a914088f..04b50c1cc 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -18,7 +18,14 @@ def insert(map, local \\ true) when is_map(map) do with nil <- Activity.get_by_ap_id(map["id"]), map <- lazy_put_activity_defaults(map), :ok <- insert_full_object(map) do - {:ok, activity} = Repo.insert(%Activity{data: map, local: local, actor: map["actor"], recipients: get_recipients(map)}) + {:ok, activity} = + Repo.insert(%Activity{ + data: map, + local: local, + actor: map["actor"], + recipients: get_recipients(map) + }) + Notification.create_notifications(activity) stream_out(activity) {:ok, activity} @@ -31,8 +38,10 @@ def insert(map, local \\ true) when is_map(map) do def stream_out(activity) do if activity.data["type"] in ["Create", "Announce"] do Pleroma.Web.Streamer.stream("user", activity) + if Enum.member?(activity.data["to"], "https://www.w3.org/ns/activitystreams#Public") do Pleroma.Web.Streamer.stream("public", activity) + if activity.local do Pleroma.Web.Streamer.stream("public:local", activity) end @@ -42,10 +51,15 @@ def stream_out(activity) do def create(%{to: to, actor: actor, context: context, object: object} = params) do additional = params[:additional] || %{} - local = !(params[:local] == false) # only accept false as false value + # only accept false as false value + local = !(params[:local] == false) published = params[:published] - with create_data <- make_create_data(%{to: to, actor: actor, published: published, context: context, object: object}, additional), + with create_data <- + make_create_data( + %{to: to, actor: actor, published: published, context: context, object: object}, + additional + ), {:ok, activity} <- insert(create_data, local), :ok <- maybe_federate(activity) do {:ok, activity} @@ -53,7 +67,8 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d end def accept(%{to: to, actor: actor, object: object} = params) do - local = !(params[:local] == false) # only accept false as false value + # only accept false as false value + local = !(params[:local] == false) with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object}, {:ok, activity} <- insert(data, local), @@ -63,9 +78,16 @@ def accept(%{to: to, actor: actor, object: object} = params) do end def update(%{to: to, cc: cc, actor: actor, object: object} = params) do - local = !(params[:local] == false) # only accept false as false value + # only accept false as false value + local = !(params[:local] == false) - with data <- %{"to" => to, "cc" => cc, "type" => "Update", "actor" => actor, "object" => object}, + with data <- %{ + "to" => to, + "cc" => cc, + "type" => "Update", + "actor" => actor, + "object" => object + }, {:ok, activity} <- insert(data, local), :ok <- maybe_federate(activity) do {:ok, activity} @@ -73,7 +95,12 @@ def update(%{to: to, cc: cc, actor: actor, object: object} = params) do end # TODO: This is weird, maybe we shouldn't check here if we can make the activity. - def like(%User{ap_id: ap_id} = user, %Object{data: %{"id" => _}} = object, activity_id \\ nil, local \\ true) do + def like( + %User{ap_id: ap_id} = user, + %Object{data: %{"id" => _}} = object, + activity_id \\ nil, + local \\ true + ) do with nil <- get_existing_like(ap_id, object), like_data <- make_like_data(user, object, activity_id), {:ok, activity} <- insert(like_data, local), @@ -91,11 +118,17 @@ def unlike(%User{} = actor, %Object{} = object) do {:ok, _activity} <- Repo.delete(activity), {:ok, object} <- remove_like_from_object(activity, object) do {:ok, object} - else _e -> {:ok, object} + else + _e -> {:ok, object} end end - def announce(%User{ap_id: _} = user, %Object{data: %{"id" => _}} = object, activity_id \\ nil, local \\ true) do + def announce( + %User{ap_id: _} = user, + %Object{data: %{"id" => _}} = object, + activity_id \\ nil, + local \\ true + ) do with true <- is_public?(object), announce_data <- make_announce_data(user, object, activity_id), {:ok, activity} <- insert(announce_data, local), @@ -119,19 +152,22 @@ def unfollow(follower, followed, local \\ true) do with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed), unfollow_data <- make_unfollow_data(follower, followed, follow_activity), {:ok, activity} <- insert(unfollow_data, local), - :ok, maybe_federate(activity) do + :ok, + maybe_federate(activity) do {:ok, activity} end end def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do user = User.get_cached_by_ap_id(actor) + data = %{ "type" => "Delete", "actor" => actor, "object" => id, "to" => [user.follower_address, "https://www.w3.org/ns/activitystreams#Public"] } + with Repo.delete(object), Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)), {:ok, activity} <- insert(data, local), @@ -142,112 +178,147 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ tru def fetch_activities_for_context(context, opts \\ %{}) do public = ["https://www.w3.org/ns/activitystreams#Public"] - recipients = if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public - query = from activity in Activity - query = query + recipients = + if opts["user"], do: [opts["user"].ap_id | opts["user"].following] ++ public, else: public + + query = from(activity in Activity) + + query = + query |> restrict_blocked(opts) |> restrict_recipients(recipients, opts["user"]) - query = from activity in query, - where: fragment("?->>'type' = ? and ?->>'context' = ?", activity.data, "Create", activity.data, ^context), - order_by: [desc: :id] + query = + from( + activity in query, + where: + fragment( + "?->>'type' = ? and ?->>'context' = ?", + activity.data, + "Create", + activity.data, + ^context + ), + order_by: [desc: :id] + ) + Repo.all(query) end # TODO: Make this work properly with unlisted. def fetch_public_activities(opts \\ %{}) do q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts) + q - |> Repo.all - |> Enum.reverse + |> Repo.all() + |> Enum.reverse() end defp restrict_since(query, %{"since_id" => since_id}) do - from activity in query, where: activity.id > ^since_id + from(activity in query, where: activity.id > ^since_id) end + defp restrict_since(query, _), do: query defp restrict_tag(query, %{"tag" => tag}) do - from activity in query, + from( + activity in query, where: fragment("? <@ (? #> '{\"object\",\"tag\"}')", ^tag, activity.data) + ) end + defp restrict_tag(query, _), do: query defp restrict_recipients(query, [], user), do: query + defp restrict_recipients(query, recipients, nil) do - from activity in query, - where: fragment("? && ?", ^recipients, activity.recipients) + from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) end + defp restrict_recipients(query, recipients, user) do - from activity in query, + from( + activity in query, where: fragment("? && ?", ^recipients, activity.recipients), or_where: activity.actor == ^user.ap_id + ) end defp restrict_limit(query, %{"limit" => limit}) do - from activity in query, - limit: ^limit + from(activity in query, limit: ^limit) end + defp restrict_limit(query, _), do: query defp restrict_local(query, %{"local_only" => true}) do - from activity in query, where: activity.local == true + from(activity in query, where: activity.local == true) end + defp restrict_local(query, _), do: query defp restrict_max(query, %{"max_id" => max_id}) do - from activity in query, where: activity.id < ^max_id + from(activity in query, where: activity.id < ^max_id) end + defp restrict_max(query, _), do: query defp restrict_actor(query, %{"actor_id" => actor_id}) do - from activity in query, - where: activity.actor == ^actor_id + from(activity in query, where: activity.actor == ^actor_id) end + defp restrict_actor(query, _), do: query defp restrict_type(query, %{"type" => type}) when is_binary(type) do restrict_type(query, %{"type" => [type]}) end + defp restrict_type(query, %{"type" => type}) do - from activity in query, - where: fragment("?->>'type' = ANY(?)", activity.data, ^type) + from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type)) end + defp restrict_type(query, _), do: query defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do - from activity in query, + from( + activity in query, where: fragment("? <@ (? #> '{\"object\",\"likes\"}')", ^ap_id, activity.data) + ) end + defp restrict_favorited_by(query, _), do: query defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do - from activity in query, + from( + activity in query, where: fragment("not (? #> '{\"object\",\"attachment\"}' = ?)", activity.data, ^[]) + ) end + defp restrict_media(query, _), do: query # Only search through last 100_000 activities by default defp restrict_recent(query, %{"whole_db" => true}), do: query + defp restrict_recent(query, _) do since = (Repo.aggregate(Activity, :max, :id) || 0) - 100_000 - from activity in query, - where: activity.id > ^since + from(activity in query, where: activity.id > ^since) end defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do blocks = info["blocks"] || [] - from activity in query, - where: fragment("not (? = ANY(?))", activity.actor, ^blocks) + from(activity in query, where: fragment("not (? = ANY(?))", activity.actor, ^blocks)) end + defp restrict_blocked(query, _), do: query def fetch_activities_query(recipients, opts \\ %{}) do - base_query = from activity in Activity, - limit: 20, - order_by: [fragment("? desc nulls last", activity.id)] + base_query = + from( + activity in Activity, + limit: 20, + order_by: [fragment("? desc nulls last", activity.id)] + ) base_query |> restrict_recipients(recipients, opts["user"]) @@ -266,8 +337,8 @@ def fetch_activities_query(recipients, opts \\ %{}) do def fetch_activities(recipients, opts \\ %{}) do fetch_activities_query(recipients, opts) - |> Repo.all - |> Enum.reverse + |> Repo.all() + |> Enum.reverse() end def upload(file) do @@ -276,15 +347,19 @@ def upload(file) do end def user_data_from_user_object(data) do - avatar = data["icon"]["url"] && %{ - "type" => "Image", - "url" => [%{"href" => data["icon"]["url"]}] - } + avatar = + data["icon"]["url"] && + %{ + "type" => "Image", + "url" => [%{"href" => data["icon"]["url"]}] + } - banner = data["image"]["url"] && %{ - "type" => "Image", - "url" => [%{"href" => data["image"]["url"]}] - } + banner = + data["image"]["url"] && + %{ + "type" => "Image", + "url" => [%{"href" => data["image"]["url"]}] + } user_data = %{ ap_id: data["id"], @@ -304,8 +379,9 @@ def user_data_from_user_object(data) do end def fetch_and_prepare_user_from_ap_id(ap_id) do - with {:ok, %{status_code: 200, body: body}} <- @httpoison.get(ap_id, ["Accept": "application/activity+json"]), - {:ok, data} <- Jason.decode(body) do + with {:ok, %{status_code: 200, body: body}} <- + @httpoison.get(ap_id, Accept: "application/activity+json"), + {:ok, data} <- Jason.decode(body) do user_data_from_user_object(data) else e -> Logger.error("Could not decode user at fetch #{ap_id}, #{inspect(e)}") @@ -333,32 +409,48 @@ def make_user_from_nickname(nickname) do end def publish(actor, activity) do - followers = if actor.follower_address in activity.recipients do - {:ok, followers} = User.get_followers(actor) - followers |> Enum.filter(&(!&1.local)) - else - [] - end + followers = + if actor.follower_address in activity.recipients do + {:ok, followers} = User.get_followers(actor) + followers |> Enum.filter(&(!&1.local)) + else + [] + end - remote_inboxes = (Pleroma.Web.Salmon.remote_users(activity) ++ followers) - |> Enum.filter(fn (user) -> User.ap_enabled?(user) end) - |> Enum.map(fn (%{info: %{"source_data" => data}}) -> - (data["endpoints"] && data["endpoints"]["sharedInbox"]) || data["inbox"] - end) - |> Enum.uniq + remote_inboxes = + (Pleroma.Web.Salmon.remote_users(activity) ++ followers) + |> Enum.filter(fn user -> User.ap_enabled?(user) end) + |> Enum.map(fn %{info: %{"source_data" => data}} -> + (data["endpoints"] && data["endpoints"]["sharedInbox"]) || data["inbox"] + end) + |> Enum.uniq() {:ok, data} = Transmogrifier.prepare_outgoing(activity.data) json = Jason.encode!(data) - Enum.each remote_inboxes, fn(inbox) -> - Federator.enqueue(:publish_single_ap, %{inbox: inbox, json: json, actor: actor, id: activity.data["id"]}) - end + + Enum.each(remote_inboxes, fn inbox -> + Federator.enqueue(:publish_single_ap, %{ + inbox: inbox, + json: json, + actor: actor, + id: activity.data["id"] + }) + end) end def publish_one(%{inbox: inbox, json: json, actor: actor, id: id}) do Logger.info("Federating #{id} to #{inbox}") host = URI.parse(inbox).host - signature = Pleroma.Web.HTTPSignatures.sign(actor, %{host: host, "content-length": byte_size(json)}) - @httpoison.post(inbox, json, [{"Content-Type", "application/activity+json"}, {"signature", signature}], hackney: [pool: :default]) + + signature = + Pleroma.Web.HTTPSignatures.sign(actor, %{host: host, "content-length": byte_size(json)}) + + @httpoison.post( + inbox, + json, + [{"Content-Type", "application/activity+json"}, {"signature", signature}], + hackney: [pool: :default] + ) end # TODO: @@ -368,17 +460,34 @@ def fetch_object_from_id(id) do {:ok, object} else Logger.info("Fetching #{id} via AP") + with true <- String.starts_with?(id, "http"), - {:ok, %{body: body, status_code: code}} when code in 200..299 <- @httpoison.get(id, [Accept: "application/activity+json"], follow_redirect: true, timeout: 10000, recv_timeout: 20000), + {:ok, %{body: body, status_code: code}} when code in 200..299 <- + @httpoison.get( + id, + [Accept: "application/activity+json"], + follow_redirect: true, + timeout: 10000, + recv_timeout: 20000 + ), {:ok, data} <- Jason.decode(body), nil <- Object.get_by_ap_id(data["id"]), - params <- %{"type" => "Create", "to" => data["to"], "cc" => data["cc"], "actor" => data["attributedTo"], "object" => data}, + params <- %{ + "type" => "Create", + "to" => data["to"], + "cc" => data["cc"], + "actor" => data["attributedTo"], + "object" => data + }, {:ok, activity} <- Transmogrifier.handle_incoming(params) do {:ok, Object.get_by_ap_id(activity.data["object"]["id"])} else - object = %Object{} -> {:ok, object} + object = %Object{} -> + {:ok, object} + e -> Logger.info("Couldn't get object via AP, trying out OStatus fetching...") + case OStatus.fetch_activity_from_url(id) do {:ok, [activity | _]} -> {:ok, Object.get_by_ap_id(activity.data["object"]["id"])} e -> e @@ -388,15 +497,17 @@ def fetch_object_from_id(id) do end def is_public?(activity) do - "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++ (activity.data["cc"] || [])) + "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++ + (activity.data["cc"] || [])) end def visible_for_user?(activity, nil) do is_public?(activity) end + def visible_for_user?(activity, user) do x = [user.ap_id | user.following] - y = (activity.data["to"] ++ (activity.data["cc"] || [])) + y = activity.data["to"] ++ (activity.data["cc"] || []) visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y)) end end diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 47a8bf5ab..12f61f5f0 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do require Logger - action_fallback :errors + action_fallback(:errors) def user(conn, %{"nickname" => nickname}) do with %User{} = user <- User.get_cached_by_nickname(nickname), @@ -31,6 +31,7 @@ def following(conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {page, _} = Integer.parse(page) + conn |> put_resp_header("content-type", "application/activity+json") |> json(UserView.render("following.json", %{user: user, page: page})) @@ -50,6 +51,7 @@ def followers(conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {page, _} = Integer.parse(page) + conn |> put_resp_header("content-type", "application/activity+json") |> json(UserView.render("followers.json", %{user: user, page: page})) @@ -74,7 +76,9 @@ def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do end end - def outbox(conn, %{"nickname" => nickname}) do outbox(conn, %{"nickname" => nickname, "max_id" => nil}) end + def outbox(conn, %{"nickname" => nickname}) do + outbox(conn, %{"nickname" => nickname, "max_id" => nil}) + end # TODO: Ensure that this inbox is a recipient of the message def inbox(%{assigns: %{valid_signature: true}} = conn, params) do @@ -84,7 +88,8 @@ def inbox(%{assigns: %{valid_signature: true}} = conn, params) do def inbox(conn, params) do headers = Enum.into(conn.req_headers, %{}) - if !(String.contains?(headers["signature"] || "", params["actor"])) do + + if !String.contains?(headers["signature"] || "", params["actor"]) do Logger.info("Signature not from author, relayed message, fetching from source") ActivityPub.fetch_object_from_id(params["object"]["id"]) else diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 6561b8d76..00b9f74ff 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -25,21 +25,25 @@ def fix_object(object) do |> fix_tag end - def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object) when not is_nil(in_reply_to_id) do + def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object) + when not is_nil(in_reply_to_id) do case ActivityPub.fetch_object_from_id(in_reply_to_id) do {:ok, replied_object} -> activity = Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) + object |> Map.put("inReplyTo", replied_object.data["id"]) |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id) |> Map.put("inReplyToStatusId", activity.id) |> Map.put("conversation", replied_object.data["context"] || object["conversation"]) |> Map.put("context", replied_object.data["context"] || object["conversation"]) + e -> Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}") object end end + def fix_in_reply_to(object), do: object def fix_context(object) do @@ -48,27 +52,32 @@ def fix_context(object) do end def fix_attachments(object) do - attachments = (object["attachment"] || []) - |> Enum.map(fn (data) -> - url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] - Map.put(data, "url", url) - end) + attachments = + (object["attachment"] || []) + |> Enum.map(fn data -> + url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] + Map.put(data, "url", url) + end) object |> Map.put("attachment", attachments) end def fix_emoji(object) do - tags = (object["tag"] || []) - emoji = tags |> Enum.filter(fn (data) -> data["type"] == "Emoji" and data["icon"] end) - emoji = emoji |> Enum.reduce(%{}, fn (data, mapping) -> - name = data["name"] - if String.starts_with?(name, ":") do - name = name |> String.slice(1..-2) - end + tags = object["tag"] || [] + emoji = tags |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end) - mapping |> Map.put(name, data["icon"]["url"]) - end) + emoji = + emoji + |> Enum.reduce(%{}, fn data, mapping -> + name = data["name"] + + if String.starts_with?(name, ":") do + name = name |> String.slice(1..-2) + end + + mapping |> Map.put(name, data["icon"]["url"]) + end) # we merge mastodon and pleroma emoji into a single mapping, to allow for both wire formats emoji = Map.merge(object["emoji"] || %{}, emoji) @@ -78,9 +87,10 @@ def fix_emoji(object) do end def fix_tag(object) do - tags = (object["tag"] || []) - |> Enum.filter(fn (data) -> data["type"] == "Hashtag" and data["name"] end) - |> Enum.map(fn (data) -> String.slice(data["name"], 1..-1) end) + tags = + (object["tag"] || []) + |> Enum.filter(fn data -> data["type"] == "Hashtag" and data["name"] end) + |> Enum.map(fn data -> String.slice(data["name"], 1..-1) end) combined = (object["tag"] || []) ++ tags @@ -103,13 +113,13 @@ def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = obje context: object["conversation"], local: false, published: data["published"], - additional: Map.take(data, [ - "cc", - "id" - ]) + additional: + Map.take(data, [ + "cc", + "id" + ]) } - ActivityPub.create(params) else %Activity{} = activity -> {:ok, activity} @@ -117,11 +127,14 @@ def handle_incoming(%{"type" => "Create", "object" => %{"type" => "Note"} = obje end end - def handle_incoming(%{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data) do + def handle_incoming( + %{"type" => "Follow", "object" => followed, "actor" => follower, "id" => id} = data + ) do with %User{local: true} = followed <- User.get_cached_by_ap_id(followed), %User{} = follower <- User.get_or_fetch_by_ap_id(follower), {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true}) + User.follow(follower, followed) {:ok, activity} else @@ -129,7 +142,9 @@ def handle_incoming(%{"type" => "Follow", "object" => followed, "actor" => follo end end - def handle_incoming(%{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data) do + def handle_incoming( + %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data + ) do with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), {:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do @@ -139,7 +154,9 @@ def handle_incoming(%{"type" => "Like", "object" => object_id, "actor" => actor, end end - def handle_incoming(%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data) do + def handle_incoming( + %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data + ) do with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), {:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do @@ -149,20 +166,31 @@ def handle_incoming(%{"type" => "Announce", "object" => object_id, "actor" => ac end end - def handle_incoming(%{"type" => "Update", "object" => %{"type" => "Person"} = object, "actor" => actor_id} = data) do + def handle_incoming( + %{"type" => "Update", "object" => %{"type" => "Person"} = object, "actor" => actor_id} = + data + ) do with %User{ap_id: ^actor_id} = actor <- User.get_by_ap_id(object["id"]) do {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object) banner = new_user_data[:info]["banner"] - update_data = new_user_data - |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner})) + + update_data = + new_user_data + |> Map.take([:name, :bio, :avatar]) + |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner})) actor |> User.upgrade_changeset(update_data) |> User.update_and_set_cache() - ActivityPub.update(%{local: false, to: data["to"] || [], cc: data["cc"] || [], object: object, actor: actor_id}) + ActivityPub.update(%{ + local: false, + to: data["to"] || [], + cc: data["cc"] || [], + object: object, + actor: actor_id + }) else e -> Logger.error(e) @@ -171,11 +199,15 @@ def handle_incoming(%{"type" => "Update", "object" => %{"type" => "Person"} = ob end # TODO: Make secure. - def handle_incoming(%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data) do - object_id = case object_id do - %{"id" => id} -> id - id -> id - end + def handle_incoming( + %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data + ) do + object_id = + case object_id do + %{"id" => id} -> id + id -> id + end + with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), {:ok, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), {:ok, activity} <- ActivityPub.delete(object, false) do @@ -203,6 +235,7 @@ def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) do _e -> object end end + def set_reply_to_uri(obj), do: obj # Prepares the object of an outgoing create activity. @@ -222,20 +255,25 @@ def prepare_object(object) do """ internal -> Mastodon """ + def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do - object = object - |> prepare_object - data = data - |> Map.put("object", object) - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + object = + object + |> prepare_object + + data = + data + |> Map.put("object", object) + |> Map.put("@context", "https://www.w3.org/ns/activitystreams") {:ok, data} end def prepare_outgoing(%{"type" => type} = data) do - data = data - |> maybe_fix_object_url - |> Map.put("@context", "https://www.w3.org/ns/activitystreams") + data = + data + |> maybe_fix_object_url + |> Map.put("@context", "https://www.w3.org/ns/activitystreams") {:ok, data} end @@ -245,11 +283,13 @@ def maybe_fix_object_url(data) do case ActivityPub.fetch_object_from_id(data["object"]) do {:ok, relative_object} -> if relative_object.data["external_url"] do - data = data - |> Map.put("object", relative_object.data["external_url"]) + data = + data + |> Map.put("object", relative_object.data["external_url"]) else data end + e -> Logger.error("Couldn't fetch #{data["object"]} #{inspect(e)}") data @@ -260,8 +300,15 @@ def maybe_fix_object_url(data) do end def add_hashtags(object) do - tags = (object["tag"] || []) - |> Enum.map fn (tag) -> %{"href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}", "name" => "##{tag}", "type" => "Hashtag"} end + tags = + (object["tag"] || []) + |> Enum.map(fn tag -> + %{ + "href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}", + "name" => "##{tag}", + "type" => "Hashtag" + } + end) object |> Map.put("tag", tags) @@ -269,10 +316,14 @@ def add_hashtags(object) do def add_mention_tags(object) do recipients = object["to"] ++ (object["cc"] || []) - mentions = recipients - |> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn(user) -> %{"type" => "Mention", "href" => user.ap_id, "name" => "@#{user.nickname}"} end) + + mentions = + recipients + |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) + |> Enum.filter(& &1) + |> Enum.map(fn user -> + %{"type" => "Mention", "href" => user.ap_id, "name" => "@#{user.nickname}"} + end) tags = object["tag"] || [] @@ -284,13 +335,18 @@ def add_mention_tags(object) do def add_emoji_tags(object) do tags = object["tag"] || [] emoji = object["emoji"] || [] - out = emoji |> Enum.map(fn {name, url} -> - %{"icon" => %{"url" => url, "type" => "Image"}, - "name" => ":" <> name <> ":", - "type" => "Emoji", - "updated" => "1970-01-01T00:00:00Z", - "id" => url} - end) + + out = + emoji + |> Enum.map(fn {name, url} -> + %{ + "icon" => %{"url" => url, "type" => "Image"}, + "name" => ":" <> name <> ":", + "type" => "Emoji", + "updated" => "1970-01-01T00:00:00Z", + "id" => url + } + end) object |> Map.put("tag", tags ++ out) @@ -313,11 +369,12 @@ def add_attributed_to(object) do end def prepare_attachments(object) do - attachments = (object["attachment"] || []) - |> Enum.map(fn (data) -> - [%{"mediaType" => media_type, "href" => href} | _] = data["url"] - %{"url" => href, "mediaType" => media_type, "name" => data["name"], "type" => "Document"} - end) + attachments = + (object["attachment"] || []) + |> Enum.map(fn data -> + [%{"mediaType" => media_type, "href" => href} | _] = data["url"] + %{"url" => href, "mediaType" => media_type, "name" => data["name"], "type" => "Document"} + end) object |> Map.put("attachment", attachments) @@ -325,9 +382,24 @@ def prepare_attachments(object) do defp user_upgrade_task(user) do old_follower_address = User.ap_followers(user) - q = from u in User, - where: ^old_follower_address in u.following, - update: [set: [following: fragment("array_replace(?,?,?)", u.following, ^old_follower_address, ^user.follower_address)]] + + q = + from( + u in User, + where: ^old_follower_address in u.following, + update: [ + set: [ + following: + fragment( + "array_replace(?,?,?)", + u.following, + ^old_follower_address, + ^user.follower_address + ) + ] + ] + ) + Repo.update_all(q, []) maybe_retire_websub(user.ap_id) @@ -335,22 +407,40 @@ defp user_upgrade_task(user) do # Only do this for recent activties, don't go through the whole db. # Only look at the last 1000 activities. since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000 - q = from a in Activity, - where: ^old_follower_address in a.recipients, - where: a.id > ^since, - update: [set: [recipients: fragment("array_replace(?,?,?)", a.recipients, ^old_follower_address, ^user.follower_address)]] + + q = + from( + a in Activity, + where: ^old_follower_address in a.recipients, + where: a.id > ^since, + update: [ + set: [ + recipients: + fragment( + "array_replace(?,?,?)", + a.recipients, + ^old_follower_address, + ^user.follower_address + ) + ] + ] + ) + Repo.update_all(q, []) end def upgrade_user_from_ap_id(ap_id, async \\ true) do with %User{local: false} = user <- User.get_by_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do - data = data - |> Map.put(:info, Map.merge(user.info, data[:info])) + data = + data + |> Map.put(:info, Map.merge(user.info, data[:info])) already_ap = User.ap_enabled?(user) - {:ok, user} = User.upgrade_changeset(user, data) - |> Repo.update() + + {:ok, user} = + User.upgrade_changeset(user, data) + |> Repo.update() if !already_ap do # This could potentially take a long time, do it in the background @@ -371,9 +461,13 @@ def upgrade_user_from_ap_id(ap_id, async \\ true) do def maybe_retire_websub(ap_id) do # some sanity checks - if is_binary(ap_id) && (String.length(ap_id) > 8) do - q = from ws in Pleroma.Web.Websub.WebsubClientSubscription, - where: fragment("? like ?", ws.topic, ^"#{ap_id}%") + if is_binary(ap_id) && String.length(ap_id) > 8 do + q = + from( + ws in Pleroma.Web.Websub.WebsubClientSubscription, + where: fragment("? like ?", ws.topic, ^"#{ap_id}%") + ) + Repo.delete_all(q) end end diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index a25b27aab..16a1d3e97 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -26,7 +26,7 @@ def make_json_ld_header do end def make_date do - DateTime.utc_now() |> DateTime.to_iso8601 + DateTime.utc_now() |> DateTime.to_iso8601() end def generate_activity_id do @@ -38,25 +38,28 @@ def generate_context_id do end def generate_object_id do - Helpers.o_status_url(Endpoint, :object, UUID.generate) + Helpers.o_status_url(Endpoint, :object, UUID.generate()) end def generate_id(type) do - "#{Web.base_url()}/#{type}/#{UUID.generate}" + "#{Web.base_url()}/#{type}/#{UUID.generate()}" end @doc """ Enqueues an activity for federation if it's local """ def maybe_federate(%Activity{local: true} = activity) do - priority = case activity.data["type"] do - "Delete" -> 10 - "Create" -> 1 - _ -> 5 - end + priority = + case activity.data["type"] do + "Delete" -> 10 + "Create" -> 1 + _ -> 5 + end + Pleroma.Web.Federator.enqueue(:publish, activity, priority) :ok end + def maybe_federate(_), do: :ok @doc """ @@ -64,9 +67,10 @@ def maybe_federate(_), do: :ok also adds it to an included object """ def lazy_put_activity_defaults(map) do - map = map - |> Map.put_new_lazy("id", &generate_activity_id/0) - |> Map.put_new_lazy("published", &make_date/0) + map = + map + |> Map.put_new_lazy("id", &generate_activity_id/0) + |> Map.put_new_lazy("published", &make_date/0) if is_map(map["object"]) do object = lazy_put_object_defaults(map["object"]) @@ -88,11 +92,13 @@ def lazy_put_object_defaults(map) do @doc """ Inserts a full object if it is contained in an activity. """ - def insert_full_object(%{"object" => %{"type" => type} = object_data}) when is_map(object_data) and type in ["Note"] do + def insert_full_object(%{"object" => %{"type" => type} = object_data}) + when is_map(object_data) and type in ["Note"] do with {:ok, _} <- Object.create(object_data) do :ok end end + def insert_full_object(_), do: :ok def update_object_in_activities(%{data: %{"id" => id}} = object) do @@ -101,7 +107,8 @@ def update_object_in_activities(%{data: %{"id" => id}} = object) do # Alternatively, just don't do this and fetch the current object each time. Most # could probably be taken from cache. relevant_activities = Activity.all_by_object_ap_id(id) - Enum.map(relevant_activities, fn (activity) -> + + Enum.map(relevant_activities, fn activity -> new_activity_data = activity.data |> Map.put("object", object.data) changeset = Changeset.change(activity, data: new_activity_data) Repo.update(changeset) @@ -114,11 +121,20 @@ def update_object_in_activities(%{data: %{"id" => id}} = object) do Returns an existing like if a user already liked an object """ def get_existing_like(actor, %{data: %{"id" => id}}) do - query = from activity in Activity, - where: fragment("(?)->>'actor' = ?", activity.data, ^actor), - # this is to use the index - where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^id), - where: fragment("(?)->>'type' = 'Like'", activity.data) + query = + from( + activity in Activity, + where: fragment("(?)->>'actor' = ?", activity.data, ^actor), + # this is to use the index + where: + fragment( + "coalesce((?)->'object'->>'id', (?)->>'object') = ?", + activity.data, + activity.data, + ^id + ), + where: fragment("(?)->>'type' = 'Like'", activity.data) + ) Repo.one(query) end @@ -137,10 +153,12 @@ def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, end def update_element_in_object(property, element, object) do - with new_data <- object.data |> Map.put("#{property}_count", length(element)) |> Map.put("#{property}s", element), + with new_data <- + object.data |> Map.put("#{property}_count", length(element)) + |> Map.put("#{property}s", element), changeset <- Changeset.change(object, data: new_data), {:ok, object} <- Repo.update(changeset), - _ <- update_object_in_activities(object) do + _ <- update_object_in_activities(object) do {:ok, object} end end @@ -150,7 +168,7 @@ def update_likes_in_object(likes, object) do end def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do - with likes <- [actor | (object.data["likes"] || [])] |> Enum.uniq do + with likes <- [actor | object.data["likes"] || []] |> Enum.uniq() do update_likes_in_object(likes, object) end end @@ -178,13 +196,20 @@ def make_follow_data(%User{ap_id: follower_id}, %User{ap_id: followed_id}, activ if activity_id, do: Map.put(data, "id", activity_id), else: data end - def fetch_latest_follow(%User{ap_id: follower_id}, - %User{ap_id: followed_id}) do - query = from activity in Activity, - where: fragment("? @> ?", activity.data, ^%{type: "Follow", actor: follower_id, - object: followed_id}), - order_by: [desc: :id], - limit: 1 + def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do + query = + from( + activity in Activity, + where: + fragment( + "? @> ?", + activity.data, + ^%{type: "Follow", actor: follower_id, object: followed_id} + ), + order_by: [desc: :id], + limit: 1 + ) + Repo.one(query) end @@ -193,7 +218,11 @@ def fetch_latest_follow(%User{ap_id: follower_id}, @doc """ Make announce activity data for the given actor and object """ - def make_announce_data(%User{ap_id: ap_id} = user, %Object{data: %{"id" => id}} = object, activity_id) do + def make_announce_data( + %User{ap_id: ap_id} = user, + %Object{data: %{"id" => id}} = object, + activity_id + ) do data = %{ "type" => "Announce", "actor" => ap_id, @@ -207,7 +236,7 @@ def make_announce_data(%User{ap_id: ap_id} = user, %Object{data: %{"id" => id}} end def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do - with announcements <- [actor | (object.data["announcements"] || [])] |> Enum.uniq do + with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do update_element_in_object("announcement", announcements, object) end end @@ -223,14 +252,14 @@ def make_unfollow_data(follower, followed, follow_activity) do } end - #### Create-related helpers def make_create_data(params, additional) do published = params.published || make_date() + %{ "type" => "Create", - "to" => params.to |> Enum.uniq, + "to" => params.to |> Enum.uniq(), "actor" => params.actor.ap_id, "object" => params.object, "published" => published, diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 9ff1f0c77..997d8308f 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -12,6 +12,7 @@ def render("user.json", %{user: user}) do {:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"]) public_key = :public_key.pem_entry_encode(:RSAPublicKey, public_key) public_key = :public_key.pem_encode([public_key]) + %{ "id" => user.ap_id, "type" => "Person", @@ -30,7 +31,7 @@ def render("user.json", %{user: user}) do "publicKeyPem" => public_key }, "endpoints" => %{ - "sharedInbox" => "#{Pleroma.Web.Endpoint.url}/inbox" + "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox" }, "icon" => %{ "type" => "Image", @@ -47,7 +48,8 @@ def render("user.json", %{user: user}) do def collection(collection, iri, page) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) - items = Enum.map(items, fn (user) -> user.ap_id end) + items = Enum.map(items, fn user -> user.ap_id end) + map = %{ "id" => "#{iri}?page=#{page}", "type" => "OrderedCollectionPage", @@ -55,19 +57,22 @@ def collection(collection, iri, page) do "totalItems" => length(collection), "orderedItems" => items } + if offset < length(collection) do - Map.put(map, "next", "#{iri}?page=#{page+1}") + Map.put(map, "next", "#{iri}?page=#{page + 1}") end end def render("following.json", %{user: user, page: page}) do {:ok, following} = User.get_friends(user) + collection(following, "#{user.ap_id}/following", page) |> Map.merge(Utils.make_json_ld_header()) end def render("following.json", %{user: user}) do {:ok, following} = User.get_friends(user) + %{ "id" => "#{user.ap_id}/following", "type" => "OrderedCollection", @@ -79,12 +84,14 @@ def render("following.json", %{user: user}) do def render("followers.json", %{user: user, page: page}) do {:ok, followers} = User.get_followers(user) + collection(followers, "#{user.ap_id}/followers", page) |> Map.merge(Utils.make_json_ld_header()) end def render("followers.json", %{user: user}) do {:ok, followers} = User.get_followers(user) + %{ "id" => "#{user.ap_id}/followers", "type" => "OrderedCollection", @@ -115,19 +122,21 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do activities = Enum.reverse(activities) max_id = Enum.at(activities, 0).id - collection = Enum.map(activities, fn (act) -> - {:ok, data} = Transmogrifier.prepare_outgoing(act.data) - data - end) + collection = + Enum.map(activities, fn act -> + {:ok, data} = Transmogrifier.prepare_outgoing(act.data) + data + end) iri = "#{user.ap_id}/outbox" + page = %{ "id" => "#{iri}?max_id=#{max_id}", "type" => "OrderedCollectionPage", "partOf" => iri, "totalItems" => info.note_count, "orderedItems" => collection, - "next" => "#{iri}?max_id=#{min_id-1}", + "next" => "#{iri}?max_id=#{min_id - 1}" } if max_qid == nil do diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex index f18b3cb80..fd8918a7d 100644 --- a/lib/pleroma/web/channels/user_socket.ex +++ b/lib/pleroma/web/channels/user_socket.ex @@ -6,11 +6,11 @@ defmodule Pleroma.Web.UserSocket do ## Channels # channel "room:*", Pleroma.Web.RoomChannel if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do - channel "chat:*", Pleroma.Web.ChatChannel + channel("chat:*", Pleroma.Web.ChatChannel) end ## Transports - transport :websocket, Phoenix.Transports.WebSocket + transport(:websocket, Phoenix.Transports.WebSocket) # transport :longpoll, Phoenix.Transports.LongPoll # Socket params are passed from the client and can diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/chat_channel.ex index ddc1d6f80..37eba8c3f 100644 --- a/lib/pleroma/web/chat_channel.ex +++ b/lib/pleroma/web/chat_channel.ex @@ -9,19 +9,21 @@ def join("chat:public", _message, socket) do end def handle_info(:after_join, socket) do - push socket, "messages", %{messages: ChatChannelState.messages()} + push(socket, "messages", %{messages: ChatChannelState.messages()}) {:noreply, socket} end def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do text = String.trim(text) + if String.length(text) > 0 do author = User.get_cached_by_nickname(user_name) author = Pleroma.Web.MastodonAPI.AccountView.render("account.json", user: author) message = ChatChannelState.add_message(%{text: text, author: author}) - broadcast! socket, "new_msg", message + broadcast!(socket, "new_msg", message) end + {:noreply, socket} end end @@ -43,6 +45,6 @@ def add_message(message) do end def messages() do - Agent.get(__MODULE__, fn state -> state[:messages] |> Enum.reverse end) + Agent.get(__MODULE__, fn state -> state[:messages] |> Enum.reverse() end) end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 5fc940261..21225c3b7 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.CommonAPI do def delete(activity_id, user) do with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id), %Object{} = object <- Object.get_by_ap_id(object_id), - true <- user.info["is_moderator"] || (user.ap_id == object.data["actor"]), + true <- user.info["is_moderator"] || user.ap_id == object.data["actor"], {:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} end @@ -46,17 +46,22 @@ def unfavorite(id_or_ap_id, user) do end end - def get_visibility(%{"visibility" => visibility}) when visibility in ~w{public unlisted private direct}, do: visibility + def get_visibility(%{"visibility" => visibility}) + when visibility in ~w{public unlisted private direct}, + do: visibility + def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(status_id) do inReplyTo = get_replied_to_activity(status_id) Pleroma.Web.MastodonAPI.StatusView.get_visibility(inReplyTo.data["object"]) end + def get_visibility(_), do: "public" @instance Application.get_env(:pleroma, :instance) @limit Keyword.get(@instance, :limit) def post(user, %{"status" => status} = data) do visibility = get_visibility(data) + with status <- String.trim(status), length when length in 1..@limit <- String.length(status), attachments <- attachments_from_ids(data["media_ids"]), @@ -64,18 +69,52 @@ def post(user, %{"status" => status} = data) do inReplyTo <- get_replied_to_activity(data["in_reply_to_status_id"]), {to, cc} <- to_for_user_and_mentions(user, mentions, inReplyTo, visibility), tags <- Formatter.parse_tags(status, data), - content_html <- make_content_html(status, mentions, attachments, tags, data["no_attachment_links"]), + content_html <- + make_content_html(status, mentions, attachments, tags, data["no_attachment_links"]), context <- make_context(inReplyTo), cw <- data["spoiler_text"], - object <- make_note_data(user.ap_id, to, context, content_html, attachments, inReplyTo, tags, cw, cc), - object <- Map.put(object, "emoji", Formatter.get_emoji(status) |> Enum.reduce(%{}, fn({name, file}, acc) -> Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url}#{file}") end)) do - res = ActivityPub.create(%{to: to, actor: user, context: context, object: object, additional: %{"cc" => cc}}) + object <- + make_note_data( + user.ap_id, + to, + context, + content_html, + attachments, + inReplyTo, + tags, + cw, + cc + ), + object <- + Map.put( + object, + "emoji", + Formatter.get_emoji(status) + |> Enum.reduce(%{}, fn {name, file}, acc -> + Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") + end) + ) do + res = + ActivityPub.create(%{ + to: to, + actor: user, + context: context, + object: object, + additional: %{"cc" => cc} + }) + User.increase_note_count(user) res end end def update(user) do - ActivityPub.update(%{local: true, to: [user.follower_address], cc: [], actor: user.ap_id, object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user})}) + ActivityPub.update(%{ + local: true, + to: [user.follower_address], + cc: [], + actor: user.ap_id, + object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) + }) end end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 3c09f0cc7..49c4ee1eb 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -6,6 +6,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do # This is a hack for twidere. def get_by_id_or_ap_id(id) do activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id) + if activity.data["type"] == "Create" do activity else @@ -16,10 +17,11 @@ def get_by_id_or_ap_id(id) do def get_replied_to_activity(id) when not is_nil(id) do Repo.get(Activity, id) end + def get_replied_to_activity(_), do: nil def attachments_from_ids(ids) do - Enum.map(ids || [], fn (media_id) -> + Enum.map(ids || [], fn media_id -> Repo.get(Object, media_id).data end) end @@ -27,8 +29,9 @@ def attachments_from_ids(ids) do def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do to = ["https://www.w3.org/ns/activitystreams#Public"] - mentioned_users = Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end) + mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) cc = [user.follower_address | mentioned_users] + if inReplyTo do {to, Enum.uniq([inReplyTo.data["actor"] | cc])} else @@ -47,7 +50,8 @@ def to_for_user_and_mentions(user, mentions, inReplyTo, "private") do end def to_for_user_and_mentions(user, mentions, inReplyTo, "direct") do - mentioned_users = Enum.map(mentions, fn ({_, %{ap_id: ap_id}}) -> ap_id end) + mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) + if inReplyTo do {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []} else @@ -62,55 +66,72 @@ def make_content_html(status, mentions, attachments, tags, no_attachment_links \ end def make_context(%Activity{data: %{"context" => context}}), do: context - def make_context(_), do: Utils.generate_context_id + def make_context(_), do: Utils.generate_context_id() def maybe_add_attachments(text, attachments, _no_links = true), do: text + def maybe_add_attachments(text, attachments, _no_links) do add_attachments(text, attachments) end + def add_attachments(text, attachments) do - attachment_text = Enum.map(attachments, fn - (%{"url" => [%{"href" => href} | _]}) -> - name = URI.decode(Path.basename(href)) - "#{shortname(name)}" - _ -> "" - end) + attachment_text = + Enum.map(attachments, fn + %{"url" => [%{"href" => href} | _]} -> + name = URI.decode(Path.basename(href)) + "#{shortname(name)}" + + _ -> + "" + end) + Enum.join([text | attachment_text], "
") end def format_input(text, mentions, tags) do text - |> Formatter.html_escape + |> Formatter.html_escape() |> String.replace("\n", "
") - |> (&({[], &1})).() - |> Formatter.add_links + |> (&{[], &1}).() + |> Formatter.add_links() |> Formatter.add_user_links(mentions) |> Formatter.add_hashtag_links(tags) - |> Formatter.finalize + |> Formatter.finalize() end def add_tag_links(text, tags) do - tags = tags - |> Enum.sort_by(fn ({tag, _}) -> -String.length(tag) end) + tags = + tags + |> Enum.sort_by(fn {tag, _} -> -String.length(tag) end) - Enum.reduce(tags, text, fn({full, tag}, text) -> - url = "#" + Enum.reduce(tags, text, fn {full, tag}, text -> + url = "#" String.replace(text, full, url) end) end - def make_note_data(actor, to, context, content_html, attachments, inReplyTo, tags, cw \\ nil, cc \\ []) do - object = %{ - "type" => "Note", - "to" => to, - "cc" => cc, - "content" => content_html, - "summary" => cw, - "context" => context, - "attachment" => attachments, - "actor" => actor, - "tag" => tags |> Enum.map(fn ({_, tag}) -> tag end) - } + def make_note_data( + actor, + to, + context, + content_html, + attachments, + inReplyTo, + tags, + cw \\ nil, + cc \\ [] + ) do + object = %{ + "type" => "Note", + "to" => to, + "cc" => cc, + "content" => content_html, + "summary" => cw, + "context" => context, + "attachment" => attachments, + "actor" => actor, + "tag" => tags |> Enum.map(fn {_, tag} -> tag end) + } if inReplyTo do object @@ -130,24 +151,25 @@ def format_asctime(date) do end def date_to_asctime(date) do - with {:ok, date, _offset} <- date |> DateTime.from_iso8601 do + with {:ok, date, _offset} <- date |> DateTime.from_iso8601() do format_asctime(date) - else _e -> + else + _e -> "" end end def to_masto_date(%NaiveDateTime{} = date) do date - |> NaiveDateTime.to_iso8601 + |> NaiveDateTime.to_iso8601() |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) end def to_masto_date(date) do try do date - |> NaiveDateTime.from_iso8601! - |> NaiveDateTime.to_iso8601 + |> NaiveDateTime.from_iso8601!() + |> NaiveDateTime.to_iso8601() |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) rescue _e -> "" diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index a2fb71451..0a2ac853e 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -2,47 +2,55 @@ defmodule Pleroma.Web.Endpoint do use Phoenix.Endpoint, otp_app: :pleroma if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do - socket "/socket", Pleroma.Web.UserSocket + socket("/socket", Pleroma.Web.UserSocket) end - socket "/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket + + socket("/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket) # Serve at "/" the static files from "priv/static" directory. # # You should set gzip to true if you are running phoenix.digest # when deploying your static files in production. - plug Plug.Static, - at: "/media", from: "uploads", gzip: false - plug Plug.Static, - at: "/", from: :pleroma, + plug(Plug.Static, at: "/media", from: "uploads", gzip: false) + + plug( + Plug.Static, + at: "/", + from: :pleroma, only: ~w(index.html static finmoji emoji packs sounds images instance sw.js) + ) # Code reloading can be explicitly enabled under the # :code_reloader configuration of your endpoint. if code_reloading? do - plug Phoenix.CodeReloader + plug(Phoenix.CodeReloader) end - plug TrailingFormatPlug - plug Plug.RequestId - plug Plug.Logger + plug(TrailingFormatPlug) + plug(Plug.RequestId) + plug(Plug.Logger) - plug Plug.Parsers, + plug( + Plug.Parsers, parsers: [:urlencoded, :multipart, :json], pass: ["*/*"], json_decoder: Jason + ) - plug Plug.MethodOverride - plug Plug.Head + plug(Plug.MethodOverride) + plug(Plug.Head) # The session will be stored in the cookie and signed, # this means its contents can be read but not tampered with. # Set :encryption_salt if you would also like to encrypt it. - plug Plug.Session, + plug( + Plug.Session, store: :cookie, key: "_pleroma_key", signing_salt: "CqaoopA2" + ) - plug Pleroma.Web.Router + plug(Pleroma.Web.Router) @doc """ Dynamically loads configuration from the system environment diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index 51d293196..8335add9c 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -16,27 +16,36 @@ defmodule Pleroma.Web.Federator do def start_link do spawn(fn -> - Process.sleep(1000 * 60 * 1) # 1 minute + # 1 minute + Process.sleep(1000 * 60 * 1) enqueue(:refresh_subscriptions, nil) end) - GenServer.start_link(__MODULE__, %{ - in: {:sets.new(), []}, - out: {:sets.new(), []} - }, name: __MODULE__) + + GenServer.start_link( + __MODULE__, + %{ + in: {:sets.new(), []}, + out: {:sets.new(), []} + }, + name: __MODULE__ + ) end def handle(:refresh_subscriptions, _) do Logger.debug("Federator running refresh subscriptions") Websub.refresh_subscriptions() + spawn(fn -> - Process.sleep(1000 * 60 * 60 * 6) # 6 hours + # 6 hours + Process.sleep(1000 * 60 * 60 * 6) enqueue(:refresh_subscriptions, nil) end) end def handle(:request_subscription, websub) do Logger.debug("Refreshing #{websub.topic}") - with {:ok, websub } <- Websub.request_subscription(websub) do + + with {:ok, websub} <- Websub.request_subscription(websub) do Logger.debug("Successfully refreshed #{websub.topic}") else _e -> Logger.debug("Couldn't refresh #{websub.topic}") @@ -45,8 +54,10 @@ def handle(:request_subscription, websub) do def handle(:publish, activity) do Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) + with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do {:ok, actor} = WebFinger.ensure_keys_present(actor) + if ActivityPub.is_public?(activity) do Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) @@ -61,7 +72,10 @@ def handle(:publish, activity) do end def handle(:verify_websub, websub) do - Logger.debug(fn -> "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})" end) + Logger.debug(fn -> + "Running WebSub verification for #{websub.id} (#{websub.topic}, #{websub.callback})" + end) + @websub.verify(websub) end @@ -72,16 +86,18 @@ def handle(:incoming_doc, doc) do def handle(:incoming_ap_doc, params) do Logger.info("Handling incoming AP activity") + with {:ok, _user} <- ap_enabled_actor(params["actor"]), nil <- Activity.get_by_ap_id(params["id"]), {:ok, activity} <- Transmogrifier.handle_incoming(params) do else %Activity{} -> Logger.info("Already had #{params["id"]}") + e -> # Just drop those for now Logger.info("Unhandled activity") - Logger.info(Poison.encode!(params, [pretty: 2])) + Logger.info(Poison.encode!(params, pretty: 2)) end end @@ -93,12 +109,21 @@ def handle(:publish_single_websub, %{xml: xml, topic: topic, callback: callback, signature = @websub.sign(secret || "", xml) Logger.debug(fn -> "Pushing #{topic} to #{callback}" end) - with {:ok, %{status_code: code}} <- @httpoison.post(callback, xml, [ - {"Content-Type", "application/atom+xml"}, - {"X-Hub-Signature", "sha1=#{signature}"} - ], timeout: 10000, recv_timeout: 20000, hackney: [pool: :default]) do + with {:ok, %{status_code: 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.debug(fn -> "Pushed to #{callback}, code #{code}" end) - else e -> + else + e -> Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(e)}" end) end end @@ -110,7 +135,7 @@ def handle(type, _) do def enqueue(type, payload, priority \\ 1) do if @federating do - if Mix.env == :test do + if Mix.env() == :test do handle(type, payload) else GenServer.cast(__MODULE__, {:enqueue, type, payload, priority}) @@ -119,7 +144,7 @@ def enqueue(type, payload, priority \\ 1) do end def maybe_start_job(running_jobs, queue) do - if (:sets.size(running_jobs) < @max_jobs) && queue != [] do + if :sets.size(running_jobs) < @max_jobs && queue != [] do {{type, payload}, queue} = queue_pop(queue) {:ok, pid} = Task.start(fn -> handle(type, payload) end) mref = Process.monitor(pid) @@ -129,7 +154,8 @@ def maybe_start_job(running_jobs, queue) do end end - def handle_cast({:enqueue, type, payload, priority}, state) when type in [:incoming_doc, :incoming_ap_doc] do + def handle_cast({:enqueue, type, payload, priority}, state) + when type in [:incoming_doc, :incoming_ap_doc] do %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state i_queue = enqueue_sorted(i_queue, {type, payload}, 1) {i_running_jobs, i_queue} = maybe_start_job(i_running_jobs, i_queue) @@ -160,7 +186,7 @@ def handle_info({:DOWN, ref, :process, _pid, _reason}, state) do def enqueue_sorted(queue, element, priority) do [%{item: element, priority: priority} | queue] - |> Enum.sort_by(fn (%{priority: priority}) -> priority end) + |> Enum.sort_by(fn %{priority: priority} -> priority end) end def queue_pop([%{item: element} | queue]) do @@ -169,6 +195,7 @@ def queue_pop([%{item: element} | queue]) do def ap_enabled_actor(id) do user = User.get_by_ap_id(id) + if User.ap_enabled?(user) do {:ok, user} else diff --git a/lib/pleroma/web/mastodon_api/mastodon_api.ex b/lib/pleroma/web/mastodon_api/mastodon_api.ex index e69de29bb..8b1378917 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api.ex @@ -0,0 +1 @@ + diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 9428acd7e..58bb5e03a 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -11,8 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do require Logger def create_app(conn, params) do - with cs <- App.register_changeset(%App{}, params) |> IO.inspect, - {:ok, app} <- Repo.insert(cs) |> IO.inspect do + with cs <- App.register_changeset(%App{}, params) |> IO.inspect(), + {:ok, app} <- Repo.insert(cs) |> IO.inspect() do res = %{ id: app.id, client_id: app.client_id, @@ -25,51 +25,57 @@ def create_app(conn, params) do def update_credentials(%{assigns: %{user: user}} = conn, params) do original_user = user - params = if bio = params["note"] do - Map.put(params, "bio", bio) - else - params - end - params = if name = params["display_name"] do - Map.put(params, "name", name) - else - params - end - - user = if avatar = params["avatar"] do - with %Plug.Upload{} <- avatar, - {:ok, object} <- ActivityPub.upload(avatar), - change = Ecto.Changeset.change(user, %{avatar: object.data}), - {:ok, user} = User.update_and_set_cache(change) do - user + params = + if bio = params["note"] do + Map.put(params, "bio", bio) else - _e -> user + params end - else - user - end - user = if banner = params["header"] do - with %Plug.Upload{} <- banner, - {:ok, object} <- ActivityPub.upload(banner), - new_info <- Map.put(user.info, "banner", object.data), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user + params = + if name = params["display_name"] do + Map.put(params, "name", name) else - _e -> user + params + end + + user = + if avatar = params["avatar"] do + with %Plug.Upload{} <- avatar, + {:ok, object} <- ActivityPub.upload(avatar), + change = Ecto.Changeset.change(user, %{avatar: object.data}), + {:ok, user} = User.update_and_set_cache(change) do + user + else + _e -> user + end + else + user + end + + user = + if banner = params["header"] do + with %Plug.Upload{} <- banner, + {:ok, object} <- ActivityPub.upload(banner), + new_info <- Map.put(user.info, "banner", object.data), + change <- User.info_changeset(user, %{info: new_info}), + {:ok, user} <- User.update_and_set_cache(change) do + user + else + _e -> user + end + else + user end - else - user - end with changeset <- User.update_changeset(user, params), {:ok, user} <- User.update_and_set_cache(changeset) do if original_user != user do CommonAPI.update(user) end - json conn, AccountView.render("account.json", %{user: user}) + + json(conn, AccountView.render("account.json", %{user: user})) else _e -> conn @@ -88,9 +94,10 @@ def user(conn, %{"id" => id}) do account = AccountView.render("account.json", %{user: user}) json(conn, account) else - _e -> conn - |> put_status(404) - |> json(%{error: "Can't find user"}) + _e -> + conn + |> put_status(404) + |> json(%{error: "Can't find user"}) end end @@ -98,16 +105,16 @@ def user(conn, %{"id" => id}) do def masto_instance(conn, _params) do response = %{ - uri: Web.base_url, + uri: Web.base_url(), title: Keyword.get(@instance, :name), description: "A Pleroma instance, an alternative fediverse server", version: Keyword.get(@instance, :version), email: Keyword.get(@instance, :email), urls: %{ - streaming_api: String.replace(Web.base_url, ["http","https"], "wss") + streaming_api: String.replace(Web.base_url(), ["http", "https"], "wss") }, - stats: Stats.get_stats, - thumbnail: Web.base_url <> "/instance/thumbnail.jpeg", + stats: Stats.get_stats(), + thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg", max_toot_chars: Keyword.get(@instance, :limit) } @@ -115,13 +122,14 @@ def masto_instance(conn, _params) do end def peers(conn, _params) do - json(conn, Stats.get_peers) + json(conn, Stats.get_peers()) end defp mastodonized_emoji do Pleroma.Formatter.get_custom_emoji() |> Enum.map(fn {shortcode, relative_url} -> - url = to_string URI.merge(Web.base_url(), relative_url) + url = to_string(URI.merge(Web.base_url(), relative_url)) + %{ "shortcode" => shortcode, "static_url" => url, @@ -132,26 +140,30 @@ defp mastodonized_emoji do def custom_emojis(conn, _params) do mastodon_emoji = mastodonized_emoji() - json conn, mastodon_emoji + json(conn, mastodon_emoji) end defp add_link_headers(conn, method, activities, param \\ false) do last = List.last(activities) first = List.first(activities) + if last do min = last.id max = first.id - {next_url, prev_url} = if param do - { - mastodon_api_url(Pleroma.Web.Endpoint, method, param, max_id: min), - mastodon_api_url(Pleroma.Web.Endpoint, method, param, since_id: max) - } - else - { - mastodon_api_url(Pleroma.Web.Endpoint, method, max_id: min), - mastodon_api_url(Pleroma.Web.Endpoint, method, since_id: max) - } - end + + {next_url, prev_url} = + if param do + { + mastodon_api_url(Pleroma.Web.Endpoint, method, param, max_id: min), + mastodon_api_url(Pleroma.Web.Endpoint, method, param, since_id: max) + } + else + { + mastodon_api_url(Pleroma.Web.Endpoint, method, max_id: min), + mastodon_api_url(Pleroma.Web.Endpoint, method, since_id: max) + } + end + conn |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") else @@ -160,13 +172,15 @@ defp add_link_headers(conn, method, activities, param \\ false) do end def home_timeline(%{assigns: %{user: user}} = conn, params) do - params = params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("blocking_user", user) - |> Map.put("user", user) + params = + params + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("blocking_user", user) + |> Map.put("user", user) - activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) - |> Enum.reverse + activities = + ActivityPub.fetch_activities([user.ap_id | user.following], params) + |> Enum.reverse() conn |> add_link_headers(:home_timeline, activities) @@ -174,13 +188,15 @@ def home_timeline(%{assigns: %{user: user}} = conn, params) do end def public_timeline(%{assigns: %{user: user}} = conn, params) do - params = params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("local_only", params["local"] in [true, "True", "true", "1"]) - |> Map.put("blocking_user", user) + params = + params + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("local_only", params["local"] in [true, "True", "true", "1"]) + |> Map.put("blocking_user", user) - activities = ActivityPub.fetch_public_activities(params) - |> Enum.reverse + activities = + ActivityPub.fetch_public_activities(params) + |> Enum.reverse() conn |> add_link_headers(:public_timeline, activities) @@ -189,13 +205,15 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do def user_statuses(%{assigns: %{user: user}} = conn, params) do with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do - params = params - |> Map.put("type", ["Create", "Announce"]) - |> Map.put("actor_id", ap_id) - |> Map.put("whole_db", true) + params = + params + |> Map.put("type", ["Create", "Announce"]) + |> Map.put("actor_id", ap_id) + |> Map.put("whole_db", true) - activities = ActivityPub.fetch_public_activities(params) - |> Enum.reverse + activities = + ActivityPub.fetch_public_activities(params) + |> Enum.reverse() conn |> add_link_headers(:user_statuses, activities, params["id"]) @@ -206,19 +224,39 @@ def user_statuses(%{assigns: %{user: user}} = conn, params) do def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Repo.get(Activity, id), true <- ActivityPub.visible_for_user?(activity, user) do - render conn, StatusView, "status.json", %{activity: activity, for: user} + render(conn, StatusView, "status.json", %{activity: activity, for: user}) end end def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Repo.get(Activity, id), - activities <- ActivityPub.fetch_activities_for_context(activity.data["context"], %{"blocking_user" => user, "user" => user}), - activities <- activities |> Enum.filter(fn (%{id: aid}) -> to_string(aid) != to_string(id) end), - activities <- activities |> Enum.filter(fn (%{data: %{"type" => type}}) -> type == "Create" end), - grouped_activities <- Enum.group_by(activities, fn (%{id: id}) -> id < activity.id end) do + activities <- + ActivityPub.fetch_activities_for_context(activity.data["context"], %{ + "blocking_user" => user, + "user" => user + }), + activities <- + activities |> Enum.filter(fn %{id: aid} -> to_string(aid) != to_string(id) end), + activities <- + activities |> Enum.filter(fn %{data: %{"type" => type}} -> type == "Create" end), + grouped_activities <- Enum.group_by(activities, fn %{id: id} -> id < activity.id end) do result = %{ - ancestors: StatusView.render("index.json", for: user, activities: grouped_activities[true] || [], as: :activity) |> Enum.reverse, - descendants: StatusView.render("index.json", for: user, activities: grouped_activities[false] || [], as: :activity) |> Enum.reverse, + ancestors: + StatusView.render( + "index.json", + for: user, + activities: grouped_activities[true] || [], + as: :activity + ) + |> Enum.reverse(), + descendants: + StatusView.render( + "index.json", + for: user, + activities: grouped_activities[false] || [], + as: :activity + ) + |> Enum.reverse() } json(conn, result) @@ -226,12 +264,13 @@ def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do end def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do - params = params - |> Map.put("in_reply_to_status_id", params["in_reply_to_id"]) - |> Map.put("no_attachment_links", true) + params = + params + |> Map.put("in_reply_to_status_id", params["in_reply_to_id"]) + |> Map.put("no_attachment_links", true) {:ok, activity} = CommonAPI.post(user, params) - render conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity} + render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do @@ -247,30 +286,32 @@ def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, announce, _activity} = CommonAPI.repeat(ap_id_or_id, user) do - render conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity} + render(conn, StatusView, "status.json", %{activity: announce, for: user, as: :activity}) end end def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do - render conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity} + render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end end def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do - render conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity} + render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) end end def notifications(%{assigns: %{user: user}} = conn, params) do notifications = Notification.for_user(user, params) - result = Enum.map(notifications, fn x -> - render_notification(user, x) - end) - |> Enum.filter(&(&1)) + + result = + Enum.map(notifications, fn x -> + render_notification(user, x) + end) + |> Enum.filter(& &1) conn |> add_link_headers(:notifications, notifications) @@ -306,27 +347,26 @@ def dismiss_notification(%{assigns: %{user: user}} = conn, %{"id" => id} = _para def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do id = List.wrap(id) - q = from u in User, - where: u.id in ^id + q = from(u in User, where: u.id in ^id) targets = Repo.all(q) - render conn, AccountView, "relationships.json", %{user: user, targets: targets} + render(conn, AccountView, "relationships.json", %{user: user, targets: targets}) end def upload(%{assigns: %{user: _}} = conn, %{"file" => file}) do with {:ok, object} <- ActivityPub.upload(file) do - data = object.data - |> Map.put("id", object.id) + data = + object.data + |> Map.put("id", object.id) - render conn, StatusView, "attachment.json", %{attachment: data} + render(conn, StatusView, "attachment.json", %{attachment: data}) end end def favourited_by(conn, %{"id" => id}) do with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do - q = from u in User, - where: u.ap_id in ^likes + q = from(u in User, where: u.ap_id in ^likes) users = Repo.all(q) - render conn, AccountView, "accounts.json", %{users: users, as: :user} + render(conn, AccountView, "accounts.json", %{users: users, as: :user}) else _ -> json(conn, []) end @@ -334,23 +374,24 @@ def favourited_by(conn, %{"id" => id}) do def reblogged_by(conn, %{"id" => id}) do with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do - q = from u in User, - where: u.ap_id in ^announces + q = from(u in User, where: u.ap_id in ^announces) users = Repo.all(q) - render conn, AccountView, "accounts.json", %{users: users, as: :user} + render(conn, AccountView, "accounts.json", %{users: users, as: :user}) else _ -> json(conn, []) end end def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do - params = params - |> Map.put("type", "Create") - |> Map.put("local_only", !!params["local"]) - |> Map.put("blocking_user", user) + params = + params + |> Map.put("type", "Create") + |> Map.put("local_only", !!params["local"]) + |> Map.put("blocking_user", user) - activities = ActivityPub.fetch_public_activities(params) - |> Enum.reverse + activities = + ActivityPub.fetch_public_activities(params) + |> Enum.reverse() conn |> add_link_headers(:hashtag_timeline, activities, params["tag"]) @@ -361,14 +402,14 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do def followers(conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_followers(user) do - render conn, AccountView, "accounts.json", %{users: followers, as: :user} + render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end def following(conn, %{"id" => id}) do with %User{} = user <- Repo.get(User, id), {:ok, followers} <- User.get_friends(user) do - render conn, AccountView, "accounts.json", %{users: followers, as: :user} + render(conn, AccountView, "accounts.json", %{users: followers, as: :user}) end end @@ -376,7 +417,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), {:ok, follower} <- User.follow(follower, followed), {:ok, _activity} <- ActivityPub.follow(follower, followed) do - render conn, AccountView, "relationship.json", %{user: follower, target: followed} + render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) else {:error, message} -> conn @@ -389,7 +430,7 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do with %User{} = followed <- Repo.get_by(User, nickname: uri), {:ok, follower} <- User.follow(follower, followed), {:ok, _activity} <- ActivityPub.follow(follower, followed) do - render conn, AccountView, "account.json", %{user: followed} + render(conn, AccountView, "account.json", %{user: followed}) else {:error, message} -> conn @@ -401,20 +442,22 @@ def follow(%{assigns: %{user: follower}} = conn, %{"uri" => uri}) do # TODO: Clean up and unify def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do with %User{} = followed <- Repo.get(User, id), - { :ok, follower, follow_activity } <- User.unfollow(follower, followed), - { :ok, _activity } <- ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - "object" => follow_activity.data["id"] # get latest Follow for these users - }) do - render conn, AccountView, "relationship.json", %{user: follower, target: followed} + {:ok, follower, follow_activity} <- User.unfollow(follower, followed), + {:ok, _activity} <- + ActivityPub.insert(%{ + "type" => "Undo", + "actor" => follower.ap_id, + # get latest Follow for these users + "object" => follow_activity.data["id"] + }) do + render(conn, AccountView, "relationship.json", %{user: follower, target: followed}) end end def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), {:ok, blocker} <- User.block(blocker, blocked) do - render conn, AccountView, "relationship.json", %{user: blocker, target: blocked} + render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> conn @@ -426,7 +469,7 @@ def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do with %User{} = blocked <- Repo.get(User, id), {:ok, blocker} <- User.unblock(blocker, blocked) do - render conn, AccountView, "relationship.json", %{user: blocker, target: blocked} + render(conn, AccountView, "relationship.json", %{user: blocker, target: blocked}) else {:error, message} -> conn @@ -438,7 +481,7 @@ def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do # TODO: Use proper query def blocks(%{assigns: %{user: user}} = conn, _) do with blocked_users <- user.info["blocks"] || [], - accounts <- Enum.map(blocked_users, fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) do + accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) json(conn, res) end @@ -447,23 +490,34 @@ def blocks(%{assigns: %{user: user}} = conn, _) do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = User.search(query, params["resolve"] == "true") - fetched = if Regex.match?(~r/https?:/, query) do - with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do - activities - else - _e -> [] - end - end || [] + fetched = + if Regex.match?(~r/https?:/, query) do + with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do + activities + else + _e -> [] + end + end || [] + + q = + from( + a in Activity, + where: fragment("?->>'type' = 'Create'", a.data), + where: + fragment( + "to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", + a.data, + ^query + ), + limit: 20 + ) - q = from a in Activity, - where: fragment("?->>'type' = 'Create'", a.data), - where: fragment("to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", a.data, ^query), - limit: 20 statuses = Repo.all(q) ++ fetched res = %{ "accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user), - "statuses" => StatusView.render("index.json", activities: statuses, for: user, as: :activity), + "statuses" => + StatusView.render("index.json", activities: statuses, for: user, as: :activity), "hashtags" => [] } @@ -479,94 +533,102 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d end def favourites(%{assigns: %{user: user}} = conn, _) do - params = %{} - |> Map.put("type", "Create") - |> Map.put("favorited_by", user.ap_id) - |> Map.put("blocking_user", user) + params = + %{} + |> Map.put("type", "Create") + |> Map.put("favorited_by", user.ap_id) + |> Map.put("blocking_user", user) - activities = ActivityPub.fetch_public_activities(params) - |> Enum.reverse + activities = + ActivityPub.fetch_public_activities(params) + |> Enum.reverse() conn |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) end def index(%{assigns: %{user: user}} = conn, _params) do - token = conn - |> get_session(:oauth_token) + token = + conn + |> get_session(:oauth_token) if user && token do mastodon_emoji = mastodonized_emoji() accounts = Map.put(%{}, user.id, AccountView.render("account.json", %{user: user})) - initial_state = %{ - meta: %{ - streaming_api_base_url: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), - access_token: token, - locale: "en", - domain: Pleroma.Web.Endpoint.host(), - admin: "1", - me: "#{user.id}", - unfollow_modal: false, - boost_modal: false, - delete_modal: true, - auto_play_gif: false, - reduce_motion: false - }, - compose: %{ - me: "#{user.id}", - default_privacy: "public", - default_sensitive: false - }, - media_attachments: %{ - accept_content_types: [ - ".jpg", - ".jpeg", - ".png", - ".gif", - ".webm", - ".mp4", - ".m4v", - "image\/jpeg", - "image\/png", - "image\/gif", - "video\/webm", - "video\/mp4" - ] - }, - settings: %{ - onboarded: true, - home: %{ - shows: %{ - reblog: true, - reply: true + + initial_state = + %{ + meta: %{ + streaming_api_base_url: + String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), + access_token: token, + locale: "en", + domain: Pleroma.Web.Endpoint.host(), + admin: "1", + me: "#{user.id}", + unfollow_modal: false, + boost_modal: false, + delete_modal: true, + auto_play_gif: false, + reduce_motion: false + }, + compose: %{ + me: "#{user.id}", + default_privacy: "public", + default_sensitive: false + }, + media_attachments: %{ + accept_content_types: [ + ".jpg", + ".jpeg", + ".png", + ".gif", + ".webm", + ".mp4", + ".m4v", + "image\/jpeg", + "image\/png", + "image\/gif", + "video\/webm", + "video\/mp4" + ] + }, + settings: %{ + onboarded: true, + home: %{ + shows: %{ + reblog: true, + reply: true + } + }, + notifications: %{ + alerts: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + }, + shows: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + }, + sounds: %{ + follow: true, + favourite: true, + reblog: true, + mention: true + } } }, - notifications: %{ - alerts: %{ - follow: true, - favourite: true, - reblog: true, - mention: true - }, - shows: %{ - follow: true, - favourite: true, - reblog: true, - mention: true - }, - sounds: %{ - follow: true, - favourite: true, - reblog: true, - mention: true - } - } - }, - push_subscription: nil, - accounts: accounts, - custom_emojis: mastodon_emoji, - char_limit: Keyword.get(@instance, :limit) - } |> Jason.encode! + push_subscription: nil, + accounts: accounts, + custom_emojis: mastodon_emoji, + char_limit: Keyword.get(@instance, :limit) + } + |> Jason.encode!() + conn |> put_layout(false) |> render(MastodonView, "index.html", %{initial_state: initial_state}) @@ -586,12 +648,18 @@ defp get_or_make_app() do {:ok, app} else _e -> - cs = App.register_changeset(%App{}, %{client_name: "Mastodon-Local", redirect_uris: ".", scopes: "read,write,follow"}) + cs = + App.register_changeset(%App{}, %{ + client_name: "Mastodon-Local", + redirect_uris: ".", + scopes: "read,write,follow" + }) + Repo.insert(cs) end end - def login_post(conn, %{"authorization" => %{ "name" => name, "password" => password}}) do + def login_post(conn, %{"authorization" => %{"name" => name, "password" => password}}) do with %User{} = user <- User.get_cached_by_nickname(name), true <- Pbkdf2.checkpw(password, user.password_hash), {:ok, app} <- get_or_make_app(), @@ -615,8 +683,9 @@ def logout(conn, _) do def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do Logger.debug("Unimplemented, returning unmodified relationship") + with %User{} = target <- Repo.get(User, id) do - render conn, AccountView, "relationship.json", %{user: user, target: target} + render(conn, AccountView, "relationship.json", %{user: user, target: target}) end end @@ -632,20 +701,53 @@ def empty_object(conn, _) do def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) do actor = User.get_cached_by_ap_id(activity.data["actor"]) - created_at = NaiveDateTime.to_iso8601(created_at) - |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) + + created_at = + NaiveDateTime.to_iso8601(created_at) + |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) + case activity.data["type"] do "Create" -> - %{id: id, type: "mention", created_at: created_at, account: AccountView.render("account.json", %{user: actor}), status: StatusView.render("status.json", %{activity: activity, for: user})} + %{ + id: id, + type: "mention", + created_at: created_at, + account: AccountView.render("account.json", %{user: actor}), + status: StatusView.render("status.json", %{activity: activity, for: user}) + } + "Like" -> liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) - %{id: id, type: "favourite", created_at: created_at, account: AccountView.render("account.json", %{user: actor}), status: StatusView.render("status.json", %{activity: liked_activity, for: user})} + + %{ + id: id, + type: "favourite", + created_at: created_at, + account: AccountView.render("account.json", %{user: actor}), + status: StatusView.render("status.json", %{activity: liked_activity, for: user}) + } + "Announce" -> announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) - %{id: id, type: "reblog", created_at: created_at, account: AccountView.render("account.json", %{user: actor}), status: StatusView.render("status.json", %{activity: announced_activity, for: user})} + + %{ + id: id, + type: "reblog", + created_at: created_at, + account: AccountView.render("account.json", %{user: actor}), + status: StatusView.render("status.json", %{activity: announced_activity, for: user}) + } + "Follow" -> - %{id: id, type: "follow", created_at: created_at, account: AccountView.render("account.json", %{user: actor})} - _ -> nil + %{ + id: id, + type: "follow", + created_at: created_at, + account: AccountView.render("account.json", %{user: actor}) + } + + _ -> + nil end end end diff --git a/lib/pleroma/web/mastodon_api/mastodon_socket.ex b/lib/pleroma/web/mastodon_api/mastodon_socket.ex index c3bae5935..f3e062941 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_socket.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_socket.ex @@ -4,17 +4,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do alias Pleroma.Web.OAuth.Token alias Pleroma.{User, Repo} - transport :streaming, Phoenix.Transports.WebSocket.Raw, - timeout: :infinity # We never receive data. + transport( + :streaming, + Phoenix.Transports.WebSocket.Raw, + # We never receive data. + timeout: :infinity + ) def connect(params, socket) do with token when not is_nil(token) <- params["access_token"], %Token{user_id: user_id} <- Repo.get_by(Token, token: token), %User{} = user <- Repo.get(User, user_id), stream when stream in ["public", "public:local", "user"] <- params["stream"] do - socket = socket - |> assign(:topic, params["stream"]) - |> assign(:user, user) + socket = + socket + |> assign(:topic, params["stream"]) + |> assign(:user, user) + Pleroma.Web.Streamer.add_socket(params["stream"], socket) {:ok, socket} else @@ -25,11 +31,11 @@ def connect(params, socket) do def id(_), do: nil def handle(:text, message, _state) do - #| :ok - #| state - #| {:text, message} - #| {:text, message, state} - #| {:close, "Goodbye!"} + # | :ok + # | state + # | {:text, message} + # | {:text, message, state} + # | {:close, "Goodbye!"} {:text, message} end diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index a4539d47e..bc5ae5da7 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -10,37 +10,52 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do defp get_replied_to_activities(activities) do activities |> Enum.map(fn - (%{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}}) -> - (inReplyTo != "") && inReplyTo - _ -> nil + %{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}} -> + inReplyTo != "" && inReplyTo + + _ -> + nil end) - |> Enum.filter(&(&1)) + |> Enum.filter(& &1) |> Activity.create_activity_by_object_id_query() - |> Repo.all - |> Enum.reduce(%{}, fn(activity, acc) -> Map.put(acc,activity.data["object"]["id"], activity) end) + |> Repo.all() + |> Enum.reduce(%{}, fn activity, acc -> + Map.put(acc, activity.data["object"]["id"], activity) + end) end def render("index.json", opts) do replied_to_activities = get_replied_to_activities(opts.activities) - render_many(opts.activities, StatusView, "status.json", Map.put(opts, :replied_to_activities, replied_to_activities)) + + render_many( + opts.activities, + StatusView, + "status.json", + Map.put(opts, :replied_to_activities, replied_to_activities) + ) end - def render("status.json", %{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts) do + def render( + "status.json", + %{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts + ) do user = User.get_cached_by_ap_id(activity.data["actor"]) created_at = Utils.to_masto_date(activity.data["published"]) reblogged = Activity.get_create_activity_by_object_ap_id(object) reblogged = render("status.json", Map.put(opts, :activity, reblogged)) - mentions = activity.recipients - |> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn (user) -> AccountView.render("mention.json", %{user: user}) end) + mentions = + activity.recipients + |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) + |> Enum.filter(& &1) + |> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end) %{ id: to_string(activity.id), uri: object, - url: nil, # TODO: This might be wrong, check with mastodon. + # TODO: This might be wrong, check with mastodon. + url: nil, account: AccountView.render("account.json", %{user: user}), in_reply_to_id: nil, in_reply_to_account_id: nil, @@ -89,27 +104,30 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} tags = object["tag"] || [] sensitive = object["sensitive"] || Enum.member?(tags, "nsfw") - mentions = activity.recipients - |> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn (user) -> AccountView.render("mention.json", %{user: user}) end) + mentions = + activity.recipients + |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) + |> Enum.filter(& &1) + |> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) - attachments = render_many(object["attachment"] || [], StatusView, "attachment.json", as: :attachment) + attachments = + render_many(object["attachment"] || [], StatusView, "attachment.json", as: :attachment) created_at = Utils.to_masto_date(object["published"]) reply_to = get_reply_to(activity, opts) reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"]) - emojis = (activity.data["object"]["emoji"] || []) - |> Enum.map(fn {name, url} -> - name = HtmlSanitizeEx.strip_tags(name) - url = HtmlSanitizeEx.strip_tags(url) - %{ shortcode: name, url: url, static_url: url } - end) + emojis = + (activity.data["object"]["emoji"] || []) + |> Enum.map(fn {name, url} -> + name = HtmlSanitizeEx.strip_tags(name) + url = HtmlSanitizeEx.strip_tags(url) + %{shortcode: name, url: url, static_url: url} + end) %{ id: to_string(activity.id), @@ -131,7 +149,8 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity} visibility: get_visibility(object), media_attachments: attachments |> Enum.take(4), mentions: mentions, - tags: [], # fix, + # fix, + tags: [], application: %{ name: "Web", website: nil @@ -145,10 +164,11 @@ def get_visibility(object) do public = "https://www.w3.org/ns/activitystreams#Public" to = object["to"] || [] cc = object["cc"] || [] + cond do public in to -> "public" public in cc -> "unlisted" - Enum.any?(to, &(String.contains?(&1, "/followers"))) -> "private" + Enum.any?(to, &String.contains?(&1, "/followers")) -> "private" true -> "direct" end end @@ -156,14 +176,15 @@ def get_visibility(object) do def render("attachment.json", %{attachment: attachment}) do [%{"mediaType" => media_type, "href" => href} | _] = attachment["url"] - type = cond do - String.contains?(media_type, "image") -> "image" - String.contains?(media_type, "video") -> "video" - String.contains?(media_type, "audio") -> "audio" - true -> "unknown" - end + type = + cond do + String.contains?(media_type, "image") -> "image" + String.contains?(media_type, "video") -> "video" + String.contains?(media_type, "audio") -> "audio" + true -> "unknown" + end - << hash_id::signed-32, _rest::binary >> = :crypto.hash(:md5, href) + <> = :crypto.hash(:md5, href) %{ id: to_string(attachment["id"] || hash_id), diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex index b0bbe8b64..8195a665e 100644 --- a/lib/pleroma/web/media_proxy/controller.ex +++ b/lib/pleroma/web/media_proxy/controller.ex @@ -4,47 +4,59 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do @httpoison Application.get_env(:pleroma, :httpoison) - @max_body_length 25 * 1048576 + @max_body_length 25 * 1_048_576 @cache_control %{ default: "public, max-age=1209600", - error: "public, must-revalidate, max-age=160", + error: "public, must-revalidate, max-age=160" } def remote(conn, %{"sig" => sig, "url" => url}) do config = Application.get_env(:pleroma, :media_proxy, []) - with \ - true <- Keyword.get(config, :enabled, false), - {:ok, url} <- Pleroma.Web.MediaProxy.decode_url(sig, url), - {:ok, content_type, body} <- proxy_request(url) - do + + with true <- Keyword.get(config, :enabled, false), + {:ok, url} <- Pleroma.Web.MediaProxy.decode_url(sig, url), + {:ok, content_type, body} <- proxy_request(url) do conn |> put_resp_content_type(content_type) |> set_cache_header(:default) |> send_resp(200, body) else - false -> send_error(conn, 404) - {:error, :invalid_signature} -> send_error(conn, 403) - {:error, {:http, _, url}} -> redirect_or_error(conn, url, Keyword.get(config, :redirect_on_failure, true)) + false -> + send_error(conn, 404) + + {:error, :invalid_signature} -> + send_error(conn, 403) + + {:error, {:http, _, url}} -> + redirect_or_error(conn, url, Keyword.get(config, :redirect_on_failure, true)) end end defp proxy_request(link) do - headers = [{"user-agent", "Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{Application.get_env(:pleroma, :instance)[:email]}>"}] - options = @httpoison.process_request_options([:insecure, {:follow_redirect, true}]) ++ [{:pool, :default}] - with \ - {:ok, 200, headers, client} <- :hackney.request(:get, link, headers, "", options), - headers = Enum.into(headers, Map.new), - {:ok, body} <- proxy_request_body(client), - content_type <- proxy_request_content_type(headers, body) - do + headers = [ + {"user-agent", + "Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{ + Application.get_env(:pleroma, :instance)[:email] + }>"} + ] + + options = + @httpoison.process_request_options([:insecure, {:follow_redirect, true}]) ++ + [{:pool, :default}] + + with {:ok, 200, headers, client} <- :hackney.request(:get, link, headers, "", options), + headers = Enum.into(headers, Map.new()), + {:ok, body} <- proxy_request_body(client), + content_type <- proxy_request_content_type(headers, body) do {:ok, content_type, body} else {:ok, status, _, _} -> - Logger.warn "MediaProxy: request failed, status #{status}, link: #{link}" + Logger.warn("MediaProxy: request failed, status #{status}, link: #{link}") {:error, {:http, :bad_status, link}} + {:error, error} -> - Logger.warn "MediaProxy: request failed, error #{inspect error}, link: #{link}" + Logger.warn("MediaProxy: request failed, error #{inspect(error)}, link: #{link}") {:error, {:http, error, link}} end end @@ -63,13 +75,15 @@ defp send_error(conn, code, body \\ "") do end defp proxy_request_body(client), do: proxy_request_body(client, <<>>) + defp proxy_request_body(client, body) when byte_size(body) < @max_body_length do case :hackney.stream_body(client) do - {:ok, data} -> proxy_request_body(client, <>) + {:ok, data} -> proxy_request_body(client, <>) :done -> {:ok, body} {:error, reason} -> {:error, reason} end end + defp proxy_request_body(client, _) do :hackney.close(client) {:error, :body_too_large} @@ -80,5 +94,4 @@ defp proxy_request_body(client, _) do defp proxy_request_content_type(headers, _body) do headers["Content-Type"] || headers["content-type"] || "image/jpeg" end - end diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 23efc18fa..37718f48b 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -7,14 +7,15 @@ def url(url = "/" <> _), do: url def url(url) do config = Application.get_env(:pleroma, :media_proxy, []) - if !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url) do + + if !Keyword.get(config, :enabled, false) or String.starts_with?(url, Pleroma.Web.base_url()) do url else secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] base64 = Base.url_encode64(url, @base64_opts) sig = :crypto.hmac(:sha, secret, base64) sig64 = sig |> Base.url_encode64(@base64_opts) - Keyword.get(config, :base_url, Pleroma.Web.base_url) <> "/proxy/#{sig64}/#{base64}" + Keyword.get(config, :base_url, Pleroma.Web.base_url()) <> "/proxy/#{sig64}/#{base64}" end end @@ -22,11 +23,11 @@ def decode_url(sig, url) do secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] sig = Base.url_decode64!(sig, @base64_opts) local_sig = :crypto.hmac(:sha, secret, url) + if local_sig == sig do {:ok, Base.url_decode64!(url, @base64_opts)} else {:error, :invalid_signature} end end - end diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index ff52ba82e..b3273bc6e 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -3,25 +3,26 @@ defmodule Pleroma.Web.OAuth.App do import Ecto.{Changeset} schema "apps" do - field :client_name, :string - field :redirect_uris, :string - field :scopes, :string - field :website, :string - field :client_id, :string - field :client_secret, :string + field(:client_name, :string) + field(:redirect_uris, :string) + field(:scopes, :string) + field(:website, :string) + field(:client_id, :string) + field(:client_secret, :string) timestamps() end def register_changeset(struct, params \\ %{}) do - changeset = struct - |> cast(params, [:client_name, :redirect_uris, :scopes, :website]) - |> validate_required([:client_name, :redirect_uris, :scopes]) + changeset = + struct + |> cast(params, [:client_name, :redirect_uris, :scopes, :website]) + |> validate_required([:client_name, :redirect_uris, :scopes]) if changeset.valid? do changeset - |> put_change(:client_id, :crypto.strong_rand_bytes(32) |> Base.url_encode64) - |> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64) + |> put_change(:client_id, :crypto.strong_rand_bytes(32) |> Base.url_encode64()) + |> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64()) else changeset end diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex index 1ba5be602..94f44c9f2 100644 --- a/lib/pleroma/web/oauth/authorization.ex +++ b/lib/pleroma/web/oauth/authorization.ex @@ -7,24 +7,24 @@ defmodule Pleroma.Web.OAuth.Authorization do import Ecto.{Changeset} schema "oauth_authorizations" do - field :token, :string - field :valid_until, :naive_datetime - field :used, :boolean, default: false - belongs_to :user, Pleroma.User - belongs_to :app, Pleroma.App + field(:token, :string) + field(:valid_until, :naive_datetime) + field(:used, :boolean, default: false) + belongs_to(:user, Pleroma.User) + belongs_to(:app, Pleroma.App) timestamps() end def create_authorization(%App{} = app, %User{} = user) do - token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 + token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() authorization = %Authorization{ token: token, used: false, user_id: user.id, app_id: app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 60 * 10) + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) } Repo.insert(authorization) @@ -37,11 +37,12 @@ def use_changeset(%Authorization{} = auth, params) do end def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do - if NaiveDateTime.diff(NaiveDateTime.utc_now, valid_until) < 0 do + if NaiveDateTime.diff(NaiveDateTime.utc_now(), valid_until) < 0 do Repo.update(use_changeset(auth, %{used: true})) else {:error, "token expired"} end end + def use_token(%Authorization{used: true}), do: {:error, "already used"} end diff --git a/lib/pleroma/web/oauth/fallback_controller.ex b/lib/pleroma/web/oauth/fallback_controller.ex index daa110532..3927cdb64 100644 --- a/lib/pleroma/web/oauth/fallback_controller.ex +++ b/lib/pleroma/web/oauth/fallback_controller.ex @@ -1,12 +1,11 @@ defmodule Pleroma.Web.OAuth.FallbackController do - use Pleroma.Web, :controller - alias Pleroma.Web.OAuth.OAuthController + use Pleroma.Web, :controller + alias Pleroma.Web.OAuth.OAuthController - # No user/password - def call(conn, _) do - conn - |> put_flash(:error, "Invalid Username/Password") - |> OAuthController.authorize(conn.params) - end - -end \ No newline at end of file + # No user/password + def call(conn, _) do + conn + |> put_flash(:error, "Invalid Username/Password") + |> OAuthController.authorize(conn.params) + end +end diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index cebc18252..05f366611 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -5,38 +5,49 @@ defmodule Pleroma.Web.OAuth.OAuthController do alias Pleroma.{Repo, User} alias Comeonin.Pbkdf2 - plug :fetch_session - plug :fetch_flash + plug(:fetch_session) + plug(:fetch_flash) - action_fallback Pleroma.Web.OAuth.FallbackController + action_fallback(Pleroma.Web.OAuth.FallbackController) def authorize(conn, params) do - render conn, "show.html", %{ + render(conn, "show.html", %{ response_type: params["response_type"], client_id: params["client_id"], scope: params["scope"], redirect_uri: params["redirect_uri"], state: params["state"] - } + }) end - def create_authorization(conn, %{"authorization" => %{"name" => name, "password" => password, "client_id" => client_id, "redirect_uri" => redirect_uri} = params}) do + def create_authorization(conn, %{ + "authorization" => + %{ + "name" => name, + "password" => password, + "client_id" => client_id, + "redirect_uri" => redirect_uri + } = params + }) do with %User{} = user <- User.get_cached_by_nickname(name), true <- Pbkdf2.checkpw(password, user.password_hash), %App{} = app <- Repo.get_by(App, client_id: client_id), {:ok, auth} <- Authorization.create_authorization(app, user) do if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" do - render conn, "results.html", %{ + render(conn, "results.html", %{ auth: auth - } + }) else connector = if String.contains?(redirect_uri, "?"), do: "&", else: "?" url = "#{redirect_uri}#{connector}code=#{auth.token}" - url = if params["state"] do - url <> "&state=#{params["state"]}" - else - url - end + + url = + if params["state"] do + url <> "&state=#{params["state"]}" + else + url + end + redirect(conn, external: url) end end @@ -45,7 +56,12 @@ def create_authorization(conn, %{"authorization" => %{"name" => name, "password" # TODO # - proper scope handling def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do - with %App{} = app <- Repo.get_by(App, client_id: params["client_id"], client_secret: params["client_secret"]), + with %App{} = app <- + Repo.get_by( + App, + client_id: params["client_id"], + client_secret: params["client_secret"] + ), fixed_token = fix_padding(params["code"]), %Authorization{} = auth <- Repo.get_by(Authorization, token: fixed_token, app_id: app.id), {:ok, token} <- Token.exchange_token(app, auth) do @@ -56,6 +72,7 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do expires_in: 60 * 10, scope: "read write follow" } + json(conn, response) else _error -> json(conn, %{error: "Invalid credentials"}) @@ -64,8 +81,16 @@ def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do # TODO # - investigate a way to verify the user wants to grant read/write/follow once scope handling is done - def token_exchange(conn, %{"grant_type" => "password", "name" => name, "password" => password} = params) do - with %App{} = app <- Repo.get_by(App, client_id: params["client_id"], client_secret: params["client_secret"]), + def token_exchange( + conn, + %{"grant_type" => "password", "name" => name, "password" => password} = params + ) do + with %App{} = app <- + Repo.get_by( + App, + client_id: params["client_id"], + client_secret: params["client_secret"] + ), %User{} = user <- User.get_cached_by_nickname(name), true <- Pbkdf2.checkpw(password, user.password_hash), {:ok, auth} <- Authorization.create_authorization(app, user), @@ -77,6 +102,7 @@ def token_exchange(conn, %{"grant_type" => "password", "name" => name, "password expires_in: 60 * 10, scope: "read write follow" } + json(conn, response) else _error -> json(conn, %{error: "Invalid credentials"}) @@ -86,6 +112,6 @@ def token_exchange(conn, %{"grant_type" => "password", "name" => name, "password defp fix_padding(token) do token |> Base.url_decode64!(padding: false) - |> Base.url_encode64 + |> Base.url_encode64() end end diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index 828a966fb..65abd78c8 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -5,11 +5,11 @@ defmodule Pleroma.Web.OAuth.Token do alias Pleroma.Web.OAuth.{Token, App, Authorization} schema "oauth_tokens" do - field :token, :string - field :refresh_token, :string - field :valid_until, :naive_datetime - belongs_to :user, Pleroma.User - belongs_to :app, Pleroma.App + field(:token, :string) + field(:refresh_token, :string) + field(:valid_until, :naive_datetime) + belongs_to(:user, Pleroma.User) + belongs_to(:app, Pleroma.App) timestamps() end @@ -22,15 +22,15 @@ def exchange_token(app, auth) do end def create_token(%App{} = app, %User{} = user) do - token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 - refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64 + token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() + refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64() token = %Token{ token: token, refresh_token: refresh_token, user_id: user.id, app_id: app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 60 * 10) + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 60 * 10) } Repo.insert(token) diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index c8ade52a4..2f28c456e 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -5,7 +5,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do require Logger defp get_href(id) do - with %Object{data: %{ "external_url" => external_url } }<- Object.get_cached_by_ap_id(id) do + with %Object{data: %{"external_url" => external_url}} <- Object.get_cached_by_ap_id(id) do external_url else _e -> id @@ -13,42 +13,60 @@ defp get_href(id) do end defp get_in_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}}) do - [{:"thr:in-reply-to", [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []}] + [ + {:"thr:in-reply-to", + [ref: to_charlist(in_reply_to), href: to_charlist(get_href(in_reply_to))], []} + ] end defp get_in_reply_to(_), do: [] defp get_mentions(to) do - Enum.map(to, fn (id) -> + Enum.map(to, fn id -> cond do # Special handling for the AP/Ostatus public collections "https://www.w3.org/ns/activitystreams#Public" == id -> - {:link, [rel: "mentioned", "ostatus:object-type": "http://activitystrea.ms/schema/1.0/collection", href: "http://activityschema.org/collection/public"], []} + {:link, + [ + rel: "mentioned", + "ostatus:object-type": "http://activitystrea.ms/schema/1.0/collection", + href: "http://activityschema.org/collection/public" + ], []} + # Ostatus doesn't handle follower collections, ignore these. - Regex.match?(~r/^#{Pleroma.Web.base_url}.+followers$/, id) -> + Regex.match?(~r/^#{Pleroma.Web.base_url()}.+followers$/, id) -> [] + true -> - {:link, [rel: "mentioned", "ostatus:object-type": "http://activitystrea.ms/schema/1.0/person", href: id], []} + {:link, + [ + rel: "mentioned", + "ostatus:object-type": "http://activitystrea.ms/schema/1.0/person", + href: id + ], []} end end) end defp get_links(%{local: true, data: data}) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end + [ {:link, [type: ['application/atom+xml'], href: h.(data["object"]["id"]), rel: 'self'], []}, {:link, [type: ['text/html'], href: h.(data["object"]["id"]), rel: 'alternate'], []} ] end - defp get_links(%{local: false, - data: %{ - "object" => %{ - "external_url" => external_url - } - }}) do + defp get_links(%{ + local: false, + data: %{ + "object" => %{ + "external_url" => external_url + } + } + }) do + h = fn str -> [to_charlist(str)] end - h = fn(str) -> [to_charlist(str)] end [ {:link, [type: ['text/html'], href: h.(external_url), rel: 'alternate'], []} ] @@ -57,60 +75,72 @@ defp get_links(%{local: false, defp get_links(_activity), do: [] defp get_emoji_links(emojis) do - Enum.map(emojis, fn({emoji, file}) -> + Enum.map(emojis, fn {emoji, file} -> {:link, [name: to_charlist(emoji), rel: 'emoji', href: to_charlist(file)], []} end) end def to_simple_form(activity, user, with_author \\ false) + def to_simple_form(%{data: %{"object" => %{"type" => "Note"}}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["object"]["published"] inserted_at = activity.data["object"]["published"] - attachments = Enum.map(activity.data["object"]["attachment"] || [], fn(attachment) -> - url = hd(attachment["url"]) - {:link, [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])], []} - end) + attachments = + Enum.map(activity.data["object"]["attachment"] || [], fn attachment -> + url = hd(attachment["url"]) + + {:link, + [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])], + []} + end) in_reply_to = get_in_reply_to(activity.data) author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] mentions = activity.recipients |> get_mentions - categories = (activity.data["object"]["tag"] || []) - |> Enum.map(fn (tag) -> - if is_binary(tag) do - {:category, [term: to_charlist(tag)], []} - else - nil - end - end) - |> Enum.filter(&(&1)) + categories = + (activity.data["object"]["tag"] || []) + |> Enum.map(fn tag -> + if is_binary(tag) do + {:category, [term: to_charlist(tag)], []} + else + nil + end + end) + |> Enum.filter(& &1) emoji_links = get_emoji_links(activity.data["object"]["emoji"] || %{}) - summary = if activity.data["object"]["summary"] do - [{:summary, [], h.(activity.data["object"]["summary"])}] - else - [] - end + summary = + if activity.data["object"]["summary"] do + [{:summary, [], h.(activity.data["object"]["summary"])}] + else + [] + end [ {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']}, - {:id, h.(activity.data["object"]["id"])}, # For notes, federate the object id. + # For notes, federate the object id. + {:id, h.(activity.data["object"]["id"])}, {:title, ['New note by #{user.nickname}']}, - {:content, [type: 'html'], h.(activity.data["object"]["content"] |> String.replace(~r/[\n\r]/, ""))}, + {:content, [type: 'html'], + h.(activity.data["object"]["content"] |> String.replace(~r/[\n\r]/, ""))}, {:published, h.(inserted_at)}, {:updated, h.(updated_at)}, {:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])}, - {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, - ] ++ summary ++ get_links(activity) ++ categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links + {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []} + ] ++ + summary ++ + get_links(activity) ++ + categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links end def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] inserted_at = activity.data["published"] @@ -126,10 +156,12 @@ def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) d {:content, [type: 'html'], ['#{user.nickname} favorited something']}, {:published, h.(inserted_at)}, {:updated, h.(updated_at)}, - {:"activity:object", [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, - {:id, h.(activity.data["object"])}, # For notes, federate the object id. - ]}, + {:"activity:object", + [ + {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, + # For notes, federate the object id. + {:id, h.(activity.data["object"])} + ]}, {:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])}, {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, @@ -138,7 +170,7 @@ def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) d end def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] inserted_at = activity.data["published"] @@ -152,6 +184,7 @@ def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_autho retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true) mentions = activity.recipients |> get_mentions + [ {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']}, @@ -168,7 +201,7 @@ def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_autho end def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] inserted_at = activity.data["published"] @@ -176,26 +209,29 @@ def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] mentions = (activity.recipients || []) |> get_mentions + [ {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']}, {:id, h.(activity.data["id"])}, {:title, ['#{user.nickname} started following #{activity.data["object"]}']}, - {:content, [type: 'html'], ['#{user.nickname} started following #{activity.data["object"]}']}, + {:content, [type: 'html'], + ['#{user.nickname} started following #{activity.data["object"]}']}, {:published, h.(inserted_at)}, {:updated, h.(updated_at)}, - {:"activity:object", [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, - {:id, h.(activity.data["object"])}, - {:uri, h.(activity.data["object"])}, - ]}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, + {:"activity:object", + [ + {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, + {:id, h.(activity.data["object"])}, + {:uri, h.(activity.data["object"])} + ]}, + {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} ] ++ mentions ++ author end # Only undos of follow for now. Will need to get redone once there are more def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] inserted_at = activity.data["published"] @@ -204,25 +240,28 @@ def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) d follow_activity = Activity.get_by_ap_id(activity.data["object"]) mentions = (activity.recipients || []) |> get_mentions + [ {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']}, {:id, h.(activity.data["id"])}, {:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, - {:content, [type: 'html'], ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, + {:content, [type: 'html'], + ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, {:published, h.(inserted_at)}, {:updated, h.(updated_at)}, - {:"activity:object", [ - {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, - {:id, h.(follow_activity.data["object"])}, - {:uri, h.(follow_activity.data["object"])}, - ]}, - {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, + {:"activity:object", + [ + {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, + {:id, h.(follow_activity.data["object"])}, + {:uri, h.(follow_activity.data["object"])} + ]}, + {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []} ] ++ mentions ++ author end def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end updated_at = activity.data["published"] inserted_at = activity.data["published"] @@ -237,20 +276,24 @@ def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) {:content, [type: 'html'], ['An object was deleted']}, {:published, h.(inserted_at)}, {:updated, h.(updated_at)} - ] ++ author + ] ++ author end def to_simple_form(_, _, _), do: nil def wrap_with_entry(simple_form) do - [{ - :entry, [ - xmlns: 'http://www.w3.org/2005/Atom', - "xmlns:thr": 'http://purl.org/syndication/thread/1.0', - "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', - "xmlns:poco": 'http://portablecontacts.net/spec/1.0', - "xmlns:ostatus": 'http://ostatus.org/schema/1.0' - ], simple_form - }] + [ + { + :entry, + [ + xmlns: 'http://www.w3.org/2005/Atom', + "xmlns:thr": 'http://purl.org/syndication/thread/1.0', + "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', + "xmlns:poco": 'http://portablecontacts.net/spec/1.0', + "xmlns:ostatus": 'http://ostatus.org/schema/1.0' + ], + simple_form + } + ] end end diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex index 8461b2b9f..279672673 100644 --- a/lib/pleroma/web/ostatus/feed_representer.ex +++ b/lib/pleroma/web/ostatus/feed_representer.ex @@ -5,44 +5,57 @@ defmodule Pleroma.Web.OStatus.FeedRepresenter do alias Pleroma.Web.MediaProxy def to_simple_form(user, activities, _users) do - most_recent_update = (List.first(activities) || user).updated_at - |> NaiveDateTime.to_iso8601 + most_recent_update = + (List.first(activities) || user).updated_at + |> NaiveDateTime.to_iso8601() - h = fn(str) -> [to_charlist(str)] end + h = fn str -> [to_charlist(str)] end last_activity = List.last(activities) - entries = activities - |> Enum.map(fn(activity) -> - {:entry, ActivityRepresenter.to_simple_form(activity, user)} - end) - |> Enum.filter(fn ({_, form}) -> form end) + entries = + activities + |> Enum.map(fn activity -> + {:entry, ActivityRepresenter.to_simple_form(activity, user)} + end) + |> Enum.filter(fn {_, form} -> form end) - [{ - :feed, [ - xmlns: 'http://www.w3.org/2005/Atom', - "xmlns:thr": 'http://purl.org/syndication/thread/1.0', - "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', - "xmlns:poco": 'http://portablecontacts.net/spec/1.0', - "xmlns:ostatus": 'http://ostatus.org/schema/1.0' - ], [ - {:id, h.(OStatus.feed_path(user))}, - {:title, ['#{user.nickname}\'s timeline']}, - {:updated, h.(most_recent_update)}, - {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]}, - {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []}, - {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []}, - {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'], []}, - {:author, UserRepresenter.to_simple_form(user)}, - ] ++ - if last_activity do - [{:link, [rel: 'next', - href: to_charlist(OStatus.feed_path(user)) ++ '?max_id=' ++ to_charlist(last_activity.id), - type: 'application/atom+xml'], []}] - else - [] - end - ++ entries - }] + [ + { + :feed, + [ + xmlns: 'http://www.w3.org/2005/Atom', + "xmlns:thr": 'http://purl.org/syndication/thread/1.0', + "xmlns:activity": 'http://activitystrea.ms/spec/1.0/', + "xmlns:poco": 'http://portablecontacts.net/spec/1.0', + "xmlns:ostatus": 'http://ostatus.org/schema/1.0' + ], + [ + {:id, h.(OStatus.feed_path(user))}, + {:title, ['#{user.nickname}\'s timeline']}, + {:updated, h.(most_recent_update)}, + {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]}, + {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []}, + {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []}, + {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'], + []}, + {:author, UserRepresenter.to_simple_form(user)} + ] ++ + if last_activity do + [ + {:link, + [ + rel: 'next', + href: + to_charlist(OStatus.feed_path(user)) ++ + '?max_id=' ++ to_charlist(last_activity.id), + type: 'application/atom+xml' + ], []} + ] + else + [] + end ++ entries + } + ] end end diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex index f5db5582b..162407e04 100644 --- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex @@ -6,7 +6,8 @@ defmodule Pleroma.Web.OStatus.FollowHandler do def handle(entry, doc) do with {:ok, actor} <- OStatus.find_make_or_update_user(doc), id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), - followed_uri when not is_nil(followed_uri) <- XML.string_from_xpath("/entry/activity:object/id", entry), + followed_uri when not is_nil(followed_uri) <- + XML.string_from_xpath("/entry/activity:object/id", entry), {:ok, followed} <- OStatus.find_or_make_user(followed_uri), {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do User.follow(actor, followed) diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex index 38f9fc478..b012abd51 100644 --- a/lib/pleroma/web/ostatus/handlers/note_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex @@ -13,49 +13,56 @@ defmodule Pleroma.Web.OStatus.NoteHandler do 3. A newly generated context id. """ def get_context(entry, inReplyTo) do - context = ( - XML.string_from_xpath("//ostatus:conversation[1]", entry) - || XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) - || "") |> String.trim + context = + (XML.string_from_xpath("//ostatus:conversation[1]", entry) || + XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "") + |> String.trim() with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(inReplyTo) do context - else _e -> - if String.length(context) > 0 do - context - else - Utils.generate_context_id - end + else + _e -> + if String.length(context) > 0 do + context + else + Utils.generate_context_id() + end end end def get_people_mentions(entry) do - :xmerl_xpath.string('//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]', entry) - |> Enum.map(fn(person) -> XML.string_from_xpath("@href", person) end) + :xmerl_xpath.string( + '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]', + entry + ) + |> Enum.map(fn person -> XML.string_from_xpath("@href", person) end) end def get_collection_mentions(entry) do transmogrify = fn - ("http://activityschema.org/collection/public") -> + "http://activityschema.org/collection/public" -> "https://www.w3.org/ns/activitystreams#Public" - (group) -> + + group -> group end - :xmerl_xpath.string('//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]', entry) - |> Enum.map(fn(collection) -> XML.string_from_xpath("@href", collection) |> transmogrify.() end) + :xmerl_xpath.string( + '//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]', + entry + ) + |> Enum.map(fn collection -> XML.string_from_xpath("@href", collection) |> transmogrify.() end) end def get_mentions(entry) do - (get_people_mentions(entry) - ++ get_collection_mentions(entry)) - |> Enum.filter(&(&1)) + (get_people_mentions(entry) ++ get_collection_mentions(entry)) + |> Enum.filter(& &1) end def get_emoji(entry) do try do :xmerl_xpath.string('//link[@rel="emoji"]', entry) - |> Enum.reduce(%{}, fn(emoji, acc) -> + |> Enum.reduce(%{}, fn emoji, acc -> Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji)) end) rescue @@ -79,7 +86,8 @@ def fetch_replied_to_activity(entry, inReplyTo) do activity else _e -> - with inReplyToHref when not is_nil(inReplyToHref) <- XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry), + with inReplyToHref when not is_nil(inReplyToHref) <- + XML.string_from_xpath("//thr:in-reply-to[1]/@href", entry), {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(inReplyToHref) do activity else @@ -107,16 +115,40 @@ def handle_note(entry, doc \\ nil) do date <- XML.string_from_xpath("//published", entry), unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted", cc <- if(unlisted, do: ["https://www.w3.org/ns/activitystreams#Public"], else: []), - note <- CommonAPI.Utils.make_note_data(actor.ap_id, to, context, content_html, attachments, inReplyToActivity, [], cw), + note <- + CommonAPI.Utils.make_note_data( + actor.ap_id, + to, + context, + content_html, + attachments, + inReplyToActivity, + [], + cw + ), note <- note |> Map.put("id", id) |> Map.put("tag", tags), note <- note |> Map.put("published", date), note <- note |> Map.put("emoji", get_emoji(entry)), note <- add_external_url(note, entry), note <- note |> Map.put("cc", cc), # TODO: Handle this case in make_note_data - note <- (if inReplyTo && !inReplyToActivity, do: note |> Map.put("inReplyTo", inReplyTo), else: note) - do - res = ActivityPub.create(%{to: to, actor: actor, context: context, object: note, published: date, local: false, additional: %{"cc" => cc}}) + note <- + if( + inReplyTo && !inReplyToActivity, + do: note |> Map.put("inReplyTo", inReplyTo), + else: note + ) do + res = + ActivityPub.create(%{ + to: to, + actor: actor, + context: context, + object: note, + published: date, + local: false, + additional: %{"cc" => cc} + }) + User.increase_note_count(actor) res else diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index f3ef4d690..5c4a1fd69 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -16,7 +16,7 @@ def feed_path(user) do end def pubsub_path(user) do - "#{Web.base_url}/push/hub/#{user.nickname}" + "#{Web.base_url()}/push/hub/#{user.nickname}" end def salmon_path(user) do @@ -24,48 +24,59 @@ def salmon_path(user) do end def remote_follow_path do - "#{Web.base_url}/ostatus_subscribe?acct={uri}" + "#{Web.base_url()}/ostatus_subscribe?acct={uri}" end def handle_incoming(xml_string) do with doc when doc != :error <- parse_document(xml_string) do entries = :xmerl_xpath.string('//entry', doc) - activities = Enum.map(entries, fn (entry) -> - {:xmlObj, :string, object_type} = :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry) - {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry) - Logger.debug("Handling #{verb}") + activities = + Enum.map(entries, fn entry -> + {:xmlObj, :string, object_type} = + :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry) - try do - case verb do - 'http://activitystrea.ms/schema/1.0/delete' -> - with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity - 'http://activitystrea.ms/schema/1.0/follow' -> - with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity - 'http://activitystrea.ms/schema/1.0/share' -> - with {:ok, activity, retweeted_activity} <- handle_share(entry, doc), do: [activity, retweeted_activity] - 'http://activitystrea.ms/schema/1.0/favorite' -> - with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc), do: [activity, favorited_activity] - _ -> - case object_type do - 'http://activitystrea.ms/schema/1.0/note' -> - with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity - 'http://activitystrea.ms/schema/1.0/comment' -> - with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity - _ -> - Logger.error("Couldn't parse incoming document") - nil - end + {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry) + Logger.debug("Handling #{verb}") + + try do + case verb do + 'http://activitystrea.ms/schema/1.0/delete' -> + with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity + + 'http://activitystrea.ms/schema/1.0/follow' -> + with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity + + 'http://activitystrea.ms/schema/1.0/share' -> + with {:ok, activity, retweeted_activity} <- handle_share(entry, doc), + do: [activity, retweeted_activity] + + 'http://activitystrea.ms/schema/1.0/favorite' -> + with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc), + do: [activity, favorited_activity] + + _ -> + case object_type do + 'http://activitystrea.ms/schema/1.0/note' -> + with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity + + 'http://activitystrea.ms/schema/1.0/comment' -> + with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity + + _ -> + Logger.error("Couldn't parse incoming document") + nil + end + end + rescue + e -> + Logger.error("Error occured while handling activity") + Logger.error(xml_string) + Logger.error(inspect(e)) + nil end - rescue - e -> - Logger.error("Error occured while handling activity") - Logger.error(xml_string) - Logger.error(inspect(e)) - nil - end - end) - |> Enum.filter(&(&1)) + end) + |> Enum.filter(& &1) {:ok, activities} else @@ -113,15 +124,20 @@ def get_or_build_object(entry) do def get_or_try_fetching(entry) do Logger.debug("Trying to get entry from db") + with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do {:ok, activity} - else _ -> + else + _ -> Logger.debug("Couldn't get, will try to fetch") - with href when not is_nil(href) <- string_from_xpath("//activity:object[1]/link[@type=\"text/html\"]/@href", entry), + + with href when not is_nil(href) <- + string_from_xpath("//activity:object[1]/link[@type=\"text/html\"]/@href", entry), {:ok, [favorited_activity]} <- fetch_activity_from_url(href) do {:ok, favorited_activity} - else e -> Logger.debug("Couldn't find href: #{inspect(e)}") + else + e -> Logger.debug("Couldn't find href: #{inspect(e)}") end end end @@ -137,20 +153,22 @@ def handle_favorite(entry, doc) do def get_attachments(entry) do :xmerl_xpath.string('/entry/link[@rel="enclosure"]', entry) - |> Enum.map(fn (enclosure) -> + |> Enum.map(fn enclosure -> with href when not is_nil(href) <- string_from_xpath("/link/@href", enclosure), type when not is_nil(type) <- string_from_xpath("/link/@type", enclosure) do %{ "type" => "Attachment", - "url" => [%{ - "type" => "Link", - "mediaType" => type, - "href" => href - }] + "url" => [ + %{ + "type" => "Link", + "mediaType" => type, + "href" => href + } + ] } end end) - |> Enum.filter(&(&1)) + |> Enum.filter(& &1) end @doc """ @@ -166,14 +184,15 @@ def get_content(entry) do def get_cw(entry) do with cw when not is_nil(cw) <- string_from_xpath("/*/summary", entry) do cw - else _e -> nil + else + _e -> nil end end def get_tags(entry) do :xmerl_xpath.string('//category', entry) - |> Enum.map(fn (category) -> string_from_xpath("/category/@term", category) end) - |> Enum.filter(&(&1)) + |> Enum.map(fn category -> string_from_xpath("/category/@term", category) end) + |> Enum.filter(& &1) |> Enum.map(&String.downcase/1) end @@ -184,6 +203,7 @@ def maybe_update(doc, user) do maybe_update_ostatus(doc, user) end end + def maybe_update_ostatus(doc, user) do old_data = %{ avatar: user.avatar, @@ -196,26 +216,33 @@ def maybe_update_ostatus(doc, user) do avatar <- make_avatar_object(doc), bio <- string_from_xpath("//author[1]/summary", doc), name <- string_from_xpath("//author[1]/poco:displayName", doc), - info <- Map.put(user.info, "banner", make_avatar_object(doc, "header") || user.info["banner"]), - new_data <- %{avatar: avatar || old_data.avatar, name: name || old_data.name, bio: bio || old_data.bio, info: info || old_data.info}, + info <- + Map.put(user.info, "banner", make_avatar_object(doc, "header") || user.info["banner"]), + new_data <- %{ + avatar: avatar || old_data.avatar, + name: name || old_data.name, + bio: bio || old_data.bio, + info: info || old_data.info + }, false <- new_data == old_data do change = Ecto.Changeset.change(user, new_data) Repo.update(change) - else _ -> - {:ok, user} + else + _ -> + {:ok, user} end end def find_make_or_update_user(doc) do uri = string_from_xpath("//author/uri[1]", doc) + with {:ok, user} <- find_or_make_user(uri) do maybe_update(doc, user) end end def find_or_make_user(uri) do - query = from user in User, - where: user.ap_id == ^uri + query = from(user in User, where: user.ap_id == ^uri) user = Repo.one(query) @@ -236,10 +263,12 @@ def make_user(uri, update \\ false) do avatar: info["avatar"], bio: info["bio"] } + with false <- update, %User{} = user <- User.get_by_ap_id(data.ap_id) do {:ok, user} - else _e -> User.insert_or_update_user(data) + else + _e -> User.insert_or_update_user(data) end end end @@ -252,12 +281,13 @@ def make_avatar_object(author_doc, rel \\ "avatar") do if href do %{ "type" => "Image", - "url" => - [%{ - "type" => "Link", - "mediaType" => type, - "href" => href - }] + "url" => [ + %{ + "type" => "Link", + "mediaType" => type, + "href" => href + } + ] } else nil @@ -268,9 +298,10 @@ def gather_user_info(username) do with {:ok, webfinger_data} <- WebFinger.finger(username), {:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do {:ok, Map.merge(webfinger_data, feed_data) |> Map.put("fqn", username)} - else e -> - Logger.debug(fn -> "Couldn't gather info for #{username}" end) - {:error, e} + else + e -> + Logger.debug(fn -> "Couldn't gather info for #{username}" end) + {:error, e} end end @@ -284,12 +315,15 @@ def get_atom_url(body) do Regex.match?(@mastodon_regex, body) -> [[_, match]] = Regex.scan(@mastodon_regex, body) {:ok, match} + Regex.match?(@gs_regex, body) -> [[_, match]] = Regex.scan(@gs_regex, body) {:ok, match} + Regex.match?(@gs_classic_regex, body) -> [[_, match]] = Regex.scan(@gs_classic_regex, body) {:ok, match} + true -> Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end) {:error, "Couldn't find the Atom link"} @@ -298,7 +332,14 @@ def get_atom_url(body) 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 <- @httpoison.get(url, [Accept: "application/atom+xml"], follow_redirect: true, timeout: 10000, recv_timeout: 20000) do + {:ok, %{body: body, status_code: code}} when code in 200..299 <- + @httpoison.get( + url, + [Accept: "application/atom+xml"], + follow_redirect: true, + timeout: 10000, + recv_timeout: 20000 + ) do Logger.debug("Got document from #{url}, handling...") handle_incoming(body) else @@ -310,10 +351,12 @@ def fetch_activity_from_atom_url(url) do def fetch_activity_from_html_url(url) 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, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000), {:ok, atom_url} <- get_atom_url(body) do - fetch_activity_from_atom_url(atom_url) + fetch_activity_from_atom_url(atom_url) else e -> Logger.debug("Couldn't get #{url}: #{inspect(e)}") @@ -326,9 +369,10 @@ def fetch_activity_from_url(url) 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 + _e -> + with {:ok, activities} <- fetch_activity_from_html_url(url) do + {:ok, activities} + end end rescue e -> diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 7e71c156c..a02f55fe6 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -16,23 +16,26 @@ def feed_redirect(conn, %{"nickname" => nickname} = params) do case get_format(conn) do "html" -> Fallback.RedirectController.redirector(conn, nil) "activity+json" -> ActivityPubController.user(conn, params) - _ -> redirect conn, external: OStatus.feed_path(user) + _ -> redirect(conn, external: OStatus.feed_path(user)) end end def feed(conn, %{"nickname" => nickname} = params) do user = User.get_cached_by_nickname(nickname) - query_params = Map.take(params, ["max_id"]) - |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) + query_params = + Map.take(params, ["max_id"]) + |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) - activities = ActivityPub.fetch_public_activities(query_params) - |> Enum.reverse + activities = + ActivityPub.fetch_public_activities(query_params) + |> Enum.reverse() - response = user - |> FeedRepresenter.to_simple_form(activities, [user]) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string + response = + user + |> FeedRepresenter.to_simple_form(activities, [user]) + |> :xmerl.export_simple(:xmerl_xml) + |> to_string conn |> put_resp_content_type("application/atom+xml") @@ -73,7 +76,7 @@ def object(conn, %{"uuid" => uuid} = params) do else with id <- o_status_url(conn, :object, uuid), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id), - %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do + %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case get_format(conn) do "html" -> redirect(conn, to: "/notice/#{activity.id}") _ -> represent_activity(conn, activity, user) @@ -96,24 +99,27 @@ def activity(conn, %{"uuid" => uuid}) do # TODO: Data leak def notice(conn, %{"id" => id}) do - with %Activity{} = activity <- Repo.get(Activity, id), - %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do + with %Activity{} = activity <- Repo.get(Activity, id), + %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case get_format(conn) do "html" -> conn |> put_resp_content_type("text/html") |> send_file(200, "priv/static/index.html") - _ -> represent_activity(conn, activity, user) + + _ -> + represent_activity(conn, activity, user) end end end defp represent_activity(conn, activity, user) do - response = activity - |> ActivityRepresenter.to_simple_form(user, true) - |> ActivityRepresenter.wrap_with_entry - |> :xmerl.export_simple(:xmerl_xml) - |> to_string + response = + activity + |> ActivityRepresenter.to_simple_form(user, true) + |> ActivityRepresenter.wrap_with_entry() + |> :xmerl.export_simple(:xmerl_xml) + |> to_string conn |> put_resp_content_type("application/atom+xml") diff --git a/lib/pleroma/web/ostatus/user_representer.ex b/lib/pleroma/web/ostatus/user_representer.ex index 5af439c9d..2e696506e 100644 --- a/lib/pleroma/web/ostatus/user_representer.ex +++ b/lib/pleroma/web/ostatus/user_representer.ex @@ -1,22 +1,26 @@ defmodule Pleroma.Web.OStatus.UserRepresenter do alias Pleroma.User + def to_simple_form(user) do ap_id = to_charlist(user.ap_id) nickname = to_charlist(user.nickname) name = to_charlist(user.name) bio = to_charlist(user.bio) avatar_url = to_charlist(User.avatar_url(user)) - banner = if banner_url = User.banner_url(user) do - [{:link, [rel: 'header', href: banner_url], []}] - else - [] - end - ap_enabled = if user.local do - [{:ap_enabled, ['true']}] - else - [] - end + banner = + if banner_url = User.banner_url(user) do + [{:link, [rel: 'header', href: banner_url], []}] + else + [] + end + + ap_enabled = + if user.local do + [{:ap_enabled, ['true']}] + else + [] + end [ {:id, [ap_id]}, diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 22a5257d5..5f27f3caa 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -11,294 +11,305 @@ def user_fetcher(username) do end pipeline :api do - plug :accepts, ["json"] - plug :fetch_session - plug Pleroma.Plugs.OAuthPlug - plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} + plug(:accepts, ["json"]) + plug(:fetch_session) + plug(Pleroma.Plugs.OAuthPlug) + plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true}) end pipeline :authenticated_api do - plug :accepts, ["json"] - plug :fetch_session - plug Pleroma.Plugs.OAuthPlug - plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1} + plug(:accepts, ["json"]) + plug(:fetch_session) + plug(Pleroma.Plugs.OAuthPlug) + plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1}) end pipeline :mastodon_html do - plug :accepts, ["html"] - plug :fetch_session - plug Pleroma.Plugs.OAuthPlug - plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(Pleroma.Plugs.OAuthPlug) + plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true}) end pipeline :pleroma_html do - plug :accepts, ["html"] - plug :fetch_session - plug Pleroma.Plugs.OAuthPlug - plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} + plug(:accepts, ["html"]) + plug(:fetch_session) + plug(Pleroma.Plugs.OAuthPlug) + plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true}) end pipeline :well_known do - plug :accepts, ["xml", "xrd+xml", "json", "jrd+json"] + plug(:accepts, ["xml", "xrd+xml", "json", "jrd+json"]) end pipeline :config do - plug :accepts, ["json", "xml"] + plug(:accepts, ["json", "xml"]) end pipeline :oauth do - plug :accepts, ["html", "json"] + plug(:accepts, ["html", "json"]) end pipeline :pleroma_api do - plug :accepts, ["html", "json"] + plug(:accepts, ["html", "json"]) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do - pipe_through :pleroma_api - get "/password_reset/:token", UtilController, :show_password_reset - post "/password_reset", UtilController, :password_reset - get "/emoji", UtilController, :emoji + pipe_through(:pleroma_api) + get("/password_reset/:token", UtilController, :show_password_reset) + post("/password_reset", UtilController, :password_reset) + get("/emoji", UtilController, :emoji) end scope "/", Pleroma.Web.TwitterAPI do - pipe_through :pleroma_html - get "/ostatus_subscribe", UtilController, :remote_follow - post "/ostatus_subscribe", UtilController, :do_remote_follow - post "/main/ostatus", UtilController, :remote_subscribe + pipe_through(:pleroma_html) + get("/ostatus_subscribe", UtilController, :remote_follow) + post("/ostatus_subscribe", UtilController, :do_remote_follow) + post("/main/ostatus", UtilController, :remote_subscribe) end scope "/api/pleroma", Pleroma.Web.TwitterAPI do - pipe_through :authenticated_api - post "/follow_import", UtilController, :follow_import + pipe_through(:authenticated_api) + post("/follow_import", UtilController, :follow_import) end scope "/oauth", Pleroma.Web.OAuth do - get "/authorize", OAuthController, :authorize - post "/authorize", OAuthController, :create_authorization - post "/token", OAuthController, :token_exchange + get("/authorize", OAuthController, :authorize) + post("/authorize", OAuthController, :create_authorization) + post("/token", OAuthController, :token_exchange) end scope "/api/v1", Pleroma.Web.MastodonAPI do - pipe_through :authenticated_api + pipe_through(:authenticated_api) - patch "/accounts/update_credentials", MastodonAPIController, :update_credentials - get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials - get "/accounts/relationships", MastodonAPIController, :relationships - get "/accounts/search", MastodonAPIController, :account_search - post "/accounts/:id/follow", MastodonAPIController, :follow - post "/accounts/:id/unfollow", MastodonAPIController, :unfollow - post "/accounts/:id/block", MastodonAPIController, :block - post "/accounts/:id/unblock", MastodonAPIController, :unblock - post "/accounts/:id/mute", MastodonAPIController, :relationship_noop - post "/accounts/:id/unmute", MastodonAPIController, :relationship_noop + patch("/accounts/update_credentials", MastodonAPIController, :update_credentials) + get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials) + get("/accounts/relationships", MastodonAPIController, :relationships) + get("/accounts/search", MastodonAPIController, :account_search) + post("/accounts/:id/follow", MastodonAPIController, :follow) + post("/accounts/:id/unfollow", MastodonAPIController, :unfollow) + post("/accounts/:id/block", MastodonAPIController, :block) + post("/accounts/:id/unblock", MastodonAPIController, :unblock) + post("/accounts/:id/mute", MastodonAPIController, :relationship_noop) + post("/accounts/:id/unmute", MastodonAPIController, :relationship_noop) - post "/follows", MastodonAPIController, :follow + post("/follows", MastodonAPIController, :follow) - get "/blocks", MastodonAPIController, :blocks + get("/blocks", MastodonAPIController, :blocks) - get "/domain_blocks", MastodonAPIController, :empty_array - get "/follow_requests", MastodonAPIController, :empty_array - get "/mutes", MastodonAPIController, :empty_array + get("/domain_blocks", MastodonAPIController, :empty_array) + get("/follow_requests", MastodonAPIController, :empty_array) + get("/mutes", MastodonAPIController, :empty_array) - get "/timelines/home", MastodonAPIController, :home_timeline + get("/timelines/home", MastodonAPIController, :home_timeline) - get "/favourites", MastodonAPIController, :favourites + get("/favourites", MastodonAPIController, :favourites) - post "/statuses", MastodonAPIController, :post_status - delete "/statuses/:id", MastodonAPIController, :delete_status + post("/statuses", MastodonAPIController, :post_status) + delete("/statuses/:id", MastodonAPIController, :delete_status) - post "/statuses/:id/reblog", MastodonAPIController, :reblog_status - post "/statuses/:id/favourite", MastodonAPIController, :fav_status - post "/statuses/:id/unfavourite", MastodonAPIController, :unfav_status + post("/statuses/:id/reblog", MastodonAPIController, :reblog_status) + post("/statuses/:id/favourite", MastodonAPIController, :fav_status) + post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) - post "/notifications/clear", MastodonAPIController, :clear_notifications - post "/notifications/dismiss", MastodonAPIController, :dismiss_notification - get "/notifications", MastodonAPIController, :notifications - get "/notifications/:id", MastodonAPIController, :get_notification + post("/notifications/clear", MastodonAPIController, :clear_notifications) + post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) + get("/notifications", MastodonAPIController, :notifications) + get("/notifications/:id", MastodonAPIController, :get_notification) - post "/media", MastodonAPIController, :upload + post("/media", MastodonAPIController, :upload) end scope "/api/v1", Pleroma.Web.MastodonAPI do - pipe_through :api - get "/instance", MastodonAPIController, :masto_instance - get "/instance/peers", MastodonAPIController, :peers - post "/apps", MastodonAPIController, :create_app - get "/custom_emojis", MastodonAPIController, :custom_emojis + pipe_through(:api) + get("/instance", MastodonAPIController, :masto_instance) + get("/instance/peers", MastodonAPIController, :peers) + post("/apps", MastodonAPIController, :create_app) + get("/custom_emojis", MastodonAPIController, :custom_emojis) - get "/timelines/public", MastodonAPIController, :public_timeline - get "/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline + get("/timelines/public", MastodonAPIController, :public_timeline) + get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline) - get "/statuses/:id", MastodonAPIController, :get_status - get "/statuses/:id/context", MastodonAPIController, :get_context - get "/statuses/:id/card", MastodonAPIController, :empty_object - get "/statuses/:id/favourited_by", MastodonAPIController, :favourited_by - get "/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by + get("/statuses/:id", MastodonAPIController, :get_status) + get("/statuses/:id/context", MastodonAPIController, :get_context) + get("/statuses/:id/card", MastodonAPIController, :empty_object) + get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by) + get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by) - get "/accounts/:id/statuses", MastodonAPIController, :user_statuses - get "/accounts/:id/followers", MastodonAPIController, :followers - get "/accounts/:id/following", MastodonAPIController, :following - get "/accounts/:id", MastodonAPIController, :user + get("/accounts/:id/statuses", MastodonAPIController, :user_statuses) + get("/accounts/:id/followers", MastodonAPIController, :followers) + get("/accounts/:id/following", MastodonAPIController, :following) + get("/accounts/:id", MastodonAPIController, :user) - get "/search", MastodonAPIController, :search + get("/search", MastodonAPIController, :search) end scope "/api", Pleroma.Web do - pipe_through :config + pipe_through(:config) - get "/help/test", TwitterAPI.UtilController, :help_test - post "/help/test", TwitterAPI.UtilController, :help_test - get "/statusnet/config", TwitterAPI.UtilController, :config - get "/statusnet/version", TwitterAPI.UtilController, :version + get("/help/test", TwitterAPI.UtilController, :help_test) + post("/help/test", TwitterAPI.UtilController, :help_test) + get("/statusnet/config", TwitterAPI.UtilController, :config) + get("/statusnet/version", TwitterAPI.UtilController, :version) end @instance Application.get_env(:pleroma, :instance) @registrations_open Keyword.get(@instance, :registrations_open) scope "/api", Pleroma.Web do - pipe_through :api + pipe_through(:api) - get "/statuses/public_timeline", TwitterAPI.Controller, :public_timeline - get "/statuses/public_and_external_timeline", TwitterAPI.Controller, :public_and_external_timeline - get "/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline - get "/statuses/user_timeline", TwitterAPI.Controller, :user_timeline - get "/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline - get "/users/show", TwitterAPI.Controller, :show_user + get("/statuses/public_timeline", TwitterAPI.Controller, :public_timeline) - get "/statuses/followers", TwitterAPI.Controller, :followers - get "/statuses/friends", TwitterAPI.Controller, :friends - get "/statuses/show/:id", TwitterAPI.Controller, :fetch_status - get "/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation + get( + "/statuses/public_and_external_timeline", + TwitterAPI.Controller, + :public_and_external_timeline + ) + + get("/statuses/networkpublic_timeline", TwitterAPI.Controller, :public_and_external_timeline) + get("/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) + get("/qvitter/statuses/user_timeline", TwitterAPI.Controller, :user_timeline) + get("/users/show", TwitterAPI.Controller, :show_user) + + get("/statuses/followers", TwitterAPI.Controller, :followers) + get("/statuses/friends", TwitterAPI.Controller, :friends) + get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status) + get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) if @registrations_open do - post "/account/register", TwitterAPI.Controller, :register + post("/account/register", TwitterAPI.Controller, :register) end - get "/search", TwitterAPI.Controller, :search - get "/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline + get("/search", TwitterAPI.Controller, :search) + get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) end scope "/api", Pleroma.Web do - pipe_through :authenticated_api + pipe_through(:authenticated_api) - get "/account/verify_credentials", TwitterAPI.Controller, :verify_credentials - post "/account/verify_credentials", TwitterAPI.Controller, :verify_credentials + get("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) + post("/account/verify_credentials", TwitterAPI.Controller, :verify_credentials) - post "/account/update_profile", TwitterAPI.Controller, :update_profile - post "/account/update_profile_banner", TwitterAPI.Controller, :update_banner - post "/qvitter/update_background_image", TwitterAPI.Controller, :update_background + post("/account/update_profile", TwitterAPI.Controller, :update_profile) + post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) + post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) - post "/account/most_recent_notification", TwitterAPI.Controller, :update_most_recent_notification + post( + "/account/most_recent_notification", + TwitterAPI.Controller, + :update_most_recent_notification + ) - get "/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline - get "/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline - get "/statuses/mentions", TwitterAPI.Controller, :mentions_timeline - get "/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline + get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) + get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) + get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) + get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline) - post "/statuses/update", TwitterAPI.Controller, :status_update - post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet - post "/statuses/destroy/:id", TwitterAPI.Controller, :delete_post + post("/statuses/update", TwitterAPI.Controller, :status_update) + post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet) + post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post) - post "/friendships/create", TwitterAPI.Controller, :follow - post "/friendships/destroy", TwitterAPI.Controller, :unfollow - post "/blocks/create", TwitterAPI.Controller, :block - post "/blocks/destroy", TwitterAPI.Controller, :unblock + post("/friendships/create", TwitterAPI.Controller, :follow) + post("/friendships/destroy", TwitterAPI.Controller, :unfollow) + post("/blocks/create", TwitterAPI.Controller, :block) + post("/blocks/destroy", TwitterAPI.Controller, :unblock) - post "/statusnet/media/upload", TwitterAPI.Controller, :upload - post "/media/upload", TwitterAPI.Controller, :upload_json + post("/statusnet/media/upload", TwitterAPI.Controller, :upload) + post("/media/upload", TwitterAPI.Controller, :upload_json) - post "/favorites/create/:id", TwitterAPI.Controller, :favorite - post "/favorites/create", TwitterAPI.Controller, :favorite - post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite + post("/favorites/create/:id", TwitterAPI.Controller, :favorite) + post("/favorites/create", TwitterAPI.Controller, :favorite) + post("/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite) - post "/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar + post("/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar) - get "/friends/ids", TwitterAPI.Controller, :friends_ids - get "/friendships/no_retweets/ids", TwitterAPI.Controller, :empty_array + get("/friends/ids", TwitterAPI.Controller, :friends_ids) + get("/friendships/no_retweets/ids", TwitterAPI.Controller, :empty_array) - get "/mutes/users/ids", TwitterAPI.Controller, :empty_array + get("/mutes/users/ids", TwitterAPI.Controller, :empty_array) - get "/externalprofile/show", TwitterAPI.Controller, :external_profile + get("/externalprofile/show", TwitterAPI.Controller, :external_profile) end pipeline :ostatus do - plug :accepts, ["xml", "atom", "html", "activity+json"] + plug(:accepts, ["xml", "atom", "html", "activity+json"]) end scope "/", Pleroma.Web do - pipe_through :ostatus + pipe_through(:ostatus) - get "/objects/:uuid", OStatus.OStatusController, :object - get "/activities/:uuid", OStatus.OStatusController, :activity - get "/notice/:id", OStatus.OStatusController, :notice - get "/users/:nickname/feed", OStatus.OStatusController, :feed - get "/users/:nickname", OStatus.OStatusController, :feed_redirect + get("/objects/:uuid", OStatus.OStatusController, :object) + get("/activities/:uuid", OStatus.OStatusController, :activity) + get("/notice/:id", OStatus.OStatusController, :notice) + get("/users/:nickname/feed", OStatus.OStatusController, :feed) + get("/users/:nickname", OStatus.OStatusController, :feed_redirect) if @federating do - post "/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming - post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request - get "/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation - post "/push/subscriptions/:id", Websub.WebsubController, :websub_incoming + post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming) + post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request) + get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation) + post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming) end - end pipeline :activitypub do - plug :accepts, ["activity+json"] - plug Pleroma.Web.Plugs.HTTPSignaturePlug + plug(:accepts, ["activity+json"]) + plug(Pleroma.Web.Plugs.HTTPSignaturePlug) end scope "/", Pleroma.Web.ActivityPub do # XXX: not really ostatus - pipe_through :ostatus + pipe_through(:ostatus) - get "/users/:nickname/followers", ActivityPubController, :followers - get "/users/:nickname/following", ActivityPubController, :following - get "/users/:nickname/outbox", ActivityPubController, :outbox + get("/users/:nickname/followers", ActivityPubController, :followers) + get("/users/:nickname/following", ActivityPubController, :following) + get("/users/:nickname/outbox", ActivityPubController, :outbox) end if @federating do scope "/", Pleroma.Web.ActivityPub do - pipe_through :activitypub - post "/users/:nickname/inbox", ActivityPubController, :inbox - post "/inbox", ActivityPubController, :inbox + pipe_through(:activitypub) + post("/users/:nickname/inbox", ActivityPubController, :inbox) + post("/inbox", ActivityPubController, :inbox) end scope "/.well-known", Pleroma.Web do - pipe_through :well_known + pipe_through(:well_known) - get "/host-meta", WebFinger.WebFingerController, :host_meta - get "/webfinger", WebFinger.WebFingerController, :webfinger + get("/host-meta", WebFinger.WebFingerController, :host_meta) + get("/webfinger", WebFinger.WebFingerController, :webfinger) end end scope "/", Pleroma.Web.MastodonAPI do - pipe_through :mastodon_html + pipe_through(:mastodon_html) - get "/web/login", MastodonAPIController, :login - post "/web/login", MastodonAPIController, :login_post - get "/web/*path", MastodonAPIController, :index - delete "/auth/sign_out", MastodonAPIController, :logout + get("/web/login", MastodonAPIController, :login) + post("/web/login", MastodonAPIController, :login_post) + get("/web/*path", MastodonAPIController, :index) + delete("/auth/sign_out", MastodonAPIController, :logout) end pipeline :remote_media do - plug :accepts, ["html"] + plug(:accepts, ["html"]) end + scope "/proxy/", Pleroma.Web.MediaProxy do - pipe_through :remote_media - get "/:sig/:url", MediaProxyController, :remote + pipe_through(:remote_media) + get("/:sig/:url", MediaProxyController, :remote) end scope "/", Fallback do - get "/*path", RedirectController, :redirector + get("/*path", RedirectController, :redirector) end end defmodule Fallback.RedirectController do use Pleroma.Web, :controller + def redirector(conn, _params) do - if Mix.env != :test do + if Mix.env() != :test do conn |> put_resp_content_type("text/html") |> send_file(200, "priv/static/index.html") diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index ab0f97d55..10542fd00 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -38,9 +38,10 @@ def fetch_magic_key(salmon) do def decode_and_validate(magickey, salmon) do [data, type, encoding, alg, sig] = decode(salmon) - signed_text = [data, type, encoding, alg] - |> Enum.map(&Base.url_encode64/1) - |> Enum.join(".") + signed_text = + [data, type, encoding, alg] + |> Enum.map(&Base.url_encode64/1) + |> Enum.join(".") key = decode_key(magickey) @@ -54,22 +55,23 @@ def decode_and_validate(magickey, salmon) do end def decode_key("RSA." <> magickey) do - make_integer = fn(bin) -> + make_integer = fn bin -> list = :erlang.binary_to_list(bin) - Enum.reduce(list, 0, fn (el, acc) -> (acc <<< 8) ||| el end) + Enum.reduce(list, 0, fn el, acc -> acc <<< 8 ||| el end) end - [modulus, exponent] = magickey - |> String.split(".") - |> Enum.map(fn (n) -> Base.url_decode64!(n, padding: false) end) - |> Enum.map(make_integer) + [modulus, exponent] = + magickey + |> String.split(".") + |> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end) + |> Enum.map(make_integer) {:RSAPublicKey, modulus, exponent} end def encode_key({:RSAPublicKey, modulus, exponent}) do - modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64 - exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64 + modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64() + exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64() "RSA.#{modulus_enc}.#{exponent_enc}" end @@ -78,20 +80,25 @@ def encode_key({:RSAPublicKey, modulus, exponent}) do # We try at compile time to generate natively an RSA key otherwise we fallback on the old way. try do _ = :public_key.generate_key({:rsa, 2048, 65537}) + def generate_rsa_pem do key = :public_key.generate_key({:rsa, 2048, 65537}) entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) - pem = :public_key.pem_encode([entry]) |> String.trim_trailing + pem = :public_key.pem_encode([entry]) |> String.trim_trailing() {:ok, pem} end rescue _ -> def generate_rsa_pem do port = Port.open({:spawn, "openssl genrsa"}, [:binary]) - {:ok, pem} = receive do - {^port, {:data, pem}} -> {:ok, pem} - end + + {:ok, pem} = + receive do + {^port, {:data, pem}} -> {:ok, pem} + end + Port.close(port) + if Regex.match?(~r/RSA PRIVATE KEY/, pem) do {:ok, pem} else @@ -113,17 +120,20 @@ def encode(private_key, doc) do encoding = "base64url" alg = "RSA-SHA256" - signed_text = [doc, type, encoding, alg] - |> Enum.map(&Base.url_encode64/1) - |> Enum.join(".") + signed_text = + [doc, type, encoding, alg] + |> Enum.map(&Base.url_encode64/1) + |> Enum.join(".") - signature = signed_text - |> :public_key.sign(:sha256, private_key) - |> to_string - |> Base.url_encode64 + signature = + signed_text + |> :public_key.sign(:sha256, private_key) + |> to_string + |> Base.url_encode64() - doc_base64 = doc - |> Base.url_encode64 + doc_base64 = + doc + |> Base.url_encode64() # Don't need proper xml building, these strings are safe to leave unescaped salmon = """ @@ -141,20 +151,29 @@ def encode(private_key, doc) do def remote_users(%{data: %{"to" => to} = data}) do to = to ++ (data["cc"] || []) + to - |> Enum.map(fn(id) -> User.get_cached_by_ap_id(id) end) - |> Enum.filter(fn(user) -> user && !user.local end) + |> Enum.map(fn id -> User.get_cached_by_ap_id(id) end) + |> Enum.filter(fn user -> user && !user.local end) end defp send_to_user(%{info: %{"salmon" => salmon}}, feed, poster) do - with {:ok, %{status_code: code}} <- poster.(salmon, feed, [{"Content-Type", "application/magic-envelope+xml"}], timeout: 10000, recv_timeout: 20000, hackney: [pool: :default]) do + with {:ok, %{status_code: code}} <- + poster.( + salmon, + feed, + [{"Content-Type", "application/magic-envelope+xml"}], + timeout: 10000, + recv_timeout: 20000, + hackney: [pool: :default] + ) do Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end) else e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end) end end - defp send_to_user(_,_,_), do: nil + defp send_to_user(_, _, _), do: nil @supported_activities [ "Create", @@ -165,18 +184,21 @@ defp send_to_user(_,_,_), do: nil "Delete" ] def publish(user, activity, poster \\ &@httpoison.post/4) - def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster) when type in @supported_activities do - feed = ActivityRepresenter.to_simple_form(activity, user, true) - |> ActivityRepresenter.wrap_with_entry - |> :xmerl.export_simple(:xmerl_xml) - |> to_string + + def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster) + when type in @supported_activities do + feed = + ActivityRepresenter.to_simple_form(activity, user, true) + |> ActivityRepresenter.wrap_with_entry() + |> :xmerl.export_simple(:xmerl_xml) + |> to_string if feed do {:ok, private, _} = keys_from_pem(keys) {:ok, feed} = encode(private, feed) remote_users(activity) - |> Enum.each(fn(remote_user) -> + |> Enum.each(fn remote_user -> Task.start(fn -> Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) send_to_user(remote_user, feed, poster) diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index d81732a1a..10670e71f 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -5,9 +5,11 @@ defmodule Pleroma.Web.Streamer do def start_link do spawn(fn -> - Process.sleep(1000 * 30) # 30 seconds + # 30 seconds + Process.sleep(1000 * 30) GenServer.cast(__MODULE__, %{action: :ping}) end) + GenServer.start_link(__MODULE__, %{}, name: __MODULE__) end @@ -25,39 +27,54 @@ def stream(topic, item) do def handle_cast(%{action: :ping}, topics) do Map.values(topics) - |> List.flatten - |> Enum.each(fn (socket) -> + |> List.flatten() + |> Enum.each(fn socket -> Logger.debug("Sending keepalive ping") - send socket.transport_pid, {:text, ""} + send(socket.transport_pid, {:text, ""}) end) + spawn(fn -> - Process.sleep(1000 * 30) # 30 seconds + # 30 seconds + Process.sleep(1000 * 30) GenServer.cast(__MODULE__, %{action: :ping}) end) + {:noreply, topics} end def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do topic = "user:#{item.user_id}" - Enum.each(topics[topic] || [], fn (socket) -> - json = %{ - event: "notification", - payload: Pleroma.Web.MastodonAPI.MastodonAPIController.render_notification(socket.assigns["user"], item) |> Jason.encode! - } |> Jason.encode! - send socket.transport_pid, {:text, json} + Enum.each(topics[topic] || [], fn socket -> + json = + %{ + event: "notification", + payload: + Pleroma.Web.MastodonAPI.MastodonAPIController.render_notification( + socket.assigns["user"], + item + ) + |> Jason.encode!() + } + |> Jason.encode!() + + send(socket.transport_pid, {:text, json}) end) + {:noreply, topics} end def handle_cast(%{action: :stream, topic: "user", item: item}, topics) do Logger.debug("Trying to push to users") - recipient_topics = User.get_recipients_from_activity(item) - |> Enum.map(fn (%{id: id}) -> "user:#{id}" end) - Enum.each(recipient_topics, fn (topic) -> + recipient_topics = + User.get_recipients_from_activity(item) + |> Enum.map(fn %{id: id} -> "user:#{id}" end) + + Enum.each(recipient_topics, fn topic -> push_to_socket(topics, topic, item) end) + {:noreply, topics} end @@ -92,13 +109,21 @@ def handle_cast(m, state) do end def push_to_socket(topics, topic, item) do - Enum.each(topics[topic] || [], fn (socket) -> - json = %{ - event: "update", - payload: Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: item, for: socket.assigns[:user]) |> Jason.encode! - } |> Jason.encode! + Enum.each(topics[topic] || [], fn socket -> + json = + %{ + event: "update", + payload: + Pleroma.Web.MastodonAPI.StatusView.render( + "status.json", + activity: item, + for: socket.assigns[:user] + ) + |> Jason.encode!() + } + |> Jason.encode!() - send socket.transport_pid, {:text, json} + send(socket.transport_pid, {:text, json}) end) end diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 503719dbf..8f452c31c 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -11,21 +11,21 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do def show_password_reset(conn, %{"token" => token}) do with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), - %User{} = user <- Repo.get(User, token.user_id) do - render conn, "password_reset.html", %{ + %User{} = user <- Repo.get(User, token.user_id) do + render(conn, "password_reset.html", %{ token: token, user: user - } + }) else - _e -> render conn, "invalid_token.html" + _e -> render(conn, "invalid_token.html") end end def password_reset(conn, %{"data" => data}) do with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do - render conn, "password_reset_success.html" + render(conn, "password_reset_success.html") else - _e -> render conn, "password_reset_failed.html" + _e -> render(conn, "password_reset_failed.html") end end @@ -34,14 +34,19 @@ def help_test(conn, _params) do end def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do - with %User{} = user <- User.get_cached_by_nickname(nick), - avatar = User.avatar_url(user) do + with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do conn |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false}) else - _e -> render(conn, "subscribe.html", %{nickname: nick, avatar: nil, error: "Could not find user"}) + _e -> + render(conn, "subscribe.html", %{ + nickname: nick, + avatar: nil, + error: "Could not find user" + }) end end + def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do @@ -49,7 +54,11 @@ def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profil |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) else _e -> - render(conn, "subscribe.html", %{nickname: nick, avatar: nil, error: "Something went wrong."}) + render(conn, "subscribe.html", %{ + nickname: nick, + avatar: nil, + error: "Something went wrong." + }) end end @@ -64,17 +73,26 @@ def remote_follow(%{assigns: %{user: user}} = conn, %{"acct" => acct}) do |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id}) else conn - |> render("follow_login.html", %{error: false, acct: acct, avatar: avatar, name: name, id: id}) + |> render("follow_login.html", %{ + error: false, + acct: acct, + avatar: avatar, + name: name, + id: id + }) end end - def do_remote_follow(conn, %{"authorization" => %{"name" => username, "password" => password, "id" => id}}) do + def do_remote_follow(conn, %{ + "authorization" => %{"name" => username, "password" => password, "id" => id} + }) do followee = Repo.get(User, id) avatar = User.avatar_url(followee) name = followee.nickname + with %User{} = user <- User.get_cached_by_nickname(username), true <- Pbkdf2.checkpw(password, user.password_hash), - %User{} = followed <- Repo.get(User, id), + %User{} = followed <- Repo.get(User, id), {:ok, follower} <- User.follow(user, followee), {:ok, _activity} <- ActivityPub.follow(follower, followee) do conn @@ -82,9 +100,15 @@ def do_remote_follow(conn, %{"authorization" => %{"name" => username, "password" else _e -> conn - |> render("follow_login.html", %{error: "Wrong username or password", id: id, name: name, avatar: avatar}) + |> render("follow_login.html", %{ + error: "Wrong username or password", + id: id, + name: name, + avatar: avatar + }) end end + def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do with %User{} = followee <- Repo.get(User, id), {:ok, follower} <- User.follow(user, followee), @@ -93,9 +117,10 @@ def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id} |> render("followed.html", %{error: false}) else e -> - Logger.debug("Remote follow failed with error #{inspect e}") - conn - |> render("followed.html", %{error: inspect(e)}) + Logger.debug("Remote follow failed with error #{inspect(e)}") + + conn + |> render("followed.html", %{error: inspect(e)}) end end @@ -107,60 +132,67 @@ def config(conn, _params) do #{Keyword.get(@instance, :name)} - #{Web.base_url} + #{Web.base_url()} #{Keyword.get(@instance, :limit)} #{!Keyword.get(@instance, :registrations_open)} """ + conn |> put_resp_content_type("application/xml") |> send_resp(200, response) + _ -> json(conn, %{ - site: %{ - name: Keyword.get(@instance, :name), - server: Web.base_url, - textlimit: to_string(Keyword.get(@instance, :limit)), - closed: if(Keyword.get(@instance, :registrations_open), do: "0", else: "1") - } - }) + site: %{ + name: Keyword.get(@instance, :name), + server: Web.base_url(), + textlimit: to_string(Keyword.get(@instance, :limit)), + closed: if(Keyword.get(@instance, :registrations_open), do: "0", else: "1") + } + }) end end def version(conn, _params) do version = Keyword.get(@instance, :version) + case get_format(conn) do "xml" -> response = "#{version}" + conn |> put_resp_content_type("application/xml") |> send_resp(200, response) - _ -> json(conn, version) + + _ -> + json(conn, version) end end def emoji(conn, _params) do - json conn, Enum.into(Formatter.get_custom_emoji(), %{}) + json(conn, Enum.into(Formatter.get_custom_emoji(), %{})) end def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do follow_import(conn, %{"list" => File.read!(listfile.path)}) end + def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do Task.start(fn -> - String.split(list) - |> Enum.map(fn nick -> + String.split(list) + |> Enum.map(fn nick -> with %User{} = follower <- User.get_cached_by_ap_id(user.ap_id), - %User{} = followed <- User.get_or_fetch_by_nickname(nick), - {:ok, follower} <- User.follow(follower, followed) do + %User{} = followed <- User.get_or_fetch_by_nickname(nick), + {:ok, follower} <- User.follow(follower, followed) do ActivityPub.follow(follower, followed) else - _e -> Logger.debug "follow_import: following #{nick} failed" + _e -> Logger.debug("follow_import: following #{nick} failed") end end) end) - json conn, "job started" + json(conn, "job started") end end diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 73ae3422b..c216c606e 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -7,18 +7,22 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do alias Pleroma.Formatter defp user_by_ap_id(user_list, ap_id) do - Enum.find(user_list, fn (%{ap_id: user_id}) -> ap_id == user_id end) + Enum.find(user_list, fn %{ap_id: user_id} -> ap_id == user_id end) end - def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} = activity, - %{users: users, announced_activity: announced_activity} = opts) do + def to_map( + %Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} = + activity, + %{users: users, announced_activity: announced_activity} = opts + ) do user = user_by_ap_id(users, actor) - created_at = created_at |> Utils.date_to_asctime + created_at = created_at |> Utils.date_to_asctime() text = "#{user.nickname} retweeted a status." announced_user = user_by_ap_id(users, announced_activity.data["actor"]) retweeted_status = to_map(announced_activity, Map.merge(%{user: announced_user}, opts)) + %{ "id" => activity.id, "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), @@ -35,9 +39,11 @@ def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor, "published" } end - def to_map(%Activity{data: %{"type" => "Like", "published" => created_at}} = activity, - %{user: user, liked_activity: liked_activity} = opts) do - created_at = created_at |> Utils.date_to_asctime + def to_map( + %Activity{data: %{"type" => "Like", "published" => created_at}} = activity, + %{user: user, liked_activity: liked_activity} = opts + ) do + created_at = created_at |> Utils.date_to_asctime() text = "#{user.nickname} favorited a status." @@ -56,12 +62,16 @@ def to_map(%Activity{data: %{"type" => "Like", "published" => created_at}} = act } end - def to_map(%Activity{data: %{"type" => "Follow", "object" => followed_id}} = activity, %{user: user} = opts) do - created_at = activity.data["published"] || (DateTime.to_iso8601(activity.inserted_at)) - created_at = created_at |> Utils.date_to_asctime + def to_map( + %Activity{data: %{"type" => "Follow", "object" => followed_id}} = activity, + %{user: user} = opts + ) do + created_at = activity.data["published"] || DateTime.to_iso8601(activity.inserted_at) + created_at = created_at |> Utils.date_to_asctime() followed = User.get_cached_by_ap_id(followed_id) text = "#{user.nickname} started following #{followed.nickname}" + %{ "id" => activity.id, "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), @@ -79,10 +89,16 @@ def to_map(%Activity{data: %{"type" => "Follow", "object" => followed_id}} = act # TODO: # Make this more proper. Just a placeholder to not break the frontend. - def to_map(%Activity{data: %{"type" => "Undo", "published" => created_at, "object" => undid_activity }} = activity, %{user: user} = opts) do - created_at = created_at |> Utils.date_to_asctime + def to_map( + %Activity{ + data: %{"type" => "Undo", "published" => created_at, "object" => undid_activity} + } = activity, + %{user: user} = opts + ) do + created_at = created_at |> Utils.date_to_asctime() text = "#{user.nickname} undid the action at #{undid_activity}" + %{ "id" => activity.id, "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), @@ -98,8 +114,12 @@ def to_map(%Activity{data: %{"type" => "Undo", "published" => created_at, "objec } end - def to_map(%Activity{data: %{"type" => "Delete", "published" => created_at, "object" => _ }} = activity, %{user: user} = opts) do - created_at = created_at |> Utils.date_to_asctime + def to_map( + %Activity{data: %{"type" => "Delete", "published" => created_at, "object" => _}} = + activity, + %{user: user} = opts + ) do + created_at = created_at |> Utils.date_to_asctime() %{ "id" => activity.id, @@ -107,7 +127,7 @@ def to_map(%Activity{data: %{"type" => "Delete", "published" => created_at, "obj "user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "attentions" => [], "statusnet_html" => "deleted notice {{tag", - "text" => "deleted notice {{tag" , + "text" => "deleted notice {{tag", "is_local" => activity.local, "is_post_verb" => false, "created_at" => created_at, @@ -117,8 +137,11 @@ def to_map(%Activity{data: %{"type" => "Delete", "published" => created_at, "obj } end - def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = activity, %{user: user} = opts) do - created_at = object["published"] |> Utils.date_to_asctime + def to_map( + %Activity{data: %{"object" => %{"content" => content} = object}} = activity, + %{user: user} = opts + ) do + created_at = object["published"] |> Utils.date_to_asctime() like_count = object["like_count"] || 0 announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) @@ -126,10 +149,11 @@ def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = ac mentions = opts[:mentioned] || [] - attentions = activity.recipients - |> Enum.map(fn (ap_id) -> Enum.find(mentions, fn(user) -> ap_id == user.ap_id end) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn (user) -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) + attentions = + activity.recipients + |> Enum.map(fn ap_id -> Enum.find(mentions, fn user -> ap_id == user.ap_id end) end) + |> Enum.filter(& &1) + |> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) conversation_id = conversation_id(activity) @@ -139,14 +163,17 @@ def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = ac tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags summary = activity.data["object"]["summary"] - content = if !!summary and summary != "" do - "#{activity.data["object"]["summary"]}
#{content}" - else - content - end - html = HtmlSanitizeEx.basic_html(content) - |> Formatter.emojify(object["emoji"]) + content = + if !!summary and summary != "" do + "#{activity.data["object"]["summary"]}
#{content}" + else + content + end + + html = + HtmlSanitizeEx.basic_html(content) + |> Formatter.emojify(object["emoji"]) %{ "id" => activity.id, @@ -175,7 +202,8 @@ def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = ac def conversation_id(activity) do with context when not is_nil(context) <- activity.data["context"] do TwitterAPI.context_to_conversation_id(context) - else _e -> nil + else + _e -> nil end end diff --git a/lib/pleroma/web/twitter_api/representers/base_representer.ex b/lib/pleroma/web/twitter_api/representers/base_representer.ex index 75e00520c..f32a21d47 100644 --- a/lib/pleroma/web/twitter_api/representers/base_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/base_representer.ex @@ -1,15 +1,18 @@ defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do defmacro __using__(_opts) do quote do - def to_json(object) do to_json(object, %{}) end + def to_json(object) do + to_json(object, %{}) + end + def to_json(object, options) do object |> to_map(options) - |> Jason.encode! + |> Jason.encode!() end def enum_to_list(enum, options) do - mapping = fn (el) -> to_map(el, options) end + mapping = fn el -> to_map(el, options) end Enum.map(enum, mapping) end @@ -17,11 +20,14 @@ def to_map(object) do to_map(object, %{}) end - def enum_to_json(enum) do enum_to_json(enum, %{}) end + def enum_to_json(enum) do + enum_to_json(enum, %{}) + end + def enum_to_json(enum, options) do enum |> enum_to_list(options) - |> Jason.encode! + |> Jason.encode!() end end end diff --git a/lib/pleroma/web/twitter_api/representers/object_representer.ex b/lib/pleroma/web/twitter_api/representers/object_representer.ex index e2d653ba8..9af8a1691 100644 --- a/lib/pleroma/web/twitter_api/representers/object_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/object_representer.ex @@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do def to_map(%Object{data: %{"url" => [url | _]}} = object, _opts) do data = object.data + %{ url: url["href"] |> Pleroma.Web.MediaProxy.url(), mimetype: url["mediaType"], diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 58801bc33..4eac8a8fd 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -13,37 +13,42 @@ def create_status(%User{} = user, %{"status" => _} = data) do end def fetch_friend_statuses(user, opts \\ %{}) do - opts = opts - |> Map.put("blocking_user", user) - |> Map.put("user", user) - |> Map.put("type", ["Create", "Announce", "Follow", "Like"]) + opts = + opts + |> Map.put("blocking_user", user) + |> Map.put("user", user) + |> Map.put("type", ["Create", "Announce", "Follow", "Like"]) ActivityPub.fetch_activities([user.ap_id | user.following], opts) |> activities_to_statuses(%{for: user}) end def fetch_public_statuses(user, opts \\ %{}) do - opts = opts - |> Map.put("local_only", true) - |> Map.put("blocking_user", user) - |> Map.put("type", ["Create", "Announce", "Follow"]) + opts = + opts + |> Map.put("local_only", true) + |> Map.put("blocking_user", user) + |> Map.put("type", ["Create", "Announce", "Follow"]) ActivityPub.fetch_public_activities(opts) |> activities_to_statuses(%{for: user}) end def fetch_public_and_external_statuses(user, opts \\ %{}) do - opts = opts - |> Map.put("blocking_user", user) - |> Map.put("type", ["Create", "Announce", "Follow"]) + opts = + opts + |> Map.put("blocking_user", user) + |> Map.put("type", ["Create", "Announce", "Follow"]) ActivityPub.fetch_public_activities(opts) |> activities_to_statuses(%{for: user}) end def fetch_user_statuses(user, opts \\ %{}) do - opts = opts - |> Map.put("type", ["Create"]) + opts = + opts + |> Map.put("type", ["Create"]) + ActivityPub.fetch_public_activities(opts) |> activities_to_statuses(%{for: user}) end @@ -55,12 +60,16 @@ def fetch_mentions(user, opts \\ %{}) do def fetch_conversation(user, id) do with context when is_binary(context) <- conversation_id_to_context(id), - activities <- ActivityPub.fetch_activities_for_context(context, %{"blocking_user" => user, "user" => user}), - statuses <- activities |> activities_to_statuses(%{for: user}) - do + activities <- + ActivityPub.fetch_activities_for_context(context, %{ + "blocking_user" => user, + "user" => user + }), + statuses <- activities |> activities_to_statuses(%{for: user}) do statuses - else _e -> - [] + else + _e -> + [] end end @@ -74,8 +83,7 @@ def fetch_status(user, id) do def follow(%User{} = follower, params) do with {:ok, %User{} = followed} <- get_user(params), {:ok, follower} <- User.follow(follower, followed), - {:ok, activity} <- ActivityPub.follow(follower, followed) - do + {:ok, activity} <- ActivityPub.follow(follower, followed) do {:ok, follower, followed, activity} else err -> err @@ -83,16 +91,17 @@ def follow(%User{} = follower, params) do end def unfollow(%User{} = follower, params) do - with { :ok, %User{} = unfollowed } <- get_user(params), - { :ok, follower, follow_activity } <- User.unfollow(follower, unfollowed), - { :ok, _activity } <- ActivityPub.insert(%{ - "type" => "Undo", - "actor" => follower.ap_id, - "object" => follow_activity.data["id"], # get latest Follow for these users - "published" => make_date() - }) - do - { :ok, follower, unfollowed } + with {:ok, %User{} = unfollowed} <- get_user(params), + {:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed), + {:ok, _activity} <- + ActivityPub.insert(%{ + "type" => "Undo", + "actor" => follower.ap_id, + # get latest Follow for these users + "object" => follow_activity.data["id"], + "published" => make_date() + }) do + {:ok, follower, unfollowed} else err -> err end @@ -100,8 +109,7 @@ def unfollow(%User{} = follower, params) do def block(%User{} = blocker, params) do with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.block(blocker, blocked) - do + {:ok, blocker} <- User.block(blocker, blocked) do {:ok, blocker, blocked} else err -> err @@ -110,8 +118,7 @@ def block(%User{} = blocker, params) do def unblock(%User{} = blocker, params) do with {:ok, %User{} = blocked} <- get_user(params), - {:ok, blocker} <- User.unblock(blocker, blocked) - do + {:ok, blocker} <- User.unblock(blocker, blocked) do {:ok, blocker, blocked} else err -> err @@ -163,13 +170,15 @@ def upload(%Plug.Upload{} = file, format \\ "xml") do """ + "json" -> %{ media_id: object.id, media_id_string: "#{object.id}}", media_url: href, size: 0 - } |> Jason.encode! + } + |> Jason.encode!() end end @@ -189,9 +198,11 @@ def register_user(params) do {:ok, user} else {:error, changeset} -> - errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) - |> Jason.encode! - {:error, %{error: errors}} + errors = + Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) + |> Jason.encode!() + + {:error, %{error: errors}} end end @@ -209,16 +220,20 @@ def get_user(user \\ nil, params) do case target = get_by_id_or_nickname(user_id) do nil -> {:error, "No user with such user_id"} + _ -> {:ok, target} end + %{"screen_name" => nickname} -> case target = Repo.get_by(User, nickname: nickname) do nil -> {:error, "No user with such screen_name"} + _ -> {:ok, target} end + _ -> if user do {:ok, user} @@ -229,6 +244,7 @@ def get_user(user \\ nil, params) do end defp parse_int(string, default) + defp parse_int(string, default) when is_binary(string) do with {n, _} <- Integer.parse(string) do n @@ -236,6 +252,7 @@ defp parse_int(string, default) when is_binary(string) do _e -> default end end + defp parse_int(_, default), do: default def search(user, %{"q" => query} = params) do @@ -243,19 +260,28 @@ def search(user, %{"q" => query} = params) do page = parse_int(params["page"], 1) offset = (page - 1) * limit - q = from a in Activity, - where: fragment("?->>'type' = 'Create'", a.data), - where: fragment("to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", a.data, ^query), - limit: ^limit, - offset: ^offset, - order_by: [desc: :inserted_at] # this one isn't indexed so psql won't take the wrong index. + q = + from( + a in Activity, + where: fragment("?->>'type' = 'Create'", a.data), + where: + fragment( + "to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", + a.data, + ^query + ), + limit: ^limit, + offset: ^offset, + # this one isn't indexed so psql won't take the wrong index. + order_by: [desc: :inserted_at] + ) activities = Repo.all(q) activities_to_statuses(activities, %{for: user}) end defp activities_to_statuses(activities, opts) do - Enum.map(activities, fn(activity) -> + Enum.map(activities, fn activity -> activity_to_status(activity, opts) end) end @@ -266,7 +292,10 @@ defp activity_to_status(%Activity{data: %{"type" => "Like"}} = activity, opts) d user = User.get_cached_by_ap_id(actor) [liked_activity] = Activity.all_by_object_ap_id(activity.data["object"]) - ActivityRepresenter.to_map(activity, Map.merge(opts, %{user: user, liked_activity: liked_activity})) + ActivityRepresenter.to_map( + activity, + Map.merge(opts, %{user: user, liked_activity: liked_activity}) + ) end # For announces, fetch the announced activity and the user. @@ -276,7 +305,10 @@ defp activity_to_status(%Activity{data: %{"type" => "Announce"}} = activity, opt [announced_activity] = Activity.all_by_object_ap_id(activity.data["object"]) announced_actor = User.get_cached_by_ap_id(announced_activity.data["actor"]) - ActivityRepresenter.to_map(activity, Map.merge(opts, %{users: [user, announced_actor], announced_activity: announced_activity})) + ActivityRepresenter.to_map( + activity, + Map.merge(opts, %{users: [user, announced_actor], announced_activity: announced_activity}) + ) end defp activity_to_status(%Activity{data: %{"type" => "Delete"}} = activity, opts) do @@ -289,32 +321,41 @@ defp activity_to_status(activity, opts) do actor = get_in(activity.data, ["actor"]) user = User.get_cached_by_ap_id(actor) # mentioned_users = Repo.all(from user in User, where: user.ap_id in ^activity.data["to"]) - mentioned_users = Enum.map(activity.recipients || [], fn (ap_id) -> - if ap_id do - User.get_cached_by_ap_id(ap_id) - else - nil - end - end) - |> Enum.filter(&(&1)) + mentioned_users = + Enum.map(activity.recipients || [], fn ap_id -> + if ap_id do + User.get_cached_by_ap_id(ap_id) + else + nil + end + end) + |> Enum.filter(& &1) - ActivityRepresenter.to_map(activity, Map.merge(opts, %{user: user, mentioned: mentioned_users})) + ActivityRepresenter.to_map( + activity, + Map.merge(opts, %{user: user, mentioned: mentioned_users}) + ) end defp make_date do - DateTime.utc_now() |> DateTime.to_iso8601 + DateTime.utc_now() |> DateTime.to_iso8601() end def context_to_conversation_id(context) do with %Object{id: id} <- Object.get_cached_by_ap_id(context) do id - else _e -> + else + _e -> changeset = Object.context_mapping(context) + case Repo.insert(changeset) do - {:ok, %{id: id}} -> id + {:ok, %{id: id}} -> + id + # This should be solved by an upsert, but it seems ecto # has problems accessing the constraint inside the jsonb. - {:error, _} -> Object.get_cached_by_ap_id(context).id + {:error, _} -> + Object.get_cached_by_ap_id(context).id end end end @@ -322,8 +363,9 @@ def context_to_conversation_id(context) do def conversation_id_to_context(id) do with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do context - else _e -> - {:error, "No such conversation"} + else + _e -> + {:error, "No such conversation"} end end @@ -331,12 +373,15 @@ def get_external_profile(for_user, uri) do with %User{} = user <- User.get_or_fetch(uri) do spawn(fn -> with url <- user.info["topic"], - {:ok, %{body: body}} <- @httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000) do + {:ok, %{body: body}} <- + @httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000) do OStatus.handle_incoming(body) end end) + {:ok, UserView.render("show.json", %{user: user, for: for_user})} - else _e -> + else + _e -> {:error, "Couldn't find user"} end end diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index a3c98a245..a8bcae5ba 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -16,7 +16,8 @@ def verify_credentials(%{assigns: %{user: user}} = conn, _params) do def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do with media_ids <- extract_media_ids(status_data), - {:ok, activity} <- TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do + {:ok, activity} <- + TwitterAPI.create_status(user, Map.put(status_data, "media_ids", media_ids)) do conn |> json(ActivityRepresenter.to_map(activity, %{user: user})) else @@ -35,10 +36,10 @@ defp empty_status_reply(conn) do defp extract_media_ids(status_data) do with media_ids when not is_nil(media_ids) <- status_data["media_ids"], split_ids <- String.split(media_ids, ","), - clean_ids <- Enum.reject(split_ids, fn (id) -> String.length(id) == 0 end) - do - clean_ids - else _e -> [] + clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do + clean_ids + else + _e -> [] end end @@ -69,9 +70,9 @@ def friends_timeline(%{assigns: %{user: user}} = conn, params) do def show_user(conn, params) do with {:ok, shown} <- TwitterAPI.get_user(params) do if user = conn.assigns.user do - render conn, UserView, "show.json", %{user: shown, for: user} + render(conn, UserView, "show.json", %{user: shown, for: user}) else - render conn, UserView, "show.json", %{user: shown} + render(conn, UserView, "show.json", %{user: shown}) end else {:error, msg} -> @@ -83,9 +84,11 @@ def user_timeline(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.get_user(user, params) do {:ok, target_user} -> params = Map.merge(params, %{"actor_id" => target_user.ap_id, "whole_db" => true}) - statuses = TwitterAPI.fetch_user_statuses(user, params) + statuses = TwitterAPI.fetch_user_statuses(user, params) + conn - |> json_reply(200, statuses |> Jason.encode!) + |> json_reply(200, statuses |> Jason.encode!()) + {:error, msg} -> bad_request_reply(conn, msg) end @@ -103,29 +106,36 @@ def follow(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.follow(user, params) do {:ok, user, followed, _activity} -> render(conn, UserView, "show.json", %{user: followed, for: user}) - {:error, msg} -> forbidden_json_reply(conn, msg) + + {:error, msg} -> + forbidden_json_reply(conn, msg) end end def block(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.block(user, params) do {:ok, user, blocked} -> - render conn, UserView, "show.json", %{user: blocked, for: user} - {:error, msg} -> forbidden_json_reply(conn, msg) + render(conn, UserView, "show.json", %{user: blocked, for: user}) + + {:error, msg} -> + forbidden_json_reply(conn, msg) end end def unblock(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.unblock(user, params) do {:ok, user, blocked} -> - render conn, UserView, "show.json", %{user: blocked, for: user} - {:error, msg} -> forbidden_json_reply(conn, msg) + render(conn, UserView, "show.json", %{user: blocked, for: user}) + + {:error, msg} -> + forbidden_json_reply(conn, msg) end end def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do with {:ok, delete} <- CommonAPI.delete(id, user) do json = ActivityRepresenter.to_json(delete, %{user: user, for: user}) + conn |> json_reply(200, json) end @@ -135,14 +145,16 @@ def unfollow(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.unfollow(user, params) do {:ok, user, unfollowed} -> render(conn, UserView, "show.json", %{user: unfollowed, for: user}) - {:error, msg} -> forbidden_json_reply(conn, msg) + + {:error, msg} -> + forbidden_json_reply(conn, msg) end end def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do with %Activity{} = activity <- Repo.get(Activity, id), true <- ActivityPub.visible_for_user?(activity, user) do - render conn, ActivityView, "activity.json", %{activity: activity, for: user} + render(conn, ActivityView, "activity.json", %{activity: activity, for: user}) end end @@ -156,6 +168,7 @@ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do def upload(conn, %{"media" => media}) do response = TwitterAPI.upload(media) + conn |> put_resp_content_type("application/atom+xml") |> send_resp(200, response) @@ -163,12 +176,14 @@ def upload(conn, %{"media" => media}) do def upload_json(conn, %{"media" => media}) do response = TwitterAPI.upload(media, "json") + conn |> json_reply(200, response) end def get_by_id_or_ap_id(id) do activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id) + if activity.data["type"] == "Create" do activity else @@ -199,8 +214,8 @@ def register(conn, params) do render(conn, UserView, "show.json", %{user: user}) else {:error, errors} -> - conn - |> json_reply(400, Jason.encode!(errors)) + conn + |> json_reply(400, Jason.encode!(errors)) end end @@ -219,8 +234,9 @@ def update_banner(%{assigns: %{user: user}} = conn, params) do change <- User.info_changeset(user, %{info: new_info}), {:ok, user} <- User.update_and_set_cache(change) do CommonAPI.update(user) - %{"url" => [ %{ "href" => href } | _ ]} = object.data - response = %{ url: href } |> Jason.encode! + %{"url" => [%{"href" => href} | _]} = object.data + response = %{url: href} |> Jason.encode!() + conn |> json_reply(200, response) end @@ -231,8 +247,9 @@ def update_background(%{assigns: %{user: user}} = conn, params) do new_info <- Map.put(user.info, "background", object.data), change <- User.info_changeset(user, %{info: new_info}), {:ok, _user} <- User.update_and_set_cache(change) do - %{"url" => [ %{ "href" => href } | _ ]} = object.data - response = %{ url: href } |> Jason.encode! + %{"url" => [%{"href" => href} | _]} = object.data + response = %{url: href} |> Jason.encode!() + conn |> json_reply(200, response) end @@ -285,9 +302,10 @@ def friends(conn, params) do def friends_ids(%{assigns: %{user: user}} = conn, _params) do with {:ok, friends} <- User.get_friends(user) do - ids = friends - |> Enum.map(fn x -> x.id end) - |> Jason.encode! + ids = + friends + |> Enum.map(fn x -> x.id end) + |> Jason.encode!() json(conn, ids) else @@ -300,11 +318,12 @@ def empty_array(conn, _params) do end def update_profile(%{assigns: %{user: user}} = conn, params) do - params = if bio = params["description"] do - Map.put(params, "bio", bio) - else - params - end + params = + if bio = params["description"] do + Map.put(params, "bio", bio) + else + params + end with changeset <- User.update_changeset(user, params), {:ok, user} <- User.update_and_set_cache(changeset) do @@ -339,6 +358,6 @@ defp forbidden_json_reply(conn, error_message) do end defp error_json(conn, error_message) do - %{"error" => error_message, "request" => conn.request_path} |> Jason.encode! + %{"error" => error_message, "request" => conn.request_path} |> Jason.encode!() end end diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 50d536d0e..1596b34dc 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -10,7 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do user = User.get_by_ap_id(activity.data["actor"]) - created_at = activity.data["published"] |> Utils.date_to_asctime + created_at = activity.data["published"] |> Utils.date_to_asctime() announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) text = "#{user.nickname} retweeted a status." @@ -37,8 +37,10 @@ def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activ def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do user = User.get_cached_by_ap_id(activity.data["actor"]) liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) - created_at = activity.data["published"] - |> Utils.date_to_asctime + + created_at = + activity.data["published"] + |> Utils.date_to_asctime() text = "#{user.nickname} favorited a status." @@ -57,20 +59,24 @@ def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} } end - def render("activity.json", %{activity: %{data: %{"type" => "Create", "object" => object}} = activity} = opts) do + def render( + "activity.json", + %{activity: %{data: %{"type" => "Create", "object" => object}} = activity} = opts + ) do actor = get_in(activity.data, ["actor"]) user = User.get_cached_by_ap_id(actor) - created_at = object["published"] |> Utils.date_to_asctime + created_at = object["published"] |> Utils.date_to_asctime() like_count = object["like_count"] || 0 announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) - attentions = activity.recipients - |> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) - |> Enum.filter(&(&1)) - |> Enum.map(fn (user) -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) + attentions = + activity.recipients + |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end) + |> Enum.filter(& &1) + |> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) conversation_id = conversation_id(activity) @@ -81,14 +87,17 @@ def render("activity.json", %{activity: %{data: %{"type" => "Create", "object" = summary = activity.data["object"]["summary"] content = object["content"] - content = if !!summary and summary != "" do - "#{activity.data["object"]["summary"]}
#{content}" - else - content - end - html = HtmlSanitizeEx.basic_html(content) - |> Formatter.emojify(object["emoji"]) + content = + if !!summary and summary != "" do + "#{activity.data["object"]["summary"]}
#{content}" + else + content + end + + html = + HtmlSanitizeEx.basic_html(content) + |> Formatter.emojify(object["emoji"]) %{ "id" => activity.id, @@ -117,7 +126,8 @@ def render("activity.json", %{activity: %{data: %{"type" => "Create", "object" = defp conversation_id(activity) do with context when not is_nil(context) <- activity.data["context"] do TwitterAPI.context_to_conversation_id(context) - else _e -> nil + else + _e -> nil end end end diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 6fb07f052..31527caae 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -14,20 +14,22 @@ def render("index.json", %{users: users, for: user}) do def render("user.json", %{user: user = %User{}} = assigns) do image = User.avatar_url(user) |> MediaProxy.url() - {following, follows_you, statusnet_blocking} = if assigns[:for] do - { - User.following?(assigns[:for], user), - User.following?(user, assigns[:for]), - User.blocks?(assigns[:for], user) - } - else - {false, false, false} - end + + {following, follows_you, statusnet_blocking} = + if assigns[:for] do + { + User.following?(assigns[:for], user), + User.following?(user, assigns[:for]), + User.blocks?(assigns[:for], user) + } + else + {false, false, false} + end user_info = User.get_cached_user_info(user) data = %{ - "created_at" => user.inserted_at |> Utils.format_naive_asctime, + "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "description" => HtmlSanitizeEx.strip_tags(user.bio), "favourites_count" => 0, "followers_count" => user_info[:follower_count], @@ -59,9 +61,14 @@ def render("user.json", %{user: user = %User{}} = assigns) do end end - def render("short.json", %{user: %User{ - nickname: nickname, id: id, ap_id: ap_id, name: name - }}) do + def render("short.json", %{ + user: %User{ + nickname: nickname, + id: id, + ap_id: ap_id, + name: name + } + }) do %{ "fullname" => name, "id" => id, @@ -71,6 +78,6 @@ def render("short.json", %{user: %User{ } end - defp image_url(%{"url" => [ %{ "href" => href } | _ ]}), do: href + defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil end diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex index 6c589d3a1..7106031ae 100644 --- a/lib/pleroma/web/views/error_view.ex +++ b/lib/pleroma/web/views/error_view.ex @@ -12,6 +12,6 @@ def render("500.json", _assigns) do # In case no render clause matches or no # template is found, let's render it as 500 def template_not_found(_template, assigns) do - render "500.json", assigns + render("500.json", assigns) end end diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index 2c343c2d7..b82242a78 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -26,8 +26,9 @@ def controller do def view do quote do - use Phoenix.View, root: "lib/pleroma/web/templates", - namespace: Pleroma.Web + use Phoenix.View, + root: "lib/pleroma/web/templates", + namespace: Pleroma.Web # Import convenience functions from controllers import Phoenix.Controller, only: [get_csrf_token: 0, get_flash: 2, view_module: 1] @@ -59,6 +60,6 @@ defmacro __using__(which) when is_atom(which) do end def base_url do - Pleroma.Web.Endpoint.url + Pleroma.Web.Endpoint.url() end end diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 8edb89963..e45c0ed8d 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -8,43 +8,56 @@ defmodule Pleroma.Web.WebFinger do require Logger def host_meta do - base_url = Web.base_url + base_url = Web.base_url() + { - :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, + :XRD, + %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, { - :Link, %{rel: "lrdd", type: "application/xrd+xml", template: "#{base_url}/.well-known/webfinger?resource={uri}"} + :Link, + %{ + rel: "lrdd", + type: "application/xrd+xml", + template: "#{base_url}/.well-known/webfinger?resource={uri}" + } } } - |> XmlBuilder.to_doc + |> XmlBuilder.to_doc() end def webfinger(resource, "JSON") do - host = Pleroma.Web.Endpoint.host + host = Pleroma.Web.Endpoint.host() regex = ~r/(acct:)?(?\w+)@#{host}/ + with %{"username" => username} <- Regex.named_captures(regex, resource) do user = User.get_by_nickname(username) {:ok, represent_user(user, "JSON")} - else _e -> - with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do - {:ok, represent_user(user, "JSON")} - else _e -> - {:error, "Couldn't find user"} - end + else + _e -> + with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do + {:ok, represent_user(user, "JSON")} + else + _e -> + {:error, "Couldn't find user"} + end end end def webfinger(resource, "XML") do - host = Pleroma.Web.Endpoint.host + host = Pleroma.Web.Endpoint.host() regex = ~r/(acct:)?(?\w+)@#{host}/ + with %{"username" => username} <- Regex.named_captures(regex, resource) do user = User.get_by_nickname(username) {:ok, represent_user(user, "XML")} - else _e -> - with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do - {:ok, represent_user(user, "XML")} - else _e -> - {:error, "Couldn't find user"} - end + else + _e -> + with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do + {:ok, represent_user(user, "XML")} + else + _e -> + {:error, "Couldn't find user"} + end end end @@ -52,16 +65,28 @@ def represent_user(user, "JSON") do {:ok, user} = ensure_keys_present(user) {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) magic_key = Salmon.encode_key(public) + %{ - "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host}", + "subject" => "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "aliases" => [user.ap_id], "links" => [ - %{"rel" => "http://schemas.google.com/g/2010#updates-from", "type" => "application/atom+xml", "href" => OStatus.feed_path(user)}, - %{"rel" => "http://webfinger.net/rel/profile-page", "type" => "text/html", "href" => user.ap_id}, + %{ + "rel" => "http://schemas.google.com/g/2010#updates-from", + "type" => "application/atom+xml", + "href" => OStatus.feed_path(user) + }, + %{ + "rel" => "http://webfinger.net/rel/profile-page", + "type" => "text/html", + "href" => user.ap_id + }, %{"rel" => "salmon", "href" => OStatus.salmon_path(user)}, %{"rel" => "magic-public-key", "href" => "data:application/magic-public-key,#{magic_key}"}, %{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, - %{"rel" => "http://ostatus.org/schema/1.0/subscribe", "template" => OStatus.remote_follow_path()} + %{ + "rel" => "http://ostatus.org/schema/1.0/subscribe", + "template" => OStatus.remote_follow_path() + } ] } end @@ -70,30 +95,42 @@ def represent_user(user, "XML") do {:ok, user} = ensure_keys_present(user) {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) magic_key = Salmon.encode_key(public) + { - :XRD, %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, + :XRD, + %{xmlns: "http://docs.oasis-open.org/ns/xri/xrd-1.0"}, [ - {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host}"}, + {:Subject, "acct:#{user.nickname}@#{Pleroma.Web.Endpoint.host()}"}, {:Alias, user.ap_id}, - {:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}}, - {:Link, %{rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: user.ap_id}}, + {:Link, + %{ + rel: "http://schemas.google.com/g/2010#updates-from", + type: "application/atom+xml", + href: OStatus.feed_path(user) + }}, + {:Link, + %{rel: "http://webfinger.net/rel/profile-page", type: "text/html", href: user.ap_id}}, {:Link, %{rel: "salmon", href: OStatus.salmon_path(user)}}, - {:Link, %{rel: "magic-public-key", href: "data:application/magic-public-key,#{magic_key}"}}, + {:Link, + %{rel: "magic-public-key", href: "data:application/magic-public-key,#{magic_key}"}}, {:Link, %{rel: "self", type: "application/activity+json", href: user.ap_id}}, - {:Link, %{rel: "http://ostatus.org/schema/1.0/subscribe", template: OStatus.remote_follow_path()}} + {:Link, + %{rel: "http://ostatus.org/schema/1.0/subscribe", template: OStatus.remote_follow_path()}} ] } - |> XmlBuilder.to_doc + |> XmlBuilder.to_doc() end # This seems a better fit in Salmon def ensure_keys_present(user) do info = user.info || %{} + if info["keys"] do {:ok, user} else - {:ok, pem} = Salmon.generate_rsa_pem + {:ok, pem} = Salmon.generate_rsa_pem() info = Map.put(info, "keys", pem) + Ecto.Changeset.change(user, info: info) |> User.update_and_set_cache() end @@ -102,11 +139,28 @@ def ensure_keys_present(user) do defp webfinger_from_xml(doc) do magic_key = XML.string_from_xpath(~s{//Link[@rel="magic-public-key"]/@href}, doc) "data:application/magic-public-key," <> magic_key = magic_key - topic = XML.string_from_xpath(~s{//Link[@rel="http://schemas.google.com/g/2010#updates-from"]/@href}, doc) + + topic = + XML.string_from_xpath( + ~s{//Link[@rel="http://schemas.google.com/g/2010#updates-from"]/@href}, + doc + ) + subject = XML.string_from_xpath("//Subject", doc) salmon = XML.string_from_xpath(~s{//Link[@rel="salmon"]/@href}, doc) - subscribe_address = XML.string_from_xpath(~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}, doc) - ap_id = XML.string_from_xpath(~s{//Link[@rel="self" and @type="application/activity+json"]/@href}, doc) + + subscribe_address = + XML.string_from_xpath( + ~s{//Link[@rel="http://ostatus.org/schema/1.0/subscribe"]/@template}, + doc + ) + + ap_id = + XML.string_from_xpath( + ~s{//Link[@rel="self" and @type="application/activity+json"]/@href}, + doc + ) + data = %{ "magic_key" => magic_key, "topic" => topic, @@ -115,41 +169,51 @@ defp webfinger_from_xml(doc) do "subscribe_address" => subscribe_address, "ap_id" => ap_id } + {:ok, data} end defp webfinger_from_json(doc) do - data = Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn (link, data) -> - case {link["type"], link["rel"]} do - {"application/activity+json", "self"} -> - Map.put(data, "ap_id", link["href"]) - {_, "magic-public-key"} -> - "data:application/magic-public-key," <> magic_key = link["href"] - Map.put(data, "magic_key", magic_key) - {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} -> - Map.put(data, "topic", link["href"]) - {_, "salmon"} -> - Map.put(data, "salmon", link["href"]) - {_, "http://ostatus.org/schema/1.0/subscribe"} -> - Map.put(data, "subscribe_address", link["template"]) - _ -> - Logger.debug("Unhandled type: #{inspect(link["type"])}") - data - end - end) + data = + Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data -> + case {link["type"], link["rel"]} do + {"application/activity+json", "self"} -> + Map.put(data, "ap_id", link["href"]) + + {_, "magic-public-key"} -> + "data:application/magic-public-key," <> magic_key = link["href"] + Map.put(data, "magic_key", magic_key) + + {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} -> + Map.put(data, "topic", link["href"]) + + {_, "salmon"} -> + Map.put(data, "salmon", link["href"]) + + {_, "http://ostatus.org/schema/1.0/subscribe"} -> + Map.put(data, "subscribe_address", link["template"]) + + _ -> + Logger.debug("Unhandled type: #{inspect(link["type"])}") + data + end + end) + {:ok, data} end def get_template_from_xml(body) do xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template" + with doc when doc != :error <- XML.parse_document(body), - template when template != nil <- XML.string_from_xpath(xpath, doc) do + template when template != nil <- XML.string_from_xpath(xpath, doc) do {:ok, template} end 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_code: status_code, body: body}} when status_code in 200..299 <- + @httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do get_template_from_xml(body) else _ -> @@ -163,28 +227,38 @@ def find_lrdd_template(domain) do def finger(account) do account = String.trim_leading(account, "@") - domain = with [_name, domain] <- String.split(account, "@") do - domain - else _e -> - URI.parse(account).host - end + + domain = + with [_name, domain] <- String.split(account, "@") do + domain + else + _e -> + URI.parse(account).host + end case find_lrdd_template(domain) do {:ok, template} -> address = String.replace(template, "{uri}", URI.encode(account)) + _ -> address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}" end - with response <- @httpoison.get(address, ["Accept": "application/xrd+xml,application/jrd+json"], follow_redirect: true), + with response <- + @httpoison.get( + address, + [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 - doc = XML.parse_document(body) - if doc != :error do - webfinger_from_xml(doc) - else - {:ok, doc} = Jason.decode(body) - webfinger_from_json(doc) - end + doc = XML.parse_document(body) + + if doc != :error do + webfinger_from_xml(doc) + else + {:ok, doc} = Jason.decode(body) + webfinger_from_json(doc) + end else e -> Logger.debug(fn -> "Couldn't finger #{account}" end) diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index eb54346c1..50d816256 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -4,7 +4,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do alias Pleroma.Web.WebFinger def host_meta(conn, _params) do - xml = WebFinger.host_meta + xml = WebFinger.host_meta() conn |> put_resp_content_type("application/xrd+xml") @@ -21,12 +21,14 @@ def webfinger(conn, %{"resource" => resource}) do else _e -> send_resp(conn, 404, "Couldn't find user") end + n when n in ["json", "jrd+json"] -> with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do json(conn, response) else _e -> send_resp(conn, 404, "Couldn't find user") end + _ -> send_resp(conn, 404, "Unsupported format") end diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 5caa8198c..e494811f9 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -26,15 +26,16 @@ def verify(subscription, getter \\ &@httpoison.get/3) do url = hd(String.split(subscription.callback, "?")) query = URI.parse(subscription.callback).query || "" params = Map.merge(params, URI.decode_query(query)) - with {:ok, response} <- getter.(url, [], [params: params]), - ^challenge <- response.body - do + + with {:ok, response} <- getter.(url, [], params: params), + ^challenge <- response.body do changeset = Changeset.change(subscription, %{state: "active"}) Repo.update(changeset) - else e -> - Logger.debug("Couldn't verify subscription") - Logger.debug(inspect(e)) - {:error, subscription} + else + e -> + Logger.debug("Couldn't verify subscription") + Logger.debug(inspect(e)) + {:error, subscription} end end @@ -46,17 +47,24 @@ def verify(subscription, getter \\ &@httpoison.get/3) do "Undo", "Delete" ] - def publish(topic, user, %{data: %{"type" => type}} = activity) when type in @supported_activities do + def publish(topic, user, %{data: %{"type" => type}} = activity) + when type in @supported_activities do # TODO: Only send to still valid subscriptions. - query = from sub in WebsubServerSubscription, - where: sub.topic == ^topic and sub.state == "active", - where: fragment("? > NOW()", sub.valid_until) + query = + from( + sub in WebsubServerSubscription, + where: sub.topic == ^topic and sub.state == "active", + where: fragment("? > NOW()", sub.valid_until) + ) + subscriptions = Repo.all(query) - Enum.each(subscriptions, fn(sub) -> - response = user - |> FeedRepresenter.to_simple_form([activity], [user]) - |> :xmerl.export_simple(:xmerl_xml) - |> to_string + + Enum.each(subscriptions, fn sub -> + response = + user + |> FeedRepresenter.to_simple_form([activity], [user]) + |> :xmerl.export_simple(:xmerl_xml) + |> to_string data = %{ xml: response, @@ -64,22 +72,24 @@ def publish(topic, user, %{data: %{"type" => type}} = activity) when type in @su callback: sub.callback, secret: sub.secret } + Pleroma.Web.Federator.enqueue(:publish_single_websub, data) end) end - def publish(_,_,_), do: "" + + def publish(_, _, _), do: "" def sign(secret, doc) do - :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16 |> String.downcase + :crypto.hmac(:sha, secret, to_string(doc)) |> Base.encode16() |> String.downcase() end def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do with {:ok, topic} <- valid_topic(params, user), {:ok, lease_time} <- lease_time(params), secret <- params["hub.secret"], - callback <- params["hub.callback"] - do + callback <- params["hub.callback"] do subscription = get_subscription(topic, callback) + data = %{ state: subscription.state || "requested", topic: topic, @@ -90,18 +100,20 @@ def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) d change = Changeset.change(subscription, data) websub = Repo.insert_or_update!(change) - change = Changeset.change(websub, %{valid_until: - NaiveDateTime.add(websub.updated_at, lease_time)}) + change = + Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)}) + websub = Repo.update!(change) Pleroma.Web.Federator.enqueue(:verify_websub, websub) {:ok, websub} - else {:error, reason} -> - Logger.debug("Couldn't create subscription") - Logger.debug(inspect(reason)) + else + {:error, reason} -> + Logger.debug("Couldn't create subscription") + Logger.debug(inspect(reason)) - {:error, reason} + {:error, reason} end end @@ -112,7 +124,8 @@ defp get_subscription(topic, callback) do # Temp hack for mastodon. defp lease_time(%{"hub.lease_seconds" => ""}) do - {:ok, 60 * 60 * 24 * 3} # three days + # three days + {:ok, 60 * 60 * 24 * 3} end defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do @@ -120,7 +133,8 @@ defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do end defp lease_time(_) do - {:ok, 60 * 60 * 24 * 3} # three days + # three days + {:ok, 60 * 60 * 24 * 3} end defp valid_topic(%{"hub.topic" => topic}, user) do @@ -134,21 +148,26 @@ defp valid_topic(%{"hub.topic" => topic}, user) do def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do topic = subscribed.info["topic"] # FIXME: Race condition, use transactions - {:ok, subscription} = with subscription when not is_nil(subscription) <- Repo.get_by(WebsubClientSubscription, topic: topic) do - subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq - change = Ecto.Changeset.change(subscription, %{subscribers: subscribers}) - Repo.update(change) - else _e -> - subscription = %WebsubClientSubscription{ - topic: topic, - hub: subscribed.info["hub"], - subscribers: [subscriber.ap_id], - state: "requested", - secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64, - user: subscribed - } - Repo.insert(subscription) - end + {:ok, subscription} = + with subscription when not is_nil(subscription) <- + Repo.get_by(WebsubClientSubscription, topic: topic) do + subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq() + change = Ecto.Changeset.change(subscription, %{subscribers: subscribers}) + Repo.update(change) + else + _e -> + subscription = %WebsubClientSubscription{ + topic: topic, + hub: subscribed.info["hub"], + subscribers: [subscriber.ap_id], + state: "requested", + secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(), + user: subscribed + } + + Repo.insert(subscription) + end + requester.(subscription) end @@ -159,24 +178,25 @@ def gather_feed_data(topic, getter \\ &@httpoison.get/1) do doc <- XML.parse_document(body), uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc), hub when not is_nil(hub) <- XML.string_from_xpath(~S{/feed/link[@rel="hub"]/@href}, doc) do - name = XML.string_from_xpath("/feed/author[1]/name", doc) preferredUsername = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc) displayName = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc) avatar = OStatus.make_avatar_object(doc) bio = XML.string_from_xpath("/feed/author[1]/summary", doc) - {:ok, %{ - "uri" => uri, - "hub" => hub, - "nickname" => preferredUsername || name, - "name" => displayName || name, - "host" => URI.parse(uri).host, - "avatar" => avatar, - "bio" => bio - }} - else e -> - {:error, e} + {:ok, + %{ + "uri" => uri, + "hub" => hub, + "nickname" => preferredUsername || name, + "name" => displayName || name, + "host" => URI.parse(uri).host, + "avatar" => avatar, + "bio" => bio + }} + else + e -> + {:error, e} end end @@ -190,43 +210,45 @@ def request_subscription(websub, poster \\ &@httpoison.post/3, timeout \\ 10_000 # This checks once a second if we are confirmed yet websub_checker = fn -> - helper = fn (helper) -> + helper = fn helper -> :timer.sleep(1000) websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted") if websub, do: websub, else: helper.(helper) end + helper.(helper) end task = Task.async(websub_checker) - with {:ok, %{status_code: 202}} <- poster.(websub.hub, {:form, data}, ["Content-type": "application/x-www-form-urlencoded"]), + with {:ok, %{status_code: 202}} <- + poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"), {:ok, websub} <- Task.yield(task, timeout) do {:ok, websub} - else e -> - Task.shutdown(task) + else + e -> + Task.shutdown(task) - change = Ecto.Changeset.change(websub, %{state: "rejected"}) - {:ok, websub} = Repo.update(change) + change = Ecto.Changeset.change(websub, %{state: "rejected"}) + {:ok, websub} = Repo.update(change) - Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end) - Logger.debug(fn -> "error: #{inspect(e)}" end) + Logger.debug(fn -> "Couldn't confirm subscription: #{inspect(websub)}" end) + Logger.debug(fn -> "error: #{inspect(e)}" end) - {:error, websub} + {:error, websub} end end def refresh_subscriptions(delta \\ 60 * 60 * 24) do Logger.debug("Refreshing subscriptions") - cut_off = NaiveDateTime.add(NaiveDateTime.utc_now, delta) + cut_off = NaiveDateTime.add(NaiveDateTime.utc_now(), delta) - query = from sub in WebsubClientSubscription, - where: sub.valid_until < ^cut_off + query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off) subs = Repo.all(query) - Enum.each(subs, fn (sub) -> + Enum.each(subs, fn sub -> Pleroma.Web.Federator.enqueue(:request_subscription, sub) end) end diff --git a/lib/pleroma/web/websub/websub_client_subscription.ex b/lib/pleroma/web/websub/websub_client_subscription.ex index c7a25ea22..8cea02939 100644 --- a/lib/pleroma/web/websub/websub_client_subscription.ex +++ b/lib/pleroma/web/websub/websub_client_subscription.ex @@ -3,13 +3,13 @@ defmodule Pleroma.Web.Websub.WebsubClientSubscription do alias Pleroma.User schema "websub_client_subscriptions" do - field :topic, :string - field :secret, :string - field :valid_until, :naive_datetime - field :state, :string - field :subscribers, {:array, :string}, default: [] - field :hub, :string - belongs_to :user, User + field(:topic, :string) + field(:secret, :string) + field(:valid_until, :naive_datetime) + field(:state, :string) + field(:subscribers, {:array, :string}, default: []) + field(:hub, :string) + belongs_to(:user, User) timestamps() end diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex index 115b64902..590dd74a1 100644 --- a/lib/pleroma/web/websub/websub_controller.ex +++ b/lib/pleroma/web/websub/websub_controller.ex @@ -8,36 +8,49 @@ defmodule Pleroma.Web.Websub.WebsubController do def websub_subscription_request(conn, %{"nickname" => nickname} = params) do user = User.get_cached_by_nickname(nickname) - with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) - do + with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do conn |> send_resp(202, "Accepted") - else {:error, reason} -> - conn - |> send_resp(500, reason) + else + {:error, reason} -> + conn + |> send_resp(500, reason) end end # TODO: Extract this into the Websub module - def websub_subscription_confirmation(conn, %{"id" => id, "hub.mode" => "subscribe", "hub.challenge" => challenge, "hub.topic" => topic} = params) do + def websub_subscription_confirmation( + conn, + %{ + "id" => id, + "hub.mode" => "subscribe", + "hub.challenge" => challenge, + "hub.topic" => topic + } = params + ) do Logger.debug("Got WebSub confirmation") Logger.debug(inspect(params)) - lease_seconds = if params["hub.lease_seconds"] do - String.to_integer(params["hub.lease_seconds"]) - else - # Guess 3 days - 60 * 60 * 24 * 3 - end - with %WebsubClientSubscription{} = websub <- Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do - valid_until = NaiveDateTime.add(NaiveDateTime.utc_now, lease_seconds) + lease_seconds = + if params["hub.lease_seconds"] do + String.to_integer(params["hub.lease_seconds"]) + else + # Guess 3 days + 60 * 60 * 24 * 3 + end + + with %WebsubClientSubscription{} = websub <- + Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do + valid_until = NaiveDateTime.add(NaiveDateTime.utc_now(), lease_seconds) change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until}) {:ok, _websub} = Repo.update(change) + conn |> send_resp(200, challenge) - else _e -> - conn - |> send_resp(500, "Error") + else + _e -> + conn + |> send_resp(500, "Error") end end @@ -48,12 +61,15 @@ def websub_incoming(conn, %{"id" => id}) do {:ok, body, _conn} = read_body(conn), ^signature <- Websub.sign(websub.secret, body) do Federator.enqueue(:incoming_doc, body) + conn |> send_resp(200, "OK") - else _e -> - Logger.debug("Can't handle incoming subscription post") - conn - |> send_resp(500, "Error") + else + _e -> + Logger.debug("Can't handle incoming subscription post") + + conn + |> send_resp(500, "Error") end end end diff --git a/lib/pleroma/web/websub/websub_server_subscription.ex b/lib/pleroma/web/websub/websub_server_subscription.ex index a29dd5860..0e5248a73 100644 --- a/lib/pleroma/web/websub/websub_server_subscription.ex +++ b/lib/pleroma/web/websub/websub_server_subscription.ex @@ -2,11 +2,11 @@ defmodule Pleroma.Web.Websub.WebsubServerSubscription do use Ecto.Schema schema "websub_server_subscriptions" do - field :topic, :string - field :callback, :string - field :secret, :string - field :valid_until, :naive_datetime - field :state, :string + field(:topic, :string) + field(:callback, :string) + field(:secret, :string) + field(:valid_until, :naive_datetime) + field(:state, :string) timestamps() end diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml/xml.ex index 8b28a7e7d..b85712d65 100644 --- a/lib/pleroma/web/xml/xml.ex +++ b/lib/pleroma/web/xml/xml.ex @@ -2,21 +2,24 @@ defmodule Pleroma.Web.XML do require Logger def string_from_xpath(_, :error), do: nil + def string_from_xpath(xpath, doc) do {:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc) - res = res - |> to_string - |> String.trim + res = + res + |> to_string + |> String.trim() if res == "", do: nil, else: res end def parse_document(text) do try do - {doc, _rest} = text - |> :binary.bin_to_list - |> :xmerl_scan.string + {doc, _rest} = + text + |> :binary.bin_to_list() + |> :xmerl_scan.string() doc catch diff --git a/lib/transports.ex b/lib/transports.ex index a820aa778..42f645b21 100644 --- a/lib/transports.ex +++ b/lib/transports.ex @@ -1,8 +1,10 @@ defmodule Phoenix.Transports.WebSocket.Raw do - import Plug.Conn, only: [ - fetch_query_params: 1, - send_resp: 3 - ] + import Plug.Conn, + only: [ + fetch_query_params: 1, + send_resp: 3 + ] + alias Phoenix.Socket.Transport def default_config do @@ -16,21 +18,24 @@ def default_config do def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do {_, opts} = handler.__transport__(transport) - conn = conn - |> fetch_query_params - |> Transport.transport_log(opts[:transport_log]) - |> Transport.force_ssl(handler, endpoint, opts) - |> Transport.check_origin(handler, endpoint, opts) + conn = + conn + |> fetch_query_params + |> Transport.transport_log(opts[:transport_log]) + |> Transport.force_ssl(handler, endpoint, opts) + |> Transport.check_origin(handler, endpoint, opts) case conn do %{halted: false} = conn -> case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do {:ok, socket} -> {:ok, conn, {__MODULE__, {socket, opts}}} + :error -> send_resp(conn, :forbidden, "") {:error, conn} end + _ -> {:error, conn} end @@ -52,16 +57,19 @@ def ws_handle(op, data, state) do |> case do {op, data} -> {:reply, {op, data}, state} + {op, data, state} -> {:reply, {op, data}, state} + %{} = state -> {:ok, state} + _ -> {:ok, state} end end - def ws_info({_,_} = tuple, state) do + def ws_info({_, _} = tuple, state) do {:reply, tuple, state} end diff --git a/lib/xml_builder.ex b/lib/xml_builder.ex index 52358c437..88f8ce2a3 100644 --- a/lib/xml_builder.ex +++ b/lib/xml_builder.ex @@ -23,7 +23,7 @@ def to_xml(content) when is_list(content) do for element <- content do to_xml(element) end - |> Enum.join + |> Enum.join() end def to_xml(%NaiveDateTime{} = time) do @@ -33,10 +33,12 @@ def to_xml(%NaiveDateTime{} = time) do def to_doc(content), do: ~s() <> to_xml(content) defp make_open_tag(tag, attributes) do - attributes_string = for {attribute, value} <- attributes do - "#{attribute}=\"#{value}\"" - end |> Enum.join(" ") + attributes_string = + for {attribute, value} <- attributes do + "#{attribute}=\"#{value}\"" + end + |> Enum.join(" ") - [tag, attributes_string] |> Enum.join(" ") |> String.trim + [tag, attributes_string] |> Enum.join(" ") |> String.trim() end end diff --git a/mix.exs b/mix.exs index 6a518578d..277c81672 100644 --- a/mix.exs +++ b/mix.exs @@ -2,48 +2,51 @@ defmodule Pleroma.Mixfile do use Mix.Project def project do - [app: :pleroma, - version: "0.9.0", - elixir: "~> 1.4", - elixirc_paths: elixirc_paths(Mix.env), - compilers: [:phoenix, :gettext] ++ Mix.compilers, - start_permanent: Mix.env == :prod, - aliases: aliases(), - deps: deps()] + [ + app: :pleroma, + version: "0.9.0", + elixir: "~> 1.4", + elixirc_paths: elixirc_paths(Mix.env()), + compilers: [:phoenix, :gettext] ++ Mix.compilers(), + start_permanent: Mix.env() == :prod, + aliases: aliases(), + deps: deps() + ] end # Configuration for the OTP application. # # Type `mix help compile.app` for more information. def application do - [mod: {Pleroma.Application, []}, - extra_applications: [:logger, :runtime_tools, :comeonin]] + [mod: {Pleroma.Application, []}, extra_applications: [:logger, :runtime_tools, :comeonin]] end # Specifies which paths to compile per environment. defp elixirc_paths(:test), do: ["lib", "test/support"] - defp elixirc_paths(_), do: ["lib"] + defp elixirc_paths(_), do: ["lib"] # Specifies your project dependencies. # # Type `mix help deps` for examples and options. defp deps do - [{:phoenix, "~> 1.3.0"}, - {:phoenix_pubsub, "~> 1.0"}, - {:phoenix_ecto, "~> 3.2"}, - {:postgrex, ">= 0.0.0"}, - {:gettext, "~> 0.11"}, - {:cowboy, "~> 1.0", override: true}, - {:comeonin, "~> 3.0"}, - {:trailing_format_plug, "~> 0.0.5" }, - {:html_sanitize_ex, "~> 1.3.0-rc1"}, - {:phoenix_html, "~> 2.10"}, - {:calendar, "~> 0.16.1"}, - {:cachex, "~> 2.1"}, - {:httpoison, "~> 0.11.2"}, - {:jason, "~> 1.0"}, - {:ex_machina, "~> 2.0", only: :test}, - {:credo, "~> 0.7", only: [:dev, :test]}] + [ + {:phoenix, "~> 1.3.0"}, + {:phoenix_pubsub, "~> 1.0"}, + {:phoenix_ecto, "~> 3.2"}, + {:postgrex, ">= 0.0.0"}, + {:gettext, "~> 0.11"}, + {:cowboy, "~> 1.0", override: true}, + {:comeonin, "~> 3.0"}, + {:trailing_format_plug, "~> 0.0.5"}, + {:html_sanitize_ex, "~> 1.3.0-rc1"}, + {:phoenix_html, "~> 2.10"}, + {:calendar, "~> 0.16.1"}, + {:cachex, "~> 2.1"}, + {:httpoison, "~> 0.11.2"}, + {:jason, "~> 1.0"}, + {:ex_machina, "~> 2.0", only: :test}, + {:credo, "~> 0.7", only: [:dev, :test]} + ] end # Aliases are shortcuts or tasks specific to the current project. @@ -53,8 +56,10 @@ defp deps do # # See the documentation for `Mix` for more info on aliases. defp aliases do - ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], - "ecto.reset": ["ecto.drop", "ecto.setup"], - "test": ["ecto.create --quiet", "ecto.migrate", "test"]] + [ + "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], + "ecto.reset": ["ecto.drop", "ecto.setup"], + test: ["ecto.create --quiet", "ecto.migrate", "test"] + ] end end diff --git a/test/activity_test.exs b/test/activity_test.exs index 366a2f957..55849c522 100644 --- a/test/activity_test.exs +++ b/test/activity_test.exs @@ -18,7 +18,9 @@ test "returns activities by it's objects AP ids" do test "returns the activity that created an object" do activity = insert(:note_activity) - found_activity = Pleroma.Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"]) + + found_activity = + Pleroma.Activity.get_create_activity_by_object_ap_id(activity.data["object"]["id"]) assert activity == found_activity end diff --git a/test/formatter_test.exs b/test/formatter_test.exs index 9ec2cc9f2..b7246252f 100644 --- a/test/formatter_test.exs +++ b/test/formatter_test.exs @@ -7,44 +7,56 @@ defmodule Pleroma.FormatterTest do describe ".add_hashtag_links" do test "turns hashtags into links" do text = "I love #cofe and #2hu" - expected_text = "I love and " + + expected_text = + "I love and " tags = Formatter.parse_tags(text) - assert expected_text == Formatter.add_hashtag_links({[], text}, tags) |> Formatter.finalize + + assert expected_text == + Formatter.add_hashtag_links({[], text}, tags) |> Formatter.finalize() end end describe ".add_links" do test "turning urls into links" do text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla." - expected = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla." - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + expected = + "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla." + + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected text = "https://mastodon.social/@lambadalambda" - expected = "https://mastodon.social/@lambadalambda" - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + expected = + "https://mastodon.social/@lambadalambda" + + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected text = "@lambadalambda" expected = "@lambadalambda" - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected text = "http://www.cs.vu.nl/~ast/intel/" expected = "http://www.cs.vu.nl/~ast/intel/" - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" - expected = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + expected = + "https://forum.zdoom.org/viewtopic.php?f=44&t=57087" + + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - expected = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" - assert Formatter.add_links({[], text}) |> Formatter.finalize == expected + expected = + "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" + + assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected end end @@ -60,9 +72,14 @@ test "gives a replacement for user links" do {subs, text} = Formatter.add_user_links({[], text}, mentions) assert length(subs) == 3 - Enum.each(subs, fn({uuid, _}) -> assert String.contains?(text, uuid) end) + Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) - expected_text = "@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme" + expected_text = + "@gsimg According to @archaeme, that is @daggsy. Also hello @archaeme" assert expected_text == Formatter.finalize({subs, text}) end @@ -71,6 +88,7 @@ test "gives a replacement for user links" do describe ".parse_tags" do test "parses tags in the text" do text = "Here's a #Test. Maybe these are #working or not. What about #漢字? And #は。" + expected = [ {"#Test", "test"}, {"#working", "working"}, @@ -92,7 +110,7 @@ test "it can parse mentions and return the relevant users" do expected_result = [ {"@gsimg", gsimg}, {"@archaeme", archaeme}, - {"@archaeme@archae.me", archaeme_remote}, + {"@archaeme@archae.me", archaeme_remote} ] assert Formatter.parse_mentions(text) == expected_result @@ -101,7 +119,8 @@ test "it can parse mentions and return the relevant users" do test "it adds cool emoji" do text = "I love :moominmamma:" - expected_result = "I love moominmamma" + expected_result = + "I love moominmamma" assert Formatter.emojify(text) == expected_result end diff --git a/test/notification_test.exs b/test/notification_test.exs index 1504004c8..568ad642c 100644 --- a/test/notification_test.exs +++ b/test/notification_test.exs @@ -10,7 +10,10 @@ test "notifies someone when they are directly addressed" do other_user = insert(:user) third_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname} and @#{third_user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{ + "status" => "hey @#{other_user.nickname} and @#{third_user.nickname}" + }) {:ok, [notification, other_notification]} = Notification.create_notifications(activity) @@ -37,7 +40,9 @@ test "it gets a notification that belongs to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.get(other_user, notification.id) @@ -48,7 +53,9 @@ test "it returns error if the notification doesn't belong to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.get(user, notification.id) end @@ -59,7 +66,9 @@ test "it dismisses a notification that belongs to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) {:ok, notification} = Notification.dismiss(other_user, notification.id) @@ -70,7 +79,9 @@ test "it returns error if the notification doesn't belong to the user" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) {:error, _notification} = Notification.dismiss(user, notification.id) end @@ -82,9 +93,18 @@ test "it clears all notifications belonging to the user" do other_user = insert(:user) third_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !"}) + {:ok, activity} = + TwitterAPI.create_status(user, %{ + "status" => "hey @#{other_user.nickname} and @#{third_user.nickname} !" + }) + {:ok, _notifs} = Notification.create_notifications(activity) - {:ok, activity} = TwitterAPI.create_status(user, %{"status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !"}) + + {:ok, activity} = + TwitterAPI.create_status(user, %{ + "status" => "hey again @#{other_user.nickname} and @#{third_user.nickname} !" + }) + {:ok, _notifs} = Notification.create_notifications(activity) Notification.clear(other_user) diff --git a/test/plugs/authentication_plug_test.exs b/test/plugs/authentication_plug_test.exs index 5480dab43..729ac8ae5 100644 --- a/test/plugs/authentication_plug_test.exs +++ b/test/plugs/authentication_plug_test.exs @@ -37,22 +37,24 @@ defp basic_auth_enc(username, password) do describe "without an authorization header" do test "it halts the application" do - conn = build_conn() - |> Plug.Session.call(Plug.Session.init(@session_opts)) - |> fetch_session - |> AuthenticationPlug.call(%{}) + conn = + build_conn() + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session + |> AuthenticationPlug.call(%{}) assert conn.status == 403 assert conn.halted == true end test "it assigns a nil user if the 'optional' option is used" do - conn = build_conn() - |> Plug.Session.call(Plug.Session.init(@session_opts)) - |> fetch_session - |> AuthenticationPlug.call(%{optional: true}) + conn = + build_conn() + |> Plug.Session.call(Plug.Session.init(@session_opts)) + |> fetch_session + |> AuthenticationPlug.call(%{optional: true}) - assert %{ user: nil } == conn.assigns + assert %{user: nil} == conn.assigns end end @@ -73,9 +75,9 @@ test "it assigns a nil user if the 'optional' option is used" do build_conn() |> Plug.Session.call(Plug.Session.init(@session_opts)) |> fetch_session - |> AuthenticationPlug.call(%{optional: true, fetcher: &fetch_nil/1 }) + |> AuthenticationPlug.call(%{optional: true, fetcher: &fetch_nil/1}) - assert %{ user: nil } == conn.assigns + assert %{user: nil} == conn.assigns end end @@ -113,7 +115,7 @@ test "it assigns a nil user if the 'optional' option is used" do |> put_req_header("authorization", header) |> AuthenticationPlug.call(opts) - assert %{ user: nil } == conn.assigns + assert %{user: nil} == conn.assigns end end @@ -126,13 +128,14 @@ test "it assigns the user", %{conn: conn} do header = basic_auth_enc("dude", "guy") - conn = conn + conn = + conn |> Plug.Session.call(Plug.Session.init(@session_opts)) |> fetch_session |> put_req_header("authorization", header) |> AuthenticationPlug.call(opts) - assert %{ user: @user } == conn.assigns + assert %{user: @user} == conn.assigns assert get_session(conn, :user_id) == @user.id assert conn.halted == false end @@ -147,7 +150,8 @@ test "it halts the appication", %{conn: conn} do header = basic_auth_enc("dude", "guy") - conn = conn + conn = + conn |> Plug.Session.call(Plug.Session.init(@session_opts)) |> fetch_session |> put_req_header("authorization", header) @@ -167,14 +171,15 @@ test "it assigns the user", %{conn: conn} do header = basic_auth_enc("dude", "THIS IS WRONG") - conn = conn + conn = + conn |> Plug.Session.call(Plug.Session.init(@session_opts)) |> fetch_session |> put_session(:user_id, @user.id) |> put_req_header("authorization", header) |> AuthenticationPlug.call(opts) - assert %{ user: @user } == conn.assigns + assert %{user: @user} == conn.assigns assert get_session(conn, :user_id) == @user.id assert conn.halted == false end @@ -182,8 +187,9 @@ test "it assigns the user", %{conn: conn} do describe "with an assigned user" do test "it does nothing, returning the incoming conn", %{conn: conn} do - conn = conn - |> assign(:user, @user) + conn = + conn + |> assign(:user, @user) conn_result = AuthenticationPlug.call(conn, %{}) diff --git a/test/support/builders/activity_builder.ex b/test/support/builders/activity_builder.ex index 0ebf633b3..d9c188955 100644 --- a/test/support/builders/activity_builder.ex +++ b/test/support/builders/activity_builder.ex @@ -4,17 +4,19 @@ defmodule Pleroma.Builders.ActivityBuilder do def build(data \\ %{}, opts \\ %{}) do user = opts[:user] || Pleroma.Factory.insert(:user) + activity = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), "actor" => user.ap_id, "to" => ["https://www.w3.org/ns/activitystreams#Public"], "type" => "Create", "object" => %{ "type" => "Note", "content" => "test", - "to" => ["https://www.w3.org/ns/activitystreams#Public"], + "to" => ["https://www.w3.org/ns/activitystreams#Public"] } } + Map.merge(activity, data) end @@ -24,7 +26,7 @@ def insert(data \\ %{}, opts \\ %{}) do end def insert_list(times, data \\ %{}, opts \\ %{}) do - Enum.map(1..times, fn (n) -> + Enum.map(1..times, fn n -> {:ok, activity} = insert(data, opts) activity end) diff --git a/test/support/builders/user_builder.ex b/test/support/builders/user_builder.ex index 1e1e80ac9..7a1ca79b5 100644 --- a/test/support/builders/user_builder.ex +++ b/test/support/builders/user_builder.ex @@ -10,6 +10,7 @@ def build(data \\ %{}) do bio: "A tester.", ap_id: "some id" } + Map.merge(user, data) end diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex index aec3061b3..68995a01d 100644 --- a/test/support/channel_case.ex +++ b/test/support/channel_case.ex @@ -25,13 +25,13 @@ defmodule Pleroma.Web.ChannelCase do end end - setup tags do :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + unless tags[:async] do Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) end + :ok end - end diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex index a83ef3b3a..2e6707087 100644 --- a/test/support/conn_case.ex +++ b/test/support/conn_case.ex @@ -26,14 +26,14 @@ defmodule Pleroma.Web.ConnCase do end end - setup tags do Cachex.clear(:user_cache) :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) + unless tags[:async] do Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) end + {:ok, conn: Phoenix.ConnTest.build_conn()} end - end diff --git a/test/support/factory.ex b/test/support/factory.ex index 1445fe828..8e21e2562 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -9,20 +9,27 @@ def user_factory do password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), bio: sequence(:bio, &"Tester Number #{&1}") } - %{ user | ap_id: Pleroma.User.ap_id(user), follower_address: Pleroma.User.ap_followers(user), following: [Pleroma.User.ap_id(user)] } + + %{ + user + | ap_id: Pleroma.User.ap_id(user), + follower_address: Pleroma.User.ap_followers(user), + following: [Pleroma.User.ap_id(user)] + } end def note_factory do text = sequence(:text, &"This is :moominmamma: note #{&1}") user = insert(:user) + data = %{ "type" => "Note", "content" => text, - "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(), "actor" => user.ap_id, "to" => ["https://www.w3.org/ns/activitystreams#Public"], - "published" => DateTime.utc_now() |> DateTime.to_iso8601, + "published" => DateTime.utc_now() |> DateTime.to_iso8601(), "likes" => [], "like_count" => 0, "context" => "2hu", @@ -40,13 +47,14 @@ def note_factory do def note_activity_factory do note = insert(:note) + data = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "type" => "Create", "actor" => note.data["actor"], "to" => note.data["to"], "object" => note.data, - "published" => DateTime.utc_now() |> DateTime.to_iso8601, + "published" => DateTime.utc_now() |> DateTime.to_iso8601(), "context" => note.data["context"] } @@ -62,11 +70,11 @@ def like_activity_factory do user = insert(:user) data = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "actor" => user.ap_id, "type" => "Like", "object" => note_activity.data["object"]["id"], - "published_at" => DateTime.utc_now() |> DateTime.to_iso8601 + "published_at" => DateTime.utc_now() |> DateTime.to_iso8601() } %Pleroma.Activity{ @@ -79,11 +87,11 @@ def follow_activity_factory do followed = insert(:user) data = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "actor" => follower.ap_id, "type" => "Follow", "object" => followed.ap_id, - "published_at" => DateTime.utc_now() |> DateTime.to_iso8601 + "published_at" => DateTime.utc_now() |> DateTime.to_iso8601() } %Pleroma.Activity{ @@ -96,7 +104,7 @@ def websub_subscription_factory do topic: "http://example.org", callback: "http://example/org/callback", secret: "here's a secret", - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 100), + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 100), state: "requested" } end diff --git a/test/support/httpoison_mock.ex b/test/support/httpoison_mock.ex index c70598691..ba35c4460 100644 --- a/test/support/httpoison_mock.ex +++ b/test/support/httpoison_mock.ex @@ -3,461 +3,712 @@ defmodule HTTPoisonMock do def get(url, body \\ [], headers \\ []) - def get("http://framatube.org/.well-known/webfinger?resource=acct:framasoft@framatube.org", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json") - }} - end - def get("http://gnusocial.de/.well-known/webfinger?resource=acct:winterdienst@gnusocial.de", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json") - }} + def get( + "http://framatube.org/.well-known/webfinger?resource=acct:framasoft@framatube.org", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json") + }} end - def get("https://social.heldscal.la/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "nonexistant@social.heldscal.la"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 500, - body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml") - }} + def get( + "http://gnusocial.de/.well-known/webfinger?resource=acct:winterdienst@gnusocial.de", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json") + }} end - def get("https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "nonexistant@social.heldscal.la"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 500, + body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml") + }} end - def get("https://social.heldscal.la/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "shp@social.heldscal.la"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger?resource=shp@social.heldscal.la", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") + }} end - def get("https://social.heldscal.la/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://social.heldscal.la/user/23211"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "shp@social.heldscal.la"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") + }} end - def get("https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/23211", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://social.heldscal.la/user/23211"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") + }} end - def get("https://social.heldscal.la/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://social.heldscal.la/user/29191"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/23211", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") + }} end - def get("https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/29191", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://social.heldscal.la/user/29191"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") + }} end - def get("https://mastodon.social/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://mastodon.social/users/lambadalambda"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml") - }} + def get( + "https://social.heldscal.la/.well-known/webfinger?resource=https://social.heldscal.la/user/29191", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") + }} end - def get("https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/lambadalambda", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml") - }} + def get( + "https://mastodon.social/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://mastodon.social/users/lambadalambda"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml" + ) + }} end - def get("https://shitposter.club/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://shitposter.club/user/1"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") - }} + def get( + "https://mastodon.social/.well-known/webfinger?resource=https://mastodon.social/users/lambadalambda", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml" + ) + }} end - def get("https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") - }} + def get( + "https://shitposter.club/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://shitposter.club/user/1"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") + }} end - def get("https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml") - }} + def get( + "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/1", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") + }} end - def get("http://gs.example.org/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "http://gs.example.org:4040/index.php/user/1"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml") - }} + def get( + "https://shitposter.club/.well-known/webfinger?resource=https://shitposter.club/user/5381", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml") + }} end - def get("http://gs.example.org/.well-known/webfinger?resource=http://gs.example.org:4040/index.php/user/1", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml") - }} + def get( + "http://gs.example.org/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "http://gs.example.org:4040/index.php/user/1"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml" + ) + }} end - def get("https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=https://social.stopwatchingus-heidelberg.de/user/18330", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml") - }} + def get( + "http://gs.example.org/.well-known/webfinger?resource=http://gs.example.org:4040/index.php/user/1", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml" + ) + }} end - def get("https://pleroma.soykaf.com/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://pleroma.soykaf.com/users/lain"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") - }} + def get( + "https://social.stopwatchingus-heidelberg.de/.well-known/webfinger?resource=https://social.stopwatchingus-heidelberg.de/user/18330", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml") + }} end - def get("https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/lain", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") - }} + def get( + "https://pleroma.soykaf.com/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://pleroma.soykaf.com/users/lain"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") + }} + end + + def get( + "https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/lain", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") + }} end def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml" + ) + }} end def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom") + }} end def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml" + ) + }} end def get("https://mastodon.social/users/lambadalambda.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom" + ) + }} end - def get("https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml") - }} + def get( + "https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom", + _body, + _headers + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml") + }} end def get("https://pleroma.soykaf.com/users/lain/feed.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml" + ) + }} end def get("https://social.sakamoto.gq/users/eal/feed.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom") + }} end def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml" + ) + }} end def get("https://shitposter.club/notice/2827873", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html") + }} end def get("https://shitposter.club/api/statuses/show/2827873.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml" + ) + }} end def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml") - }} + {:ok, + %Response{ + status_code: 200, + body: + File.read!( + "test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml" + ) + }} end - def post("https://social.heldscal.la/main/push/hub", {:form, data}, ["Content-type": "application/x-www-form-urlencoded"]) do - {:ok, %Response{ - status_code: 202 - }} + def post( + "https://social.heldscal.la/main/push/hub", + {:form, data}, + "Content-type": "application/x-www-form-urlencoded" + ) do + {:ok, + %Response{ + status_code: 202 + }} end - def get("https://pawoo.net/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://pawoo.net/users/pekorino"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") - }} + def get( + "https://pawoo.net/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://pawoo.net/users/pekorino"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") + }} end - def get("https://pawoo.net/.well-known/webfinger?resource=https://pawoo.net/users/pekorino", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") - }} + def get( + "https://pawoo.net/.well-known/webfinger?resource=https://pawoo.net/users/pekorino", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") + }} end def get("https://pawoo.net/users/pekorino.atom", _, _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom") + }} end - def get("https://mamot.fr/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://mamot.fr/users/Skruyb"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") - }} + def get( + "https://mamot.fr/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://mamot.fr/users/Skruyb"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") + }} end - def get("https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") - }} + def get( + "https://mamot.fr/.well-known/webfinger?resource=https://mamot.fr/users/Skruyb", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") + }} end - def get("https://social.sakamoto.gq/.well-known/webfinger", [Accept: "application/xrd+xml,application/jrd+json"], [params: [resource: "https://social.sakamoto.gq/users/eal"], follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") - }} + def get( + "https://social.sakamoto.gq/.well-known/webfinger", + [Accept: "application/xrd+xml,application/jrd+json"], + params: [resource: "https://social.sakamoto.gq/users/eal"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") + }} end - def get("https://social.sakamoto.gq/.well-known/webfinger?resource=https://social.sakamoto.gq/users/eal", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") - }} + def get( + "https://social.sakamoto.gq/.well-known/webfinger?resource=https://social.sakamoto.gq/users/eal", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") + }} end - def get("https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/shp", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner") - }} + def get( + "https://pleroma.soykaf.com/.well-known/webfinger?resource=https://pleroma.soykaf.com/users/shp", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner") + }} end - def get("https://squeet.me/xrd/?uri=lain@squeet.me", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml") - }} + def get( + "https://squeet.me/xrd/?uri=lain@squeet.me", + [Accept: "application/xrd+xml,application/jrd+json"], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml") + }} end def get("https://mamot.fr/users/Skruyb.atom", _, _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom") + }} end - def get("https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", [Accept: "application/atom+xml"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom") - }} + def get( + "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", + [Accept: "application/atom+xml"], + _ + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom") + }} end def get("https://pleroma.soykaf.com/users/shp/feed.atom", _, _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed") + }} end - def get("http://social.heldscal.la/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta") - }} + def get("http://social.heldscal.la/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta") + }} end - def get("http://macgirvin.com/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta") - }} + def get("http://macgirvin.com/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta") + }} end - def get("http://mastodon.social/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta") - }} + def get("http://mastodon.social/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta") + }} end - def get("http://shitposter.club/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta") - }} + def get("http://shitposter.club/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta") + }} end - def get("http://pleroma.soykaf.com/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta") - }} + def get("http://pleroma.soykaf.com/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta") + }} end - def get("http://social.sakamoto.gq/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta") - }} + def get("http://social.sakamoto.gq/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta") + }} end - def get("http://gs.example.org/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta") - }} + def get("http://gs.example.org/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta") + }} end - def get("http://pawoo.net/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/pawoo.net_host_meta") - }} + def get("http://pawoo.net/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/pawoo.net_host_meta") + }} end - def get("http://mamot.fr/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/mamot.fr_host_meta") - }} + def get("http://mamot.fr/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/mamot.fr_host_meta") + }} end - def get("http://mastodon.xyz/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/mastodon.xyz_host_meta") - }} + def get("http://mastodon.xyz/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/mastodon.xyz_host_meta") + }} end - def get("http://social.wxcafe.net/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/social.wxcafe.net_host_meta") - }} + def get("http://social.wxcafe.net/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/social.wxcafe.net_host_meta") + }} end - def get("http://squeet.me/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta") - }} + def get("http://squeet.me/.well-known/host-meta", [], follow_redirect: true) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta") + }} end - def get("http://social.stopwatchingus-heidelberg.de/.well-known/host-meta", [], [follow_redirect: true]) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta") - }} + def get( + "http://social.stopwatchingus-heidelberg.de/.well-known/host-meta", + [], + follow_redirect: true + ) do + {:ok, + %Response{ + status_code: 200, + body: + File.read!("test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta") + }} end - def get("http://mastodon.example.org/users/admin", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json") - }} + def get("http://mastodon.example.org/users/admin", [Accept: "application/activity+json"], _) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json") + }} end - def get("https://masto.quad.moe/users/_HellPie", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/hellpie.json") - }} + def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/hellpie.json") + }} end - def get("https://niu.moe/users/rye", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/rye.json") - }} + def get("https://niu.moe/users/rye", [Accept: "application/activity+json"], _) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/rye.json") + }} end - def get("https://mst3k.interlinked.me/users/luciferMysticus", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json") - }} + def get( + "https://mst3k.interlinked.me/users/luciferMysticus", + [Accept: "application/activity+json"], + _ + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json") + }} end - def get("https://mstdn.io/users/mayuutann", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/mayumayu.json") - }} + def get("https://mstdn.io/users/mayuutann", [Accept: "application/activity+json"], _) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/mayumayu.json") + }} end - def get("http://mastodon.example.org/@admin/99541947525187367", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/mastodon-note-object.json") - }} + def get( + "http://mastodon.example.org/@admin/99541947525187367", + [Accept: "application/activity+json"], + _ + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/mastodon-note-object.json") + }} end - def get("https://mstdn.io/users/mayuutann/statuses/99568293732299394", ["Accept": "application/activity+json"], _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json") - }} + def get( + "https://mstdn.io/users/mayuutann/statuses/99568293732299394", + [Accept: "application/activity+json"], + _ + ) do + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json") + }} end def get("https://shitposter.club/notice/7369654", _, _) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/7369654.html") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/7369654.html") + }} end def get("https://shitposter.club/api/statuses/show/7369654.atom", _body, _headers) do - {:ok, %Response{ - status_code: 200, - body: File.read!("test/fixtures/httpoison_mock/7369654.atom") - }} + {:ok, + %Response{ + status_code: 200, + body: File.read!("test/fixtures/httpoison_mock/7369654.atom") + }} end def get(url, body, headers) do - {:error, "Not implemented the mock response for get #{inspect(url)}, #{inspect(body)}, #{inspect(headers)}"} + {:error, + "Not implemented the mock response for get #{inspect(url)}, #{inspect(body)}, #{ + inspect(headers) + }"} end def post(url, body, headers) do diff --git a/test/support/ostatus_mock.ex b/test/support/ostatus_mock.ex index c26e91990..36865ae02 100644 --- a/test/support/ostatus_mock.ex +++ b/test/support/ostatus_mock.ex @@ -1,5 +1,6 @@ defmodule Pleroma.Web.OStatusMock do import Pleroma.Factory + def handle_incoming(_doc) do insert(:note_activity) end diff --git a/test/test_helper.exs b/test/test_helper.exs index a2a9c7fd9..94ba68ff8 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -2,4 +2,3 @@ Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual) {:ok, _} = Application.ensure_all_started(:ex_machina) - diff --git a/test/upload_test.exs b/test/upload_test.exs index f90c4d713..d68b3e7ba 100644 --- a/test/upload_test.exs +++ b/test/upload_test.exs @@ -4,20 +4,37 @@ defmodule Pleroma.UploadTest do describe "Storing a file" do test "copies the file to the configured folder" do - file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an [image.jpg"} + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an [image.jpg" + } + data = Upload.store(file) assert data["name"] == "an [image.jpg" - assert List.first(data["url"])["href"] == "http://localhost:4001/media/#{data["uuid"]}/an%20%5Bimage.jpg" + + assert List.first(data["url"])["href"] == + "http://localhost:4001/media/#{data["uuid"]}/an%20%5Bimage.jpg" end test "fixes an incorrect content type" do - file = %Plug.Upload{content_type: "application/octet-stream", path: Path.absname("test/fixtures/image.jpg"), filename: "an [image.jpg"} + file = %Plug.Upload{ + content_type: "application/octet-stream", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an [image.jpg" + } + data = Upload.store(file) assert hd(data["url"])["mediaType"] == "image/jpeg" end test "does not modify a valid content type" do - file = %Plug.Upload{content_type: "image/png", path: Path.absname("test/fixtures/image.jpg"), filename: "an [image.jpg"} + file = %Plug.Upload{ + content_type: "image/png", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an [image.jpg" + } + data = Upload.store(file) assert hd(data["url"])["mediaType"] == "image/png" end diff --git a/test/user_test.exs b/test/user_test.exs index 35de0a7ce..6e9025f2a 100644 --- a/test/user_test.exs +++ b/test/user_test.exs @@ -10,15 +10,15 @@ defmodule Pleroma.UserTest do import Ecto.Query test "ap_id returns the activity pub id for the user" do - user = UserBuilder.build + user = UserBuilder.build() - expected_ap_id = "#{Pleroma.Web.base_url}/users/#{user.nickname}" + expected_ap_id = "#{Pleroma.Web.base_url()}/users/#{user.nickname}" assert expected_ap_id == User.ap_id(user) end test "ap_followers returns the followers collection for the user" do - user = UserBuilder.build + user = UserBuilder.build() expected_followers_collection = "#{User.ap_id(user)}/followers" @@ -67,7 +67,7 @@ test "unfollow takes a user and another user" do followed = insert(:user) user = insert(:user, %{following: [User.ap_followers(followed)]}) - {:ok, user, _activity } = User.unfollow(user, followed) + {:ok, user, _activity} = User.unfollow(user, followed) user = Repo.get(User, user.id) @@ -83,7 +83,6 @@ test "unfollow doesn't unfollow yourself" do assert user.following == [user.ap_id] end - test "test if a user is following another user" do followed = insert(:user) user = insert(:user, %{following: [User.ap_followers(followed)]}) @@ -104,12 +103,12 @@ test "test if a user is following another user" do test "it requires an email, name, nickname and password, bio is optional" do @full_user_data - |> Map.keys - |> Enum.each(fn (key) -> + |> Map.keys() + |> Enum.each(fn key -> params = Map.delete(@full_user_data, key) changeset = User.register_changeset(%User{}, params) - assert (if key == :bio, do: changeset.valid?, else: not changeset.valid?) + assert if key == :bio, do: changeset.valid?, else: not changeset.valid? end) end @@ -120,7 +119,11 @@ test "it sets the password_hash, ap_id and following fields" do assert is_binary(changeset.changes[:password_hash]) assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) - assert changeset.changes[:following] == [User.ap_followers(%User{nickname: @full_user_data.nickname})] + + assert changeset.changes[:following] == [ + User.ap_followers(%User{nickname: @full_user_data.nickname}) + ] + assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers" end end @@ -158,12 +161,24 @@ test "returns nil for nonexistant local user" do test "returns an ap_id for a user" do user = insert(:user) - assert User.ap_id(user) == Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.nickname) + + assert User.ap_id(user) == + Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Endpoint, + :feed_redirect, + user.nickname + ) end test "returns an ap_followers link for a user" do user = insert(:user) - assert User.ap_followers(user) == Pleroma.Web.Router.Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.nickname) <> "/followers" + + assert User.ap_followers(user) == + Pleroma.Web.Router.Helpers.o_status_url( + Pleroma.Web.Endpoint, + :feed_redirect, + user.nickname + ) <> "/followers" end describe "remote user creation changeset" do @@ -184,7 +199,8 @@ test "it confirms validity" do test "it sets the follower_adress" do cs = User.remote_user_creation(@valid_remote) # remote users get a fake local follower address - assert cs.changes.follower_address == User.ap_followers(%User{ nickname: @valid_remote[:nickname] }) + assert cs.changes.follower_address == + User.ap_followers(%User{nickname: @valid_remote[:nickname]}) end test "it enforces the fqn format for nicknames" do @@ -196,7 +212,7 @@ test "it enforces the fqn format for nicknames" do test "it has required fields" do [:name, :nickname, :ap_id] - |> Enum.each(fn (field) -> + |> Enum.each(fn field -> cs = User.remote_user_creation(Map.delete(@valid_remote, field)) refute cs.valid? end) @@ -204,7 +220,7 @@ test "it has required fields" do test "it restricts some sizes" do [bio: 5000, name: 100] - |> Enum.each(fn ({field, size}) -> + |> Enum.each(fn {field, size} -> string = String.pad_leading(".", size) cs = User.remote_user_creation(Map.put(@valid_remote, field, string)) assert cs.valid? @@ -323,7 +339,11 @@ test "get recipients from activity" do user_two = insert(:user, local: false) addressed = insert(:user, local: true) addressed_remote = insert(:user, local: false) - {:ok, activity} = CommonAPI.post(actor, %{"status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}"}) + + {:ok, activity} = + CommonAPI.post(actor, %{ + "status" => "hey @#{addressed.nickname} @#{addressed_remote.nickname}" + }) assert [addressed] == User.get_recipients_from_activity(activity) @@ -379,7 +399,7 @@ test "get_public_key_for_ap_id fetches a user that's not in the db" do test "insert or update a user from given data" do user = insert(:user, %{nickname: "nick@name.de"}) - data = %{ ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname } + data = %{ap_id: user.ap_id <> "xxx", name: user.name, nickname: user.nickname} assert {:ok, %User{}} = User.insert_or_update_user(data) end diff --git a/test/web/activity_pub/activity_pub_controller_test.exs b/test/web/activity_pub/activity_pub_controller_test.exs index c8082cd03..28d765d83 100644 --- a/test/web/activity_pub/activity_pub_controller_test.exs +++ b/test/web/activity_pub/activity_pub_controller_test.exs @@ -9,9 +9,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do test "it returns a json representation of the user", %{conn: conn} do user = insert(:user) - conn = conn - |> put_req_header("accept", "application/activity+json") - |> get("/users/#{user.nickname}") + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/users/#{user.nickname}") user = Repo.get(User, user.id) @@ -22,11 +23,12 @@ test "it returns a json representation of the user", %{conn: conn} do describe "/object/:uuid" do test "it returns a json representation of the object", %{conn: conn} do note = insert(:note) - uuid = String.split(note.data["id"], "/") |> List.last + uuid = String.split(note.data["id"], "/") |> List.last() - conn = conn - |> put_req_header("accept", "application/activity+json") - |> get("/objects/#{uuid}") + conn = + conn + |> put_req_header("accept", "application/activity+json") + |> get("/objects/#{uuid}") assert json_response(conn, 200) == ObjectView.render("object.json", %{object: note}) end @@ -34,12 +36,13 @@ test "it returns a json representation of the object", %{conn: conn} do describe "/users/:nickname/inbox" do test "it inserts an incoming activity into the database", %{conn: conn} do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode! + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() - conn = conn - |> assign(:valid_signature, true) - |> put_req_header("content-type", "application/activity+json") - |> post("/inbox", data) + conn = + conn + |> assign(:valid_signature, true) + |> put_req_header("content-type", "application/activity+json") + |> post("/inbox", data) assert "ok" == json_response(conn, 200) :timer.sleep(500) diff --git a/test/web/activity_pub/activity_pub_test.exs b/test/web/activity_pub/activity_pub_test.exs index 96792bca5..cf25abfc1 100644 --- a/test/web/activity_pub/activity_pub_test.exs +++ b/test/web/activity_pub/activity_pub_test.exs @@ -22,7 +22,7 @@ test "it returns a user" do describe "insertion" do test "returns the activity if one with the same id is already in" do activity = insert(:note_activity) - {:ok, new_activity}= ActivityPub.insert(activity.data) + {:ok, new_activity} = ActivityPub.insert(activity.data) assert activity == new_activity end @@ -37,6 +37,7 @@ test "inserts a given map into the activity database, giving it an id if it has assert is_binary(activity.data["id"]) given_id = "bla" + data = %{ "ok" => true, "id" => given_id @@ -63,7 +64,14 @@ test "adds an id to a given object if it lacks one and is a note and inserts it describe "create activities" do test "removes doubled 'to' recipients" do - {:ok, activity} = ActivityPub.create(%{to: ["user1", "user1", "user2"], actor: %User{ap_id: "1"}, context: "", object: %{}}) + {:ok, activity} = + ActivityPub.create(%{ + to: ["user1", "user1", "user2"], + actor: %User{ap_id: "1"}, + context: "", + object: %{} + }) + assert activity.data["to"] == ["user1", "user2"] assert activity.actor == "1" assert activity.recipients == ["user1", "user2"] @@ -124,11 +132,11 @@ test "doesn't return blocked activities" do describe "public fetch activities" do test "retrieves public activities" do - _activities = ActivityPub.fetch_public_activities + _activities = ActivityPub.fetch_public_activities() - %{public: public} = ActivityBuilder.public_and_non_public + %{public: public} = ActivityBuilder.public_and_non_public() - activities = ActivityPub.fetch_public_activities + activities = ActivityPub.fetch_public_activities() assert length(activities) == 1 assert Enum.at(activities, 0) == public end @@ -137,7 +145,7 @@ test "retrieves a maximum of 20 activities" do activities = ActivityBuilder.insert_list(30) last_expected = List.last(activities) - activities = ActivityPub.fetch_public_activities + activities = ActivityPub.fetch_public_activities() last = List.last(activities) assert length(activities) == 20 @@ -232,7 +240,12 @@ test "adds an announce activity to the db" do {:ok, announce_activity, object} = ActivityPub.announce(user, object) assert object.data["announcement_count"] == 1 assert object.data["announcements"] == [user.ap_id] - assert announce_activity.data["to"] == [User.ap_followers(user), note_activity.data["actor"]] + + assert announce_activity.data["to"] == [ + User.ap_followers(user), + note_activity.data["actor"] + ] + assert announce_activity.data["object"] == object.data["id"] assert announce_activity.data["actor"] == user.ap_id assert announce_activity.data["context"] == object.data["context"] @@ -241,7 +254,11 @@ test "adds an announce activity to the db" do describe "uploading files" do test "copies the file to the configured folder" do - file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } {:ok, %Object{} = object} = ActivityPub.upload(file) assert object.data["name"] == "an_image.jpg" @@ -268,11 +285,14 @@ test "fetches the latest Follow activity" do describe "fetching an object" do test "it fetches an object" do - {:ok, object} = ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + {:ok, object} = + ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"]) assert activity.data["id"] - {:ok, object_again} = ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") + {:ok, object_again} = + ActivityPub.fetch_object_from_id("http://mastodon.example.org/@admin/99541947525187367") assert [attachment] = object.data["attachment"] assert is_list(attachment["url"]) @@ -285,7 +305,8 @@ test "it works with objects only available via Ostatus" do assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"]) assert activity.data["id"] - {:ok, object_again} = ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873") + {:ok, object_again} = + ActivityPub.fetch_object_from_id("https://shitposter.club/notice/2827873") assert object == object_again end @@ -344,7 +365,14 @@ test "it creates an update activity with the new user data" do user = insert(:user) {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) - {:ok, update} = ActivityPub.update(%{actor: user_data["id"], to: [user.follower_address], cc: [], object: user_data}) + + {:ok, update} = + ActivityPub.update(%{ + actor: user_data["id"], + to: [user.follower_address], + cc: [], + object: user_data + }) assert update.data["actor"] == user.ap_id assert update.data["to"] == [user.follower_address] diff --git a/test/web/activity_pub/transmogrifier_test.exs b/test/web/activity_pub/transmogrifier_test.exs index 375e74ad9..060ebe9f1 100644 --- a/test/web/activity_pub/transmogrifier_test.exs +++ b/test/web/activity_pub/transmogrifier_test.exs @@ -16,9 +16,10 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do test "it ignores an incoming notice if we already have it" do activity = insert(:note_activity) - data = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode! - |> Map.put("object", activity.data["object"]) + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) @@ -26,51 +27,72 @@ test "it ignores an incoming notice if we already have it" do end test "it fetches replied-to activities if we don't have them" do - data = File.read!("test/fixtures/mastodon-post-activity.json") - |> Poison.decode! + data = + File.read!("test/fixtures/mastodon-post-activity.json") + |> Poison.decode!() - object = data["object"] - |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") + object = + data["object"] + |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") - data = data - |> Map.put("object", object) + data = + data + |> Map.put("object", object) {:ok, returned_activity} = Transmogrifier.handle_incoming(data) - assert activity = Activity.get_create_activity_by_object_ap_id("tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment") - assert returned_activity.data["object"]["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" + assert activity = + Activity.get_create_activity_by_object_ap_id( + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + ) + + assert returned_activity.data["object"]["inReplyToAtomUri"] == + "https://shitposter.club/notice/2827873" + assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id end test "it works for incoming notices" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode! + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - assert data["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity" - assert data["context"] == "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99512778738411822/activity" + + assert data["context"] == + "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + assert data["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + assert data["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ] + assert data["actor"] == "http://mastodon.example.org/users/admin" object = data["object"] assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822" assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"] + assert object["cc"] == [ - "http://mastodon.example.org/users/admin/followers", - "http://localtesting.pleroma.lol/users/lain" - ] + "http://mastodon.example.org/users/admin/followers", + "http://localtesting.pleroma.lol/users/lain" + ] + assert object["actor"] == "http://mastodon.example.org/users/admin" assert object["attributedTo"] == "http://mastodon.example.org/users/admin" - assert object["context"] == "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + + assert object["context"] == + "tag:mastodon.example.org,2018-02-12:objectId=20:objectType=Conversation" + assert object["sensitive"] == true end test "it works for incoming notices with hashtags" do - data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode! + data = File.read!("test/fixtures/mastodon-post-activity-hashtag.json") |> Poison.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) assert Enum.at(data["object"]["tag"], 2) == "moo" @@ -78,8 +100,10 @@ test "it works for incoming notices with hashtags" do test "it works for incoming follow requests" do user = insert(:user) - data = File.read!("test/fixtures/mastodon-follow-activity.json") |> Poison.decode! - |> Map.put("object", user.ap_id) + + data = + File.read!("test/fixtures/mastodon-follow-activity.json") |> Poison.decode!() + |> Map.put("object", user.ap_id) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) @@ -93,8 +117,9 @@ test "it works for incoming likes" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) - data = File.read!("test/fixtures/mastodon-like.json") |> Poison.decode! - |> Map.put("object", activity.data["object"]["id"]) + data = + File.read!("test/fixtures/mastodon-like.json") |> Poison.decode!() + |> Map.put("object", activity.data["object"]["id"]) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) @@ -105,14 +130,18 @@ test "it works for incoming likes" do end test "it works for incoming announces" do - data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode! + data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["type"] == "Announce" - assert data["id"] == "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" - assert data["object"] == "http://mastodon.example.org/users/admin/statuses/99541947525187367" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["object"] == + "http://mastodon.example.org/users/admin/statuses/99541947525187367" assert Activity.get_create_activity_by_object_ap_id(data["object"]) end @@ -121,53 +150,77 @@ test "it works for incoming announces with an existing activity" do user = insert(:user) {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) - data = File.read!("test/fixtures/mastodon-announce.json") - |> Poison.decode! - |> Map.put("object", activity.data["object"]["id"]) + data = + File.read!("test/fixtures/mastodon-announce.json") + |> Poison.decode!() + |> Map.put("object", activity.data["object"]["id"]) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["type"] == "Announce" - assert data["id"] == "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + + assert data["id"] == + "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity" + assert data["object"] == activity.data["object"]["id"] assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id end test "it works for incoming update activities" do - data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode! + data = File.read!("test/fixtures/mastodon-post-activity.json") |> Poison.decode!() {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) - update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode! - object = update_data["object"] - |> Map.put("actor", data["actor"]) - |> Map.put("id", data["actor"]) + update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!() - update_data = update_data - |> Map.put("actor", data["actor"]) - |> Map.put("object", object) + object = + update_data["object"] + |> Map.put("actor", data["actor"]) + |> Map.put("id", data["actor"]) + + update_data = + update_data + |> Map.put("actor", data["actor"]) + |> Map.put("object", object) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(update_data) user = User.get_cached_by_ap_id(data["actor"]) assert user.name == "gargle" - assert user.avatar["url"] == [%{"href" => "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"}] - assert user.info["banner"]["url"] == [%{"href" => "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"}] + + assert user.avatar["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" + } + ] + + assert user.info["banner"]["url"] == [ + %{ + "href" => + "https://cd.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" + } + ] + assert user.bio == "

Some bio

" end test "it works for incoming deletes" do activity = insert(:note_activity) - data = File.read!("test/fixtures/mastodon-delete.json") - |> Poison.decode! - object = data["object"] - |> Map.put("id", activity.data["object"]["id"]) + data = + File.read!("test/fixtures/mastodon-delete.json") + |> Poison.decode!() - data = data - |> Map.put("object", object) - |> Map.put("actor", activity.data["actor"]) + object = + data["object"] + |> Map.put("id", activity.data["object"]["id"]) + + data = + data + |> Map.put("object", object) + |> Map.put("actor", activity.data["actor"]) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) @@ -180,7 +233,8 @@ test "it turns mentions into tags" do user = insert(:user) other_user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"}) + {:ok, activity} = + CommonAPI.post(user, %{"status" => "hey, @#{other_user.nickname}, how are ya? #2hu"}) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) object = modified["object"] @@ -192,7 +246,7 @@ test "it turns mentions into tags" do } expected_tag = %{ - "href" => Pleroma.Web.Endpoint.url <> "/tags/2hu", + "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu", "type" => "Hashtag", "name" => "#2hu" } @@ -247,7 +301,9 @@ test "it translates ostatus reply_to IDs to external URLs" do user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id}) + {:ok, activity} = + CommonAPI.post(user, %{"status" => "HI!", "in_reply_to_status_id" => referred_activity.id}) + {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data) assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29" @@ -256,7 +312,14 @@ test "it translates ostatus reply_to IDs to external URLs" do describe "user upgrade" do test "it upgrades a user to activitypub" do - user = insert(:user, %{nickname: "rye@niu.moe", local: false, ap_id: "https://niu.moe/users/rye", follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"})}) + user = + insert(:user, %{ + nickname: "rye@niu.moe", + local: false, + ap_id: "https://niu.moe/users/rye", + follower_address: User.ap_followers(%User{nickname: "rye@niu.moe"}) + }) + user_two = insert(:user, %{following: [user.follower_address]}) {:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) @@ -279,8 +342,25 @@ test "it upgrades a user to activitypub" do activity = Repo.get(Activity, activity.id) assert user.follower_address in activity.recipients - assert %{"url" => [%{"href" => "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg"}]} = user.avatar - assert %{"url" => [%{"href" => "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png"}]} = user.info["banner"] + + assert %{ + "url" => [ + %{ + "href" => + "https://cdn.niu.moe/accounts/avatars/000/033/323/original/fd7f8ae0b3ffedc9.jpeg" + } + ] + } = user.avatar + + assert %{ + "url" => [ + %{ + "href" => + "https://cdn.niu.moe/accounts/headers/000/033/323/original/850b3448fa5fd477.png" + } + ] + } = user.info["banner"] + refute "..." in activity.recipients unrelated_activity = Repo.get(Activity, unrelated_activity.id) diff --git a/test/web/common_api/common_api_utils_test.exs b/test/web/common_api/common_api_utils_test.exs index f6a7da9ed..689bdd61e 100644 --- a/test/web/common_api/common_api_utils_test.exs +++ b/test/web/common_api/common_api_utils_test.exs @@ -3,7 +3,8 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do use Pleroma.DataCase test "it adds attachment links to a given text and attachment set" do - name = "Sakura%20Mana%20%E2%80%93%20Turned%20on%20by%20a%20Senior%20OL%20with%20a%20Temptating%20Tight%20Skirt-s%20Full%20Hipline%20and%20Panty%20Shot-%20Beautiful%20Thick%20Thighs-%20and%20Erotic%20Ass-%20-2015-%20--%20Oppaitime%208-28-2017%206-50-33%20PM.png" + name = + "Sakura%20Mana%20%E2%80%93%20Turned%20on%20by%20a%20Senior%20OL%20with%20a%20Temptating%20Tight%20Skirt-s%20Full%20Hipline%20and%20Panty%20Shot-%20Beautiful%20Thick%20Thighs-%20and%20Erotic%20Ass-%20-2015-%20--%20Oppaitime%208-28-2017%206-50-33%20PM.png" attachment = %{ "url" => [%{"href" => name}] @@ -11,6 +12,7 @@ test "it adds attachment links to a given text and attachment set" do res = Utils.add_attachments("", [attachment]) - assert res == "
Sakura Mana – Turned on by a Se…" + assert res == + "
Sakura Mana – Turned on by a Se…" end end diff --git a/test/web/mastodon_api/account_view_test.exs b/test/web/mastodon_api/account_view_test.exs index 5eefa61e1..597690bf7 100644 --- a/test/web/mastodon_api/account_view_test.exs +++ b/test/web/mastodon_api/account_view_test.exs @@ -5,7 +5,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do alias Pleroma.User test "Represent a user account" do - user = insert(:user, %{info: %{"note_count" => 5, "follower_count" => 3}, nickname: "shp@shitposter.club", inserted_at: ~N[2017-08-15 15:47:06.597036]}) + user = + insert(:user, %{ + info: %{"note_count" => 5, "follower_count" => 3}, + nickname: "shp@shitposter.club", + inserted_at: ~N[2017-08-15 15:47:06.597036] + }) expected = %{ id: to_string(user.id), diff --git a/test/web/mastodon_api/mastodon_api_controller_test.exs b/test/web/mastodon_api/mastodon_api_controller_test.exs index a655fa74b..2c9cdd194 100644 --- a/test/web/mastodon_api/mastodon_api_controller_test.exs +++ b/test/web/mastodon_api/mastodon_api_controller_test.exs @@ -14,17 +14,19 @@ test "the home timeline", %{conn: conn} do {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"}) - conn = conn - |> assign(:user, user) - |> get("/api/v1/timelines/home") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/timelines/home") assert length(json_response(conn, 200)) == 0 {:ok, user} = User.follow(user, following) - conn = build_conn() - |> assign(:user, user) - |> get("/api/v1/timelines/home") + conn = + build_conn() + |> assign(:user, user) + |> get("/api/v1/timelines/home") assert [%{"content" => "test"}] = json_response(conn, 200) end @@ -32,44 +34,57 @@ test "the home timeline", %{conn: conn} do test "the public timeline", %{conn: conn} do following = insert(:user) - capture_log fn -> + capture_log(fn -> {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"}) - {:ok, [_activity]} = OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - conn = conn - |> get("/api/v1/timelines/public", %{"local" => "False"}) + {:ok, [_activity]} = + OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + + conn = + conn + |> get("/api/v1/timelines/public", %{"local" => "False"}) assert length(json_response(conn, 200)) == 2 - conn = build_conn() - |> get("/api/v1/timelines/public", %{"local" => "True"}) + conn = + build_conn() + |> get("/api/v1/timelines/public", %{"local" => "True"}) assert [%{"content" => "test"}] = json_response(conn, 200) - conn = build_conn() - |> get("/api/v1/timelines/public", %{"local" => "1"}) + conn = + build_conn() + |> get("/api/v1/timelines/public", %{"local" => "1"}) assert [%{"content" => "test"}] = json_response(conn, 200) - end + end) end test "posting a status", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{"status" => "cofe", "spoiler_text" => "2hu", "sensitive" => "false"}) + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{ + "status" => "cofe", + "spoiler_text" => "2hu", + "sensitive" => "false" + }) + + assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = + json_response(conn, 200) - assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = json_response(conn, 200) assert Repo.get(Activity, id) end test "posting a sensitive status", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) assert %{"content" => "cofe", "id" => id, "sensitive" => true} = json_response(conn, 200) assert Repo.get(Activity, id) @@ -80,9 +95,10 @@ test "replying to a status", %{conn: conn} do {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"}) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) assert %{"content" => "xD", "id" => id} = json_response(conn, 200) @@ -95,9 +111,10 @@ test "replying to a status", %{conn: conn} do test "verify_credentials", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> get("/api/v1/accounts/verify_credentials") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/verify_credentials") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(user.id) @@ -106,8 +123,9 @@ test "verify_credentials", %{conn: conn} do test "get a status", %{conn: conn} do activity = insert(:note_activity) - conn = conn - |> get("/api/v1/statuses/#{activity.id}") + conn = + conn + |> get("/api/v1/statuses/#{activity.id}") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(activity.id) @@ -118,9 +136,10 @@ test "when you created it", %{conn: conn} do activity = insert(:note_activity) author = User.get_by_ap_id(activity.data["actor"]) - conn = conn - |> assign(:user, author) - |> delete("/api/v1/statuses/#{activity.id}") + conn = + conn + |> assign(:user, author) + |> delete("/api/v1/statuses/#{activity.id}") assert %{} = json_response(conn, 200) @@ -131,9 +150,10 @@ test "when you didn't create it", %{conn: conn} do activity = insert(:note_activity) user = insert(:user) - conn = conn - |> assign(:user, user) - |> delete("/api/v1/statuses/#{activity.id}") + conn = + conn + |> assign(:user, user) + |> delete("/api/v1/statuses/#{activity.id}") assert %{"error" => _} = json_response(conn, 403) @@ -146,14 +166,19 @@ test "list of notifications", %{conn: conn} do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, [_notification]} = Notification.create_notifications(activity) - conn = conn - |> assign(:user, user) - |> get("/api/v1/notifications") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/notifications") + + expected_response = + "hi @#{user.nickname}" - expected_response = "hi @#{user.nickname}" assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) assert response == expected_response end @@ -162,14 +187,19 @@ test "getting a single notification", %{conn: conn} do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) - conn = conn - |> assign(:user, user) - |> get("/api/v1/notifications/#{notification.id}") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/notifications/#{notification.id}") + + expected_response = + "hi @#{user.nickname}" - expected_response = "hi @#{user.nickname}" assert %{"status" => %{"content" => response}} = json_response(conn, 200) assert response == expected_response end @@ -178,12 +208,15 @@ test "dismissing a single notification", %{conn: conn} do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, [notification]} = Notification.create_notifications(activity) - conn = conn - |> assign(:user, user) - |> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) + conn = + conn + |> assign(:user, user) + |> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) assert %{} = json_response(conn, 200) end @@ -192,18 +225,22 @@ test "clearing all notifications", %{conn: conn} do user = insert(:user) other_user = insert(:user) - {:ok, activity} = TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, activity} = + TwitterAPI.create_status(other_user, %{"status" => "hi @#{user.nickname}"}) + {:ok, [_notification]} = Notification.create_notifications(activity) - conn = conn - |> assign(:user, user) - |> post("/api/v1/notifications/clear") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/notifications/clear") assert %{} = json_response(conn, 200) - conn = build_conn() - |> assign(:user, user) - |> get("/api/v1/notifications") + conn = + build_conn() + |> assign(:user, user) + |> get("/api/v1/notifications") assert all = json_response(conn, 200) assert all == [] @@ -215,11 +252,14 @@ test "reblogs and returns the reblogged status", %{conn: conn} do activity = insert(:note_activity) user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/reblog") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/reblog") + + assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = + json_response(conn, 200) - assert %{"reblog" => %{"id" => id, "reblogged" => true, "reblogs_count" => 1}} = json_response(conn, 200) assert to_string(activity.id) == id end end @@ -229,11 +269,14 @@ test "favs a status and returns it", %{conn: conn} do activity = insert(:note_activity) user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/favourite") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/favourite") + + assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = + json_response(conn, 200) - assert %{"id" => id, "favourites_count" => 1, "favourited" => true} = json_response(conn, 200) assert to_string(activity.id) == id end end @@ -245,11 +288,14 @@ test "unfavorites a status and returns it", %{conn: conn} do {:ok, _, _} = CommonAPI.favorite(activity.id, user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/statuses/#{activity.id}/unfavourite") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/statuses/#{activity.id}/unfavourite") + + assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = + json_response(conn, 200) - assert %{"id" => id, "favourites_count" => 0, "favourited" => false} = json_response(conn, 200) assert to_string(activity.id) == id end end @@ -261,8 +307,9 @@ test "gets a users statuses", %{conn: conn} do user = User.get_by_ap_id(note_two.data["actor"]) - conn = conn - |> get("/api/v1/accounts/#{user.id}/statuses") + conn = + conn + |> get("/api/v1/accounts/#{user.id}/statuses") assert [%{"id" => id}] = json_response(conn, 200) @@ -273,20 +320,29 @@ test "gets an users media", %{conn: conn} do note = insert(:note_activity) user = User.get_by_ap_id(note.data["actor"]) - file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} - media = TwitterAPI.upload(file, "json") - |> Poison.decode! + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } - {:ok, image_post} = TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]}) + media = + TwitterAPI.upload(file, "json") + |> Poison.decode!() - conn = conn - |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) + {:ok, image_post} = + TwitterAPI.create_status(user, %{"status" => "cofe", "media_ids" => [media["media_id"]]}) + + conn = + conn + |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) - conn = build_conn() - |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) + conn = + build_conn() + |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(image_post.id) @@ -299,9 +355,10 @@ test "returns the relationships for the current user", %{conn: conn} do other_user = insert(:user) {:ok, user} = User.follow(user, other_user) - conn = conn - |> assign(:user, user) - |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) + conn = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) assert [relationship] = json_response(conn, 200) @@ -312,26 +369,33 @@ test "returns the relationships for the current user", %{conn: conn} do test "account fetching", %{conn: conn} do user = insert(:user) - conn = conn - |> get("/api/v1/accounts/#{user.id}") + conn = + conn + |> get("/api/v1/accounts/#{user.id}") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(user.id) - conn = build_conn() - |> get("/api/v1/accounts/-1") + conn = + build_conn() + |> get("/api/v1/accounts/-1") assert %{"error" => "Can't find user"} = json_response(conn, 404) end test "media upload", %{conn: conn} do - file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/media", %{"file" => file}) + conn = + conn + |> assign(:user, user) + |> post("/api/v1/media", %{"file" => file}) assert media = json_response(conn, 200) @@ -341,16 +405,20 @@ test "media upload", %{conn: conn} do test "hashtag timeline", %{conn: conn} do following = insert(:user) - capture_log fn -> + capture_log(fn -> {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"}) - {:ok, [_activity]} = OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") - conn = conn - |> get("/api/v1/timelines/tag/2hu") + + {:ok, [_activity]} = + OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873") + + conn = + conn + |> get("/api/v1/timelines/tag/2hu") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(activity.id) - end + end) end test "getting followers", %{conn: conn} do @@ -358,8 +426,9 @@ test "getting followers", %{conn: conn} do other_user = insert(:user) {:ok, user} = User.follow(user, other_user) - conn = conn - |> get("/api/v1/accounts/#{other_user.id}/followers") + conn = + conn + |> get("/api/v1/accounts/#{other_user.id}/followers") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(user.id) @@ -370,8 +439,9 @@ test "getting following", %{conn: conn} do other_user = insert(:user) {:ok, user} = User.follow(user, other_user) - conn = conn - |> get("/api/v1/accounts/#{user.id}/following") + conn = + conn + |> get("/api/v1/accounts/#{user.id}/following") assert [%{"id" => id}] = json_response(conn, 200) assert id == to_string(other_user.id) @@ -381,23 +451,28 @@ test "following / unfollowing a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/follow") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/follow") assert %{"id" => _id, "following" => true} = json_response(conn, 200) user = Repo.get(User, user.id) - conn = build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unfollow") + + conn = + build_conn() + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/unfollow") assert %{"id" => _id, "following" => false} = json_response(conn, 200) user = Repo.get(User, user.id) - conn = build_conn() - |> assign(:user, user) - |> post("/api/v1/follows", %{"uri" => other_user.nickname}) + + conn = + build_conn() + |> assign(:user, user) + |> post("/api/v1/follows", %{"uri" => other_user.nickname}) assert %{"id" => id} = json_response(conn, 200) assert id == to_string(other_user.id) @@ -407,16 +482,19 @@ test "blocking / unblocking a user", %{conn: conn} do user = insert(:user) other_user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/block") + conn = + conn + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/block") assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) user = Repo.get(User, user.id) - conn = build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/unblock") + + conn = + build_conn() + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/unblock") assert %{"id" => _id, "blocking" => false} = json_response(conn, 200) end @@ -427,9 +505,10 @@ test "getting a list of blocks", %{conn: conn} do {:ok, user} = User.block(user, other_user) - conn = conn - |> assign(:user, user) - |> get("/api/v1/blocks") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/blocks") other_user_id = to_string(other_user.id) assert [%{"id" => ^other_user_id}] = json_response(conn, 200) @@ -440,10 +519,11 @@ test "unimplemented mute endpoints" do other_user = insert(:user) ["mute", "unmute"] - |> Enum.each(fn(endpoint) -> - conn = build_conn() - |> assign(:user, user) - |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}") + |> Enum.each(fn endpoint -> + conn = + build_conn() + |> assign(:user, user) + |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}") assert %{"id" => id} = json_response(conn, 200) assert id == to_string(other_user.id) @@ -454,10 +534,11 @@ test "unimplemented mutes, follow_requests, blocks, domain blocks" do user = insert(:user) ["blocks", "domain_blocks", "mutes", "follow_requests"] - |> Enum.each(fn(endpoint) -> - conn = build_conn() - |> assign(:user, user) - |> get("/api/v1/#{endpoint}") + |> Enum.each(fn endpoint -> + conn = + build_conn() + |> assign(:user, user) + |> get("/api/v1/#{endpoint}") assert [] = json_response(conn, 200) end) @@ -468,9 +549,10 @@ test "account search", %{conn: conn} do _user_two = insert(:user, %{nickname: "shp@shitposter.club"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) - conn = conn - |> assign(:user, user) - |> get("/api/v1/accounts/search", %{"q" => "2hu"}) + conn = + conn + |> assign(:user, user) + |> get("/api/v1/accounts/search", %{"q" => "2hu"}) assert [account] = json_response(conn, 200) assert account["id"] == to_string(user_three.id) @@ -484,8 +566,9 @@ test "search", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = conn - |> get("/api/v1/search", %{"q" => "2hu"}) + conn = + conn + |> get("/api/v1/search", %{"q" => "2hu"}) assert results = json_response(conn, 200) @@ -499,19 +582,22 @@ test "search", %{conn: conn} do end test "search fetches remote statuses", %{conn: conn} do - capture_log fn -> - conn = conn - |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) + capture_log(fn -> + conn = + conn + |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) + assert results = json_response(conn, 200) [status] = results["statuses"] assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - end + end) end test "search fetches remote accounts", %{conn: conn} do - conn = conn - |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"}) + conn = + conn + |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"}) assert results = json_response(conn, 200) [account] = results["accounts"] @@ -527,9 +613,10 @@ test "returns the favorites of a user", %{conn: conn} do {:ok, _, _} = CommonAPI.favorite(activity.id, user) - conn = conn - |> assign(:user, user) - |> get("/api/v1/favourites") + conn = + conn + |> assign(:user, user) + |> get("/api/v1/favourites") assert [status] = json_response(conn, 200) assert status["id"] == to_string(activity.id) @@ -539,9 +626,10 @@ test "returns the favorites of a user", %{conn: conn} do test "updates the user's bio", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"note" => "I drink #cofe"}) + conn = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"note" => "I drink #cofe"}) assert user = json_response(conn, 200) assert user["note"] == "I drink #cofe" @@ -550,9 +638,10 @@ test "updates the user's bio", %{conn: conn} do test "updates the user's name", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) + conn = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) assert user = json_response(conn, 200) assert user["display_name"] == "markorepairs" @@ -561,11 +650,16 @@ test "updates the user's name", %{conn: conn} do test "updates the user's avatar", %{conn: conn} do user = insert(:user) - new_avatar = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} + new_avatar = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } - conn = conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) + conn = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) assert user = json_response(conn, 200) assert user["avatar"] != "https://placehold.it/48x48" @@ -574,11 +668,16 @@ test "updates the user's avatar", %{conn: conn} do test "updates the user's banner", %{conn: conn} do user = insert(:user) - new_header = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} + new_header = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } - conn = conn - |> assign(:user, user) - |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header}) + conn = + conn + |> assign(:user, user) + |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header}) assert user = json_response(conn, 200) assert user["header"] != "https://placehold.it/700x335" @@ -594,8 +693,9 @@ test "get instance information", %{conn: conn} do Pleroma.Stats.update_stats() - conn = conn - |> get("/api/v1/instance") + conn = + conn + |> get("/api/v1/instance") assert result = json_response(conn, 200) diff --git a/test/web/mastodon_api/status_view_test.exs b/test/web/mastodon_api/status_view_test.exs index 74f5b7133..d9a0a8a95 100644 --- a/test/web/mastodon_api/status_view_test.exs +++ b/test/web/mastodon_api/status_view_test.exs @@ -13,8 +13,9 @@ test "a note activity" do status = StatusView.render("status.json", %{activity: note}) - created_at = (note.data["object"]["published"] || "") - |> String.replace(~r/\.\d+Z/, ".000Z") + created_at = + (note.data["object"]["published"] || "") + |> String.replace(~r/\.\d+Z/, ".000Z") expected = %{ id: to_string(note.id), @@ -57,7 +58,9 @@ test "a note activity" do test "a reply" do note = insert(:note_activity) user = insert(:user) - {:ok, activity} = CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id}) + + {:ok, activity} = + CommonAPI.post(user, %{"status" => "he", "in_reply_to_status_id" => note.id}) status = StatusView.render("status.json", %{activity: activity}) diff --git a/test/web/oauth/authorization_test.exs b/test/web/oauth/authorization_test.exs index 52441fa7d..4a9e2a3ac 100644 --- a/test/web/oauth/authorization_test.exs +++ b/test/web/oauth/authorization_test.exs @@ -4,7 +4,15 @@ defmodule Pleroma.Web.OAuth.AuthorizationTest do import Pleroma.Factory test "create an authorization token for a valid app" do - {:ok, app} = Repo.insert(App.register_changeset(%App{}, %{client_name: "client", scopes: "scope", redirect_uris: "url"})) + {:ok, app} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client", + scopes: "scope", + redirect_uris: "url" + }) + ) + user = insert(:user) {:ok, auth} = Authorization.create_authorization(app, user) @@ -16,7 +24,15 @@ test "create an authorization token for a valid app" do end test "use up a token" do - {:ok, app} = Repo.insert(App.register_changeset(%App{}, %{client_name: "client", scopes: "scope", redirect_uris: "url"})) + {:ok, app} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client", + scopes: "scope", + redirect_uris: "url" + }) + ) + user = insert(:user) {:ok, auth} = Authorization.create_authorization(app, user) @@ -30,7 +46,7 @@ test "use up a token" do expired_auth = %Authorization{ user_id: user.id, app_id: app.id, - valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, -10), + valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10), token: "mytoken", used: false } diff --git a/test/web/oauth/token_test.exs b/test/web/oauth/token_test.exs index 3bd763989..58448949c 100644 --- a/test/web/oauth/token_test.exs +++ b/test/web/oauth/token_test.exs @@ -6,7 +6,15 @@ defmodule Pleroma.Web.OAuth.TokenTest do import Pleroma.Factory test "exchanges a auth token for an access token" do - {:ok, app} = Repo.insert(App.register_changeset(%App{}, %{client_name: "client", scopes: "scope", redirect_uris: "url"})) + {:ok, app} = + Repo.insert( + App.register_changeset(%App{}, %{ + client_name: "client", + scopes: "scope", + redirect_uris: "url" + }) + ) + user = insert(:user) {:ok, auth} = Authorization.create_authorization(app, user) diff --git a/test/web/ostatus/activity_representer_test.exs b/test/web/ostatus/activity_representer_test.exs index 3ee9034a7..8bf3bc775 100644 --- a/test/web/ostatus/activity_representer_test.exs +++ b/test/web/ostatus/activity_representer_test.exs @@ -16,9 +16,12 @@ test "an external note activity" do tuple = ActivityRepresenter.to_simple_form(activity, user) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() - assert String.contains?(res, ~s{}) + assert String.contains?( + res, + ~s{} + ) end test "a note activity" do @@ -46,7 +49,7 @@ test "a note activity" do tuple = ActivityRepresenter.to_simple_form(note_activity, user) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() assert clean(res) == clean(expected) end @@ -61,7 +64,10 @@ test "a reply note" do answer = %{answer | data: data} note_object = Object.get_by_ap_id(note.data["object"]["id"]) - Repo.update!(Object.change(note_object, %{ data: Map.put(note_object.data, "external_url", "someurl") })) + + Repo.update!( + Object.change(note_object, %{data: Map.put(note_object.data, "external_url", "someurl")}) + ) user = User.get_cached_by_ap_id(answer.data["actor"]) @@ -86,7 +92,7 @@ test "a reply note" do tuple = ActivityRepresenter.to_simple_form(answer, user) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() assert clean(res) == clean(expected) end @@ -102,9 +108,11 @@ test "an announce activity" do note_user = User.get_cached_by_ap_id(note.data["actor"]) note = Repo.get(Activity, note.id) - note_xml = ActivityRepresenter.to_simple_form(note, note_user, true) - |> :xmerl.export_simple_content(:xmerl_xml) - |> to_string + + note_xml = + ActivityRepresenter.to_simple_form(note, note_user, true) + |> :xmerl.export_simple_content(:xmerl_xml) + |> to_string expected = """ http://activitystrea.ms/schema/1.0/activity @@ -120,13 +128,16 @@ test "an announce activity" do #{note_xml} - + """ - announce_xml = ActivityRepresenter.to_simple_form(announce, user) - |> :xmerl.export_simple_content(:xmerl_xml) - |> to_string + announce_xml = + ActivityRepresenter.to_simple_form(announce, user) + |> :xmerl.export_simple_content(:xmerl_xml) + |> to_string assert clean(expected) == clean(announce_xml) end @@ -139,7 +150,7 @@ test "a like activity" do tuple = ActivityRepresenter.to_simple_form(like, user) refute is_nil(tuple) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() expected = """ http://activitystrea.ms/schema/1.0/favorite @@ -156,7 +167,9 @@ test "a like activity" do - + """ @@ -166,18 +179,20 @@ test "a like activity" do test "a follow activity" do follower = insert(:user) followed = insert(:user) - {:ok, activity} = ActivityPub.insert(%{ - "type" => "Follow", - "actor" => follower.ap_id, - "object" => followed.ap_id, - "to" => [followed.ap_id] - }) + + {:ok, activity} = + ActivityPub.insert(%{ + "type" => "Follow", + "actor" => follower.ap_id, + "object" => followed.ap_id, + "to" => [followed.ap_id] + }) tuple = ActivityRepresenter.to_simple_form(activity, follower) refute is_nil(tuple) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() expected = """ http://activitystrea.ms/schema/1.0/activity @@ -193,7 +208,9 @@ test "a follow activity" do #{activity.data["object"]} - + """ assert clean(res) == clean(expected) @@ -209,7 +226,7 @@ test "an unfollow activity" do refute is_nil(tuple) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() expected = """ http://activitystrea.ms/schema/1.0/activity @@ -225,7 +242,9 @@ test "an unfollow activity" do #{followed.ap_id} - + """ assert clean(res) == clean(expected) @@ -233,13 +252,22 @@ test "an unfollow activity" do test "a delete" do user = insert(:user) - activity = %Activity{data: %{ "id" => "ap_id", "type" => "Delete", "actor" => user.ap_id, "object" => "some_id", "published" => "2017-06-18T12:00:18+00:00" }} + + activity = %Activity{ + data: %{ + "id" => "ap_id", + "type" => "Delete", + "actor" => user.ap_id, + "object" => "some_id", + "published" => "2017-06-18T12:00:18+00:00" + } + } tuple = ActivityRepresenter.to_simple_form(activity, nil) refute is_nil(tuple) - res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary + res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> IO.iodata_to_binary() expected = """ http://activitystrea.ms/schema/1.0/activity diff --git a/test/web/ostatus/feed_representer_test.exs b/test/web/ostatus/feed_representer_test.exs index 5b8eabcb9..bf3feb14e 100644 --- a/test/web/ostatus/feed_representer_test.exs +++ b/test/web/ostatus/feed_representer_test.exs @@ -11,15 +11,19 @@ test "returns a feed of the last 20 items of the user" do tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user]) - most_recent_update = note_activity.updated_at - |> NaiveDateTime.to_iso8601 + most_recent_update = + note_activity.updated_at + |> NaiveDateTime.to_iso8601() res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string - user_xml = UserRepresenter.to_simple_form(user) - |> :xmerl.export_simple_content(:xmerl_xml) - entry_xml = ActivityRepresenter.to_simple_form(note_activity, user) - |> :xmerl.export_simple_content(:xmerl_xml) + user_xml = + UserRepresenter.to_simple_form(user) + |> :xmerl.export_simple_content(:xmerl_xml) + + entry_xml = + ActivityRepresenter.to_simple_form(note_activity, user) + |> :xmerl.export_simple_content(:xmerl_xml) expected = """ @@ -39,6 +43,7 @@ test "returns a feed of the last 20 items of the user" do """ + assert clean(res) == clean(expected) end diff --git a/test/web/ostatus/incoming_documents/delete_handling_test.exs b/test/web/ostatus/incoming_documents/delete_handling_test.exs index 43d04dea2..1e041e5b0 100644 --- a/test/web/ostatus/incoming_documents/delete_handling_test.exs +++ b/test/web/ostatus/incoming_documents/delete_handling_test.exs @@ -14,8 +14,13 @@ test "it removes the mentioned activity" do {:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object) - incoming = File.read!("test/fixtures/delete.xml") - |> String.replace("tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status", note.data["object"]["id"]) + incoming = + File.read!("test/fixtures/delete.xml") + |> String.replace( + "tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status", + note.data["object"]["id"] + ) + {:ok, [delete]} = OStatus.handle_incoming(incoming) refute Repo.get(Activity, note.id) diff --git a/test/web/ostatus/ostatus_controller_test.exs b/test/web/ostatus/ostatus_controller_test.exs index 5b8eb8156..ba8855093 100644 --- a/test/web/ostatus/ostatus_controller_test.exs +++ b/test/web/ostatus/ostatus_controller_test.exs @@ -7,9 +7,11 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do test "decodes a salmon", %{conn: conn} do user = insert(:user) salmon = File.read!("test/fixtures/salmon.xml") - conn = conn - |> put_req_header("content-type", "application/atom+xml") - |> post("/users/#{user.nickname}/salmon", salmon) + + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> post("/users/#{user.nickname}/salmon", salmon) assert response(conn, 200) end @@ -17,21 +19,30 @@ test "decodes a salmon", %{conn: conn} do test "decodes a salmon with a changed magic key", %{conn: conn} do user = insert(:user) salmon = File.read!("test/fixtures/salmon.xml") - conn = conn - |> put_req_header("content-type", "application/atom+xml") - |> post("/users/#{user.nickname}/salmon", salmon) + + conn = + conn + |> put_req_header("content-type", "application/atom+xml") + |> post("/users/#{user.nickname}/salmon", salmon) assert response(conn, 200) # Set a wrong magic-key for a user so it has to refetch salmon_user = User.get_by_ap_id("http://gs.example.org:4040/index.php/user/1") - info = salmon_user.info - |> Map.put("magic_key", "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB") # Wrong key + # Wrong key + info = + salmon_user.info + |> Map.put( + "magic_key", + "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB" + ) + Repo.update(User.info_changeset(salmon_user, %{info: info})) - conn = build_conn() - |> put_req_header("content-type", "application/atom+xml") - |> post("/users/#{user.nickname}/salmon", salmon) + conn = + build_conn() + |> put_req_header("content-type", "application/atom+xml") + |> post("/users/#{user.nickname}/salmon", salmon) assert response(conn, 200) end @@ -40,8 +51,9 @@ test "gets a feed", %{conn: conn} do note_activity = insert(:note_activity) user = User.get_cached_by_ap_id(note_activity.data["actor"]) - conn = conn - |> get("/users/#{user.nickname}/feed.atom") + conn = + conn + |> get("/users/#{user.nickname}/feed.atom") assert response(conn, 200) =~ note_activity.data["object"]["content"] end @@ -49,27 +61,30 @@ test "gets a feed", %{conn: conn} do test "gets an object", %{conn: conn} do note_activity = insert(:note_activity) user = User.get_by_ap_id(note_activity.data["actor"]) - [_, uuid] = hd Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"]) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["object"]["id"])) url = "/objects/#{uuid}" - conn = conn - |> get(url) + conn = + conn + |> get(url) - expected = ActivityRepresenter.to_simple_form(note_activity, user, true) - |> ActivityRepresenter.wrap_with_entry - |> :xmerl.export_simple(:xmerl_xml) - |> to_string + expected = + ActivityRepresenter.to_simple_form(note_activity, user, true) + |> ActivityRepresenter.wrap_with_entry() + |> :xmerl.export_simple(:xmerl_xml) + |> to_string assert response(conn, 200) == expected end test "gets an activity", %{conn: conn} do note_activity = insert(:note_activity) - [_, uuid] = hd Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"]) + [_, uuid] = hd(Regex.scan(~r/.+\/([\w-]+)$/, note_activity.data["id"])) url = "/activities/#{uuid}" - conn = conn - |> get(url) + conn = + conn + |> get(url) assert response(conn, 200) end @@ -78,8 +93,9 @@ test "gets a notice", %{conn: conn} do note_activity = insert(:note_activity) url = "/notice/#{note_activity.id}" - conn = conn - |> get(url) + conn = + conn + |> get(url) assert response(conn, 200) end diff --git a/test/web/ostatus/ostatus_test.exs b/test/web/ostatus/ostatus_test.exs index 7f67b9ec0..3e7cf0155 100644 --- a/test/web/ostatus/ostatus_test.exs +++ b/test/web/ostatus/ostatus_test.exs @@ -20,12 +20,18 @@ test "handle incoming note - GS, Salmon" do assert user.info["note_count"] == 1 assert activity.data["type"] == "Create" assert activity.data["object"]["type"] == "Note" - assert activity.data["object"]["id"] == "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note" + + assert activity.data["object"]["id"] == + "tag:gs.example.org:4040,2017-04-23:noticeId=29:objectType=note" + assert activity.data["published"] == "2017-04-23T14:51:03+00:00" assert activity.data["object"]["published"] == "2017-04-23T14:51:03+00:00" - assert activity.data["context"] == "tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b" + + assert activity.data["context"] == + "tag:gs.example.org:4040,2017-04-23:objectType=thread:nonce=f09e22f58abd5c7b" + assert "http://pleroma.example.org:4000/users/lain3" in activity.data["to"] - assert activity.data["object"]["emoji"] == %{ "marko" => "marko.png", "reimu" => "reimu.png" } + assert activity.data["object"]["emoji"] == %{"marko" => "marko.png", "reimu" => "reimu.png"} assert activity.local == false end @@ -65,10 +71,12 @@ test "handle incoming notes with tags" do test "handle incoming notes - Mastodon, salmon, reply" do # It uses the context of the replied to object Repo.insert!(%Object{ - data: %{ - "id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4", - "context" => "2hu" - }}) + data: %{ + "id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4", + "context" => "2hu" + } + }) + incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml") {:ok, [activity]} = OStatus.handle_incoming(incoming) @@ -113,8 +121,13 @@ test "handle incoming notes - GS, subscription, reply" do assert activity.data["type"] == "Create" assert activity.data["object"]["type"] == "Note" assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211" - assert activity.data["object"]["content"] == "@shpbot why not indeed." - assert activity.data["object"]["inReplyTo"] == "tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note" + + assert activity.data["object"]["content"] == + "@shpbot why not indeed." + + assert activity.data["object"]["inReplyTo"] == + "tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note" + assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"] end @@ -141,9 +154,11 @@ test "handle incoming retweets - GS, subscription - local message" do incoming = File.read!("test/fixtures/share-gs-local.xml") note_activity = insert(:note_activity) user = User.get_cached_by_ap_id(note_activity.data["actor"]) - incoming = incoming - |> String.replace("LOCAL_ID", note_activity.data["object"]["id"]) - |> String.replace("LOCAL_USER", user.ap_id) + + incoming = + incoming + |> String.replace("LOCAL_ID", note_activity.data["object"]["id"]) + |> String.replace("LOCAL_USER", user.ap_id) {:ok, [[activity, retweeted_activity]]} = OStatus.handle_incoming(incoming) @@ -168,7 +183,9 @@ test "handle incoming retweets - Mastodon, salmon" do assert activity.data["type"] == "Announce" assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda" assert activity.data["object"] == retweeted_activity.data["object"]["id"] - assert activity.data["id"] == "tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status" + + assert activity.data["id"] == + "tag:mastodon.social,2017-05-03:objectId=4934452:objectType=Status" refute activity.local assert retweeted_activity.data["type"] == "Create" @@ -178,35 +195,42 @@ test "handle incoming retweets - Mastodon, salmon" do end test "handle incoming favorites - GS, websub" do - capture_log fn -> + capture_log(fn -> incoming = File.read!("test/fixtures/favorite.xml") {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming) assert activity.data["type"] == "Like" assert activity.data["actor"] == "https://social.heldscal.la/user/23211" assert activity.data["object"] == favorited_activity.data["object"]["id"] - assert activity.data["id"] == "tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00" + + assert activity.data["id"] == + "tag:social.heldscal.la,2017-05-05:fave:23211:comment:2061643:2017-05-05T09:12:50+00:00" refute activity.local assert favorited_activity.data["type"] == "Create" assert favorited_activity.data["actor"] == "https://shitposter.club/user/1" - assert favorited_activity.data["object"]["id"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + + assert favorited_activity.data["object"]["id"] == + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + refute favorited_activity.local - end + end) end test "handle conversation references" do incoming = File.read!("test/fixtures/mastodon_conversation.xml") {:ok, [activity]} = OStatus.handle_incoming(incoming) - assert activity.data["context"] == "tag:mastodon.social,2017-08-28:objectId=7876885:objectType=Conversation" + assert activity.data["context"] == + "tag:mastodon.social,2017-08-28:objectId=7876885:objectType=Conversation" end test "handle incoming favorites with locally available object - GS, websub" do note_activity = insert(:note_activity) - incoming = File.read!("test/fixtures/favorite_with_local_note.xml") - |> String.replace("localid", note_activity.data["object"]["id"]) + incoming = + File.read!("test/fixtures/favorite_with_local_note.xml") + |> String.replace("localid", note_activity.data["object"]["id"]) {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming) @@ -224,9 +248,15 @@ test "handle incoming replies" do assert activity.data["type"] == "Create" assert activity.data["object"]["type"] == "Note" - assert activity.data["object"]["inReplyTo"] == "http://pleroma.example.org:4000/objects/55bce8fc-b423-46b1-af71-3759ab4670bc" + + assert activity.data["object"]["inReplyTo"] == + "http://pleroma.example.org:4000/objects/55bce8fc-b423-46b1-af71-3759ab4670bc" + assert "http://pleroma.example.org:4000/users/lain5" in activity.data["to"] - assert activity.data["object"]["id"] == "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note" + + assert activity.data["object"]["id"] == + "tag:gs.example.org:4040,2017-04-25:noticeId=55:objectType=note" + assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"] end @@ -234,7 +264,10 @@ test "handle incoming follows" do incoming = File.read!("test/fixtures/follow.xml") {:ok, [activity]} = OStatus.handle_incoming(incoming) assert activity.data["type"] == "Follow" - assert activity.data["id"] == "tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00" + + assert activity.data["id"] == + "tag:social.heldscal.la,2017-05-07:subscription:23211:person:44803:2017-05-07T09:54:48+00:00" + assert activity.data["actor"] == "https://social.heldscal.la/user/23211" assert activity.data["object"] == "https://pawoo.net/users/pekorino" refute activity.local @@ -304,7 +337,8 @@ test "it returns user info in a hash" do expected = %{ "hub" => "https://social.heldscal.la/main/push/hub", - "magic_key" => "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB", + "magic_key" => + "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB", "name" => "shp", "nickname" => "shp", "salmon" => "https://social.heldscal.la/main/salmon/user/29191", @@ -314,10 +348,20 @@ test "it returns user info in a hash" do "host" => "social.heldscal.la", "fqn" => user, "bio" => "cofe", - "avatar" => %{"type" => "Image", "url" => [%{"href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg", "mediaType" => "image/jpeg", "type" => "Link"}]}, + "avatar" => %{ + "type" => "Image", + "url" => [ + %{ + "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg", + "mediaType" => "image/jpeg", + "type" => "Link" + } + ] + }, "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}", "ap_id" => nil } + assert data == expected end @@ -329,7 +373,8 @@ test "it works with the uri" do expected = %{ "hub" => "https://social.heldscal.la/main/push/hub", - "magic_key" => "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB", + "magic_key" => + "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB", "name" => "shp", "nickname" => "shp", "salmon" => "https://social.heldscal.la/main/salmon/user/29191", @@ -339,28 +384,40 @@ test "it works with the uri" do "host" => "social.heldscal.la", "fqn" => user, "bio" => "cofe", - "avatar" => %{"type" => "Image", "url" => [%{"href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg", "mediaType" => "image/jpeg", "type" => "Link"}]}, + "avatar" => %{ + "type" => "Image", + "url" => [ + %{ + "href" => "https://social.heldscal.la/avatar/29191-original-20170421154949.jpeg", + "mediaType" => "image/jpeg", + "type" => "Link" + } + ] + }, "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}", "ap_id" => nil } + assert data == expected end end describe "fetching a status by it's HTML url" do test "it builds a missing status from an html url" do - capture_log fn -> + capture_log(fn -> url = "https://shitposter.club/notice/2827873" - {:ok, [activity] } = OStatus.fetch_activity_from_url(url) + {:ok, [activity]} = OStatus.fetch_activity_from_url(url) assert activity.data["actor"] == "https://shitposter.club/user/1" - assert activity.data["object"]["id"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" - end + + assert activity.data["object"]["id"] == + "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" + end) end test "it works for atom notes, too" do url = "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056" - {:ok, [activity] } = OStatus.fetch_activity_from_url(url) + {:ok, [activity]} = OStatus.fetch_activity_from_url(url) assert activity.data["actor"] == "https://social.sakamoto.gq/users/eal" assert activity.data["object"]["id"] == url end @@ -370,6 +427,9 @@ test "it doesn't add nil in the do field" do incoming = File.read!("test/fixtures/nil_mention_entry.xml") {:ok, [activity]} = OStatus.handle_incoming(incoming) - assert activity.data["to"] == ["http://localhost:4001/users/atarifrosch@social.stopwatchingus-heidelberg.de/followers", "https://www.w3.org/ns/activitystreams#Public"] + assert activity.data["to"] == [ + "http://localhost:4001/users/atarifrosch@social.stopwatchingus-heidelberg.de/followers", + "https://www.w3.org/ns/activitystreams#Public" + ] end end diff --git a/test/web/salmon/salmon_test.exs b/test/web/salmon/salmon_test.exs index cf70c908f..1b39b4b2d 100644 --- a/test/web/salmon/salmon_test.exs +++ b/test/web/salmon/salmon_test.exs @@ -22,7 +22,7 @@ test "errors on wrong magic key" do end test "generates an RSA private key pem" do - {:ok, key} = Salmon.generate_rsa_pem + {:ok, key} = Salmon.generate_rsa_pem() assert is_binary(key) assert Regex.match?(~r/RSA/, key) end @@ -62,7 +62,8 @@ test "it gets a magic key" do salmon = File.read!("test/fixtures/salmon2.xml") {:ok, key} = Salmon.fetch_magic_key(salmon) - assert key == "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB" + assert key == + "RSA.uzg6r1peZU0vXGADWxGJ0PE34WvmhjUmydbX5YYdOiXfODVLwCMi1umGoqUDm-mRu4vNEdFBVJU1CpFA7dKzWgIsqsa501i2XqElmEveXRLvNRWFB6nG03Q5OUY2as8eE54BJm0p20GkMfIJGwP6TSFb-ICp3QjzbatuSPJ6xCE=.AQAB" end test "it pushes an activity to remote accounts it's addressed to" do @@ -75,13 +76,14 @@ test "it pushes an activity to remote accounts it's addressed to" do mentioned_user = insert(:user, user_data) note = insert(:note) + activity_data = %{ - "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, + "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(), "type" => "Create", "actor" => note.data["actor"], "to" => note.data["to"] ++ [mentioned_user.ap_id], "object" => note.data, - "published_at" => DateTime.utc_now() |> DateTime.to_iso8601, + "published_at" => DateTime.utc_now() |> DateTime.to_iso8601(), "context" => note.data["context"] } @@ -89,9 +91,10 @@ test "it pushes an activity to remote accounts it's addressed to" do user = Repo.get_by(User, ap_id: activity.data["actor"]) {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) - poster = fn (url, _data, _headers, _options) -> + poster = fn url, _data, _headers, _options -> assert url == "http://example.org/salmon" end + Salmon.publish(user, activity, poster) end end diff --git a/test/web/twitter_api/representers/activity_representer_test.exs b/test/web/twitter_api/representers/activity_representer_test.exs index 98a1705b0..bb47d4409 100644 --- a/test/web/twitter_api/representers/activity_representer_test.exs +++ b/test/web/twitter_api/representers/activity_representer_test.exs @@ -16,12 +16,19 @@ test "an announce activity" do {:ok, announce_activity, _object} = ActivityPub.announce(user, object) note_activity = Activity.get_by_ap_id(note_activity.data["id"]) - status = ActivityRepresenter.to_map(announce_activity, %{users: [user, activity_actor], announced_activity: note_activity, for: user}) + status = + ActivityRepresenter.to_map(announce_activity, %{ + users: [user, activity_actor], + announced_activity: note_activity, + for: user + }) assert status["id"] == announce_activity.id assert status["user"] == UserView.render("show.json", %{user: user, for: user}) - retweeted_status = ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user}) + retweeted_status = + ActivityRepresenter.to_map(note_activity, %{user: activity_actor, for: user}) + assert retweeted_status["repeated"] == true assert retweeted_status["id"] == note_activity.id assert status["statusnet_conversation_id"] == retweeted_status["statusnet_conversation_id"] @@ -36,7 +43,9 @@ test "a like activity" do object = Object.get_by_ap_id(note_activity.data["object"]["id"]) {:ok, like_activity, _object} = ActivityPub.like(user, object) - status = ActivityRepresenter.to_map(like_activity, %{user: user, liked_activity: note_activity}) + + status = + ActivityRepresenter.to_map(like_activity, %{user: user, liked_activity: note_activity}) assert status["id"] == like_activity.id assert status["in_reply_to_status_id"] == note_activity.id @@ -49,7 +58,7 @@ test "a like activity" do end test "an activity" do - {:ok, user} = UserBuilder.insert + {:ok, user} = UserBuilder.insert() # {:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"}) mentioned_user = insert(:user, %{nickname: "shp"}) @@ -70,16 +79,20 @@ test "an activity" do } } - content_html = "Some :2hu: content mentioning @shp" - content = HtmlSanitizeEx.strip_tags(content_html) - date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601 + content_html = + "Some :2hu: content mentioning @shp" + + content = HtmlSanitizeEx.strip_tags(content_html) + date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601() + + {:ok, convo_object} = Object.context_mapping("2hu") |> Repo.insert() - {:ok, convo_object} = Object.context_mapping("2hu") |> Repo.insert to = [ User.ap_followers(user), "https://www.w3.org/ns/activitystreams#Public", mentioned_user.ap_id ] + activity = %Activity{ id: 1, data: %{ @@ -92,7 +105,7 @@ test "an activity" do "type" => "Note", "content" => content_html, "summary" => "2hu", - "inReplyToStatusId" => 213123, + "inReplyToStatusId" => 213_123, "attachment" => [ object ], @@ -112,7 +125,10 @@ test "an activity" do recipients: to } - expected_html = "2hu
alert('YAY')Some 2hu content mentioning
@shp" + expected_html = + "2hu
alert('YAY')Some 2hu content mentioning @shp" expected_status = %{ "id" => activity.id, @@ -122,7 +138,7 @@ test "an activity" do "text" => "2hu" <> content, "is_post_verb" => true, "created_at" => "Tue May 24 13:26:08 +0000 2016", - "in_reply_to_status_id" => 213123, + "in_reply_to_status_id" => 213_123, "statusnet_conversation_id" => convo_object.id, "attachments" => [ ObjectRepresenter.to_map(object) @@ -141,7 +157,11 @@ test "an activity" do "uri" => activity.data["object"]["id"] } - assert ActivityRepresenter.to_map(activity, %{user: user, for: follower, mentioned: [mentioned_user]}) == expected_status + assert ActivityRepresenter.to_map(activity, %{ + user: user, + for: follower, + mentioned: [mentioned_user] + }) == expected_status end test "an undo for a follow" do diff --git a/test/web/twitter_api/representers/object_representer_test.exs b/test/web/twitter_api/representers/object_representer_test.exs index ac8184407..ebac051dc 100644 --- a/test/web/twitter_api/representers/object_representer_test.exs +++ b/test/web/twitter_api/representers/object_representer_test.exs @@ -34,13 +34,16 @@ test "represents mastodon-style attachments" do id: nil, data: %{ "mediaType" => "image/png", - "name" => "blabla", "type" => "Document", - "url" => "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png" + "name" => "blabla", + "type" => "Document", + "url" => + "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png" } } expected_object = %{ - url: "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png", + url: + "http://mastodon.example.org/system/media_attachments/files/000/000/001/original/8619f31c6edec470.png", mimetype: "image/png", oembed: false, id: nil diff --git a/test/web/twitter_api/twitter_api_controller_test.exs b/test/web/twitter_api/twitter_api_controller_test.exs index eef639112..5abdc2e0e 100644 --- a/test/web/twitter_api/twitter_api_controller_test.exs +++ b/test/web/twitter_api/twitter_api_controller_test.exs @@ -12,13 +12,15 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do describe "POST /api/account/verify_credentials" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/account/verify_credentials.json" + conn = post(conn, "/api/account/verify_credentials.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: user} do - conn = conn + conn = + conn |> with_credentials(user.nickname, "test") |> post("/api/account/verify_credentials.json") @@ -29,13 +31,15 @@ test "with credentials", %{conn: conn, user: user} do describe "POST /api/account/most_recent_notification" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/account/most_recent_notification.json" + conn = post(conn, "/api/account/most_recent_notification.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: user} do - conn = conn + conn = + conn |> with_credentials(user.nickname, "test") |> post("/api/account/most_recent_notification.json", %{id: "200"}) @@ -47,8 +51,9 @@ test "with credentials", %{conn: conn, user: user} do describe "POST /statuses/update.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/statuses/update.json" + conn = post(conn, "/api/statuses/update.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end @@ -56,30 +61,36 @@ test "with credentials", %{conn: conn, user: user} do conn_with_creds = conn |> with_credentials(user.nickname, "test") request_path = "/api/statuses/update.json" - error_response = %{"request" => request_path, - "error" => "Client must provide a 'status' parameter with a value."} + error_response = %{ + "request" => request_path, + "error" => "Client must provide a 'status' parameter with a value." + } + conn = conn_with_creds |> post(request_path) assert json_response(conn, 400) == error_response - conn = conn_with_creds |> post(request_path, %{ status: "" }) + conn = conn_with_creds |> post(request_path, %{status: ""}) assert json_response(conn, 400) == error_response - conn = conn_with_creds |> post(request_path, %{ status: " " }) + conn = conn_with_creds |> post(request_path, %{status: " "}) assert json_response(conn, 400) == error_response - conn = conn_with_creds |> post(request_path, %{ status: "Nice meme." }) - assert json_response(conn, 200) == ActivityRepresenter.to_map(Repo.one(Activity), %{user: user}) + conn = conn_with_creds |> post(request_path, %{status: "Nice meme."}) + + assert json_response(conn, 200) == + ActivityRepresenter.to_map(Repo.one(Activity), %{user: user}) end end describe "GET /statuses/public_timeline.json" do test "returns statuses", %{conn: conn} do - {:ok, user} = UserBuilder.insert + {:ok, user} = UserBuilder.insert() activities = ActivityBuilder.insert_list(30, %{}, %{user: user}) ActivityBuilder.insert_list(10, %{}, %{user: user}) since_id = List.last(activities).id - conn = conn + conn = + conn |> get("/api/statuses/public_timeline.json", %{since_id: since_id}) response = json_response(conn, 200) @@ -94,8 +105,9 @@ test "returns one status", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "Hey!"}) actor = Repo.get_by!(User, ap_id: activity.data["actor"]) - conn = conn - |> get("/api/statuses/show/#{activity.id}.json") + conn = + conn + |> get("/api/statuses/show/#{activity.id}.json") response = json_response(conn, 200) @@ -107,8 +119,9 @@ test "returns one status", %{conn: conn} do test "gets user with screen_name", %{conn: conn} do user = insert(:user) - conn = conn - |> get("/api/users/show.json", %{"screen_name" => user.nickname}) + conn = + conn + |> get("/api/users/show.json", %{"screen_name" => user.nickname}) response = json_response(conn, 200) @@ -118,8 +131,9 @@ test "gets user with screen_name", %{conn: conn} do test "gets user with user_id", %{conn: conn} do user = insert(:user) - conn = conn - |> get("/api/users/show.json", %{"user_id" => user.id}) + conn = + conn + |> get("/api/users/show.json", %{"user_id" => user.id}) response = json_response(conn, 200) @@ -132,9 +146,10 @@ test "gets a user for a logged in user", %{conn: conn} do {:ok, logged_in, user, _activity} = TwitterAPI.follow(logged_in, %{"user_id" => user.id}) - conn = conn - |> with_credentials(logged_in.nickname, "test") - |> get("/api/users/show.json", %{"user_id" => user.id}) + conn = + conn + |> with_credentials(logged_in.nickname, "test") + |> get("/api/users/show.json", %{"user_id" => user.id}) response = json_response(conn, 200) @@ -144,14 +159,16 @@ test "gets a user for a logged in user", %{conn: conn} do describe "GET /statusnet/conversation/:id.json" do test "returns the statuses in the conversation", %{conn: conn} do - {:ok, _user} = UserBuilder.insert + {:ok, _user} = UserBuilder.insert() {:ok, _activity} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) {:ok, _activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - {:ok, object} = Object.context_mapping("2hu") |> Repo.insert - conn = conn - |> get("/api/statusnet/conversation/#{object.id}.json") + {:ok, object} = Object.context_mapping("2hu") |> Repo.insert() + + conn = + conn + |> get("/api/statusnet/conversation/#{object.id}.json") response = json_response(conn, 200) @@ -161,58 +178,87 @@ test "returns the statuses in the conversation", %{conn: conn} do describe "GET /statuses/friends_timeline.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = get conn, "/api/statuses/friends_timeline.json" + conn = get(conn, "/api/statuses/friends_timeline.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do user = insert(:user) - activities = ActivityBuilder.insert_list(30, %{"to" => [User.ap_followers(user)]}, %{user: user}) - returned_activities = ActivityBuilder.insert_list(10, %{"to" => [User.ap_followers(user)]}, %{user: user}) + + activities = + ActivityBuilder.insert_list(30, %{"to" => [User.ap_followers(user)]}, %{user: user}) + + returned_activities = + ActivityBuilder.insert_list(10, %{"to" => [User.ap_followers(user)]}, %{user: user}) + other_user = insert(:user) ActivityBuilder.insert_list(10, %{}, %{user: other_user}) since_id = List.last(activities).id - current_user = Ecto.Changeset.change(current_user, following: [User.ap_followers(user)]) |> Repo.update! + current_user = + Ecto.Changeset.change(current_user, following: [User.ap_followers(user)]) + |> Repo.update!() - conn = conn + conn = + conn |> with_credentials(current_user.nickname, "test") |> get("/api/statuses/friends_timeline.json", %{since_id: since_id}) response = json_response(conn, 200) assert length(response) == 10 - assert response == Enum.map(returned_activities, fn (activity) -> ActivityRepresenter.to_map(activity, %{user: User.get_cached_by_ap_id(activity.data["actor"]), for: current_user}) end) + + assert response == + Enum.map(returned_activities, fn activity -> + ActivityRepresenter.to_map(activity, %{ + user: User.get_cached_by_ap_id(activity.data["actor"]), + for: current_user + }) + end) end end describe "GET /statuses/mentions.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = get conn, "/api/statuses/mentions.json" + conn = get(conn, "/api/statuses/mentions.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do - {:ok, activity} = ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user}) + {:ok, activity} = + ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user}) - conn = conn + conn = + conn |> with_credentials(current_user.nickname, "test") |> get("/api/statuses/mentions.json") response = json_response(conn, 200) assert length(response) == 1 - assert Enum.at(response, 0) == ActivityRepresenter.to_map(activity, %{user: current_user, mentioned: [current_user]}) + + assert Enum.at(response, 0) == + ActivityRepresenter.to_map(activity, %{ + user: current_user, + mentioned: [current_user] + }) end end describe "GET /statuses/user_timeline.json" do setup [:valid_user] + test "without any params", %{conn: conn} do conn = get(conn, "/api/statuses/user_timeline.json") - assert json_response(conn, 400) == %{"error" => "You need to specify screen_name or user_id", "request" => "/api/statuses/user_timeline.json"} + + assert json_response(conn, 400) == %{ + "error" => "You need to specify screen_name or user_id", + "request" => "/api/statuses/user_timeline.json" + } end test "with user_id", %{conn: conn} do @@ -237,9 +283,11 @@ test "with screen_name", %{conn: conn} do test "with credentials", %{conn: conn, user: current_user} do {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: current_user}) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json") + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> get("/api/statuses/user_timeline.json") response = json_response(conn, 200) @@ -250,9 +298,11 @@ test "with credentials", %{conn: conn, user: current_user} do test "with credentials with user_id", %{conn: conn, user: current_user} do user = insert(:user) {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json", %{"user_id" => user.id}) + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> get("/api/statuses/user_timeline.json", %{"user_id" => user.id}) response = json_response(conn, 200) @@ -263,9 +313,11 @@ test "with credentials with user_id", %{conn: conn, user: current_user} do test "with credentials screen_name", %{conn: conn, user: current_user} do user = insert(:user) {:ok, activity} = ActivityBuilder.insert(%{"id" => 1}, %{user: user}) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> get("/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> get("/api/statuses/user_timeline.json", %{"screen_name" => user.nickname}) response = json_response(conn, 200) @@ -276,28 +328,33 @@ test "with credentials screen_name", %{conn: conn, user: current_user} do describe "POST /friendships/create.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/friendships/create.json" + conn = post(conn, "/api/friendships/create.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do followed = insert(:user) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/friendships/create.json", %{user_id: followed.id}) + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/friendships/create.json", %{user_id: followed.id}) current_user = Repo.get(User, current_user.id) assert User.ap_followers(followed) in current_user.following - assert json_response(conn, 200) == UserView.render("show.json", %{user: followed, for: current_user}) + + assert json_response(conn, 200) == + UserView.render("show.json", %{user: followed, for: current_user}) end end describe "POST /friendships/destroy.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/friendships/destroy.json" + conn = post(conn, "/api/friendships/destroy.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end @@ -308,40 +365,48 @@ test "with credentials", %{conn: conn, user: current_user} do assert User.ap_followers(followed) in current_user.following ActivityPub.follow(current_user, followed) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/friendships/destroy.json", %{user_id: followed.id}) + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/friendships/destroy.json", %{user_id: followed.id}) current_user = Repo.get(User, current_user.id) assert current_user.following == [current_user.ap_id] - assert json_response(conn, 200) == UserView.render("show.json", %{user: followed, for: current_user}) + + assert json_response(conn, 200) == + UserView.render("show.json", %{user: followed, for: current_user}) end end describe "POST /blocks/create.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/blocks/create.json" + conn = post(conn, "/api/blocks/create.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do blocked = insert(:user) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/blocks/create.json", %{user_id: blocked.id}) + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/blocks/create.json", %{user_id: blocked.id}) current_user = Repo.get(User, current_user.id) assert User.blocks?(current_user, blocked) - assert json_response(conn, 200) == UserView.render("show.json", %{user: blocked, for: current_user}) + + assert json_response(conn, 200) == + UserView.render("show.json", %{user: blocked, for: current_user}) end end describe "POST /blocks/destroy.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/blocks/destroy.json" + conn = post(conn, "/api/blocks/destroy.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end @@ -351,56 +416,66 @@ test "with credentials", %{conn: conn, user: current_user} do {:ok, current_user} = User.block(current_user, blocked) assert User.blocks?(current_user, blocked) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/blocks/destroy.json", %{user_id: blocked.id}) + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/blocks/destroy.json", %{user_id: blocked.id}) current_user = Repo.get(User, current_user.id) assert current_user.info["blocks"] == [] - assert json_response(conn, 200) == UserView.render("show.json", %{user: blocked, for: current_user}) + + assert json_response(conn, 200) == + UserView.render("show.json", %{user: blocked, for: current_user}) end end describe "GET /help/test.json" do test "returns \"ok\"", %{conn: conn} do - conn = get conn, "/api/help/test.json" + conn = get(conn, "/api/help/test.json") assert json_response(conn, 200) == "ok" end end describe "POST /api/qvitter/update_avatar.json" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do - conn = post conn, "/api/qvitter/update_avatar.json" + conn = post(conn, "/api/qvitter/update_avatar.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do avatar_image = File.read!("test/fixtures/avatar_data_uri") - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/qvitter/update_avatar.json", %{img: avatar_image}) + + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/qvitter/update_avatar.json", %{img: avatar_image}) current_user = Repo.get(User, current_user.id) assert is_map(current_user.avatar) - assert json_response(conn, 200) == UserView.render("show.json", %{user: current_user, for: current_user}) + + assert json_response(conn, 200) == + UserView.render("show.json", %{user: current_user, for: current_user}) end end describe "POST /api/favorites/create/:id" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do note_activity = insert(:note_activity) - conn = post conn, "/api/favorites/create/#{note_activity.id}.json" + conn = post(conn, "/api/favorites/create/#{note_activity.id}.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end test "with credentials", %{conn: conn, user: current_user} do note_activity = insert(:note_activity) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/create/#{note_activity.id}.json") + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/favorites/create/#{note_activity.id}.json") assert json_response(conn, 200) end @@ -408,9 +483,10 @@ test "with credentials", %{conn: conn, user: current_user} do describe "POST /api/favorites/destroy/:id" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do note_activity = insert(:note_activity) - conn = post conn, "/api/favorites/destroy/#{note_activity.id}.json" + conn = post(conn, "/api/favorites/destroy/#{note_activity.id}.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end @@ -419,9 +495,10 @@ test "with credentials", %{conn: conn, user: current_user} do object = Object.get_by_ap_id(note_activity.data["object"]["id"]) ActivityPub.like(current_user, object) - conn = conn - |> with_credentials(current_user.nickname, "test") - |> post("/api/favorites/destroy/#{note_activity.id}.json") + conn = + conn + |> with_credentials(current_user.nickname, "test") + |> post("/api/favorites/destroy/#{note_activity.id}.json") assert json_response(conn, 200) end @@ -429,9 +506,10 @@ test "with credentials", %{conn: conn, user: current_user} do describe "POST /api/statuses/retweet/:id" do setup [:valid_user] + test "without valid credentials", %{conn: conn} do note_activity = insert(:note_activity) - conn = post conn, "/api/statuses/retweet/#{note_activity.id}.json" + conn = post(conn, "/api/statuses/retweet/#{note_activity.id}.json") assert json_response(conn, 403) == %{"error" => "Invalid credentials."} end @@ -440,12 +518,16 @@ test "with credentials", %{conn: conn, user: current_user} do request_path = "/api/statuses/retweet/#{note_activity.id}.json" - response = conn - |> with_credentials(current_user.nickname, "test") - |> post(request_path) + response = + conn + |> with_credentials(current_user.nickname, "test") + |> post(request_path) + activity = Repo.get(Activity, note_activity.id) activity_user = Repo.get_by(User, ap_id: note_activity.data["actor"]) - assert json_response(response, 200) == ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user}) + + assert json_response(response, 200) == + ActivityRepresenter.to_map(activity, %{user: activity_user, for: current_user}) end end @@ -460,8 +542,9 @@ test "it creates a new user", %{conn: conn} do "confirm" => "bear" } - conn = conn - |> post("/api/account/register", data) + conn = + conn + |> post("/api/account/register", data) user = json_response(conn, 200) @@ -478,8 +561,9 @@ test "it returns errors on a problem", %{conn: conn} do "confirm" => "bear" } - conn = conn - |> post("/api/account/register", data) + conn = + conn + |> post("/api/account/register", data) errors = json_response(conn, 400) @@ -492,9 +576,10 @@ test "it returns the user", %{conn: conn} do user = insert(:user) other_user = insert(:user) - conn = conn - |> assign(:user, user) - |> get("/api/externalprofile/show", %{profileurl: other_user.ap_id}) + conn = + conn + |> assign(:user, user) + |> get("/api/externalprofile/show", %{profileurl: other_user.ap_id}) assert json_response(conn, 200) == UserView.render("show.json", %{user: other_user}) end @@ -510,11 +595,13 @@ test "it returns a user's followers", %{conn: conn} do {:ok, follower_one} = User.follow(follower_one, user) {:ok, follower_two} = User.follow(follower_two, user) - conn = conn - |> assign(:user, user) - |> get("/api/statuses/followers") + conn = + conn + |> assign(:user, user) + |> get("/api/statuses/followers") - assert json_response(conn, 200) == UserView.render("index.json", %{users: [follower_one, follower_two], for: user}) + assert json_response(conn, 200) == + UserView.render("index.json", %{users: [follower_one, follower_two], for: user}) end end @@ -528,11 +615,17 @@ test "it returns the logged in user's friends", %{conn: conn} do {:ok, user} = User.follow(user, followed_one) {:ok, user} = User.follow(user, followed_two) - conn = conn - |> assign(:user, user) - |> get("/api/statuses/friends") + conn = + conn + |> assign(:user, user) + |> get("/api/statuses/friends") - assert MapSet.equal?(MapSet.new(json_response(conn, 200)), MapSet.new(UserView.render("index.json", %{users: [followed_one, followed_two], for: user}))) + assert MapSet.equal?( + MapSet.new(json_response(conn, 200)), + MapSet.new( + UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) + ) + ) end test "it returns a given user's friends with user_id", %{conn: conn} do @@ -544,10 +637,16 @@ test "it returns a given user's friends with user_id", %{conn: conn} do {:ok, user} = User.follow(user, followed_one) {:ok, user} = User.follow(user, followed_two) - conn = conn - |> get("/api/statuses/friends", %{"user_id" => user.id}) + conn = + conn + |> get("/api/statuses/friends", %{"user_id" => user.id}) - assert MapSet.equal?(MapSet.new(json_response(conn, 200)), MapSet.new(UserView.render("index.json", %{users: [followed_one, followed_two], for: user}))) + assert MapSet.equal?( + MapSet.new(json_response(conn, 200)), + MapSet.new( + UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) + ) + ) end test "it returns a given user's friends with screen_name", %{conn: conn} do @@ -559,10 +658,16 @@ test "it returns a given user's friends with screen_name", %{conn: conn} do {:ok, user} = User.follow(user, followed_one) {:ok, user} = User.follow(user, followed_two) - conn = conn - |> get("/api/statuses/friends", %{"screen_name" => user.nickname}) + conn = + conn + |> get("/api/statuses/friends", %{"screen_name" => user.nickname}) - assert MapSet.equal?(MapSet.new(json_response(conn, 200)), MapSet.new(UserView.render("index.json", %{users: [followed_one, followed_two], for: user}))) + assert MapSet.equal?( + MapSet.new(json_response(conn, 200)), + MapSet.new( + UserView.render("index.json", %{users: [followed_one, followed_two], for: user}) + ) + ) end end @@ -576,12 +681,17 @@ test "it returns a user's friends", %{conn: conn} do {:ok, user} = User.follow(user, followed_one) {:ok, user} = User.follow(user, followed_two) - conn = conn - |> assign(:user, user) - |> get("/api/friends/ids") + conn = + conn + |> assign(:user, user) + |> get("/api/friends/ids") expected = [followed_one.id, followed_two.id] - assert MapSet.equal?(MapSet.new(Poison.decode!(json_response(conn, 200))), MapSet.new(expected)) + + assert MapSet.equal?( + MapSet.new(Poison.decode!(json_response(conn, 200))), + MapSet.new(expected) + ) end end @@ -589,9 +699,13 @@ test "it returns a user's friends", %{conn: conn} do test "it updates a user's profile", %{conn: conn} do user = insert(:user) - conn = conn - |> assign(:user, user) - |> post("/api/account/update_profile.json", %{"name" => "new name", "description" => "new description"}) + conn = + conn + |> assign(:user, user) + |> post("/api/account/update_profile.json", %{ + "name" => "new name", + "description" => "new description" + }) user = Repo.get!(User, user.id) assert user.name == "new name" @@ -619,8 +733,9 @@ test "it returns search results", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = conn - |> get("/api/search.json", %{"q" => "2hu", "page" => "1", "rpp" => "1"}) + conn = + conn + |> get("/api/search.json", %{"q" => "2hu", "page" => "1", "rpp" => "1"}) assert [status] = json_response(conn, 200) assert status["id"] == activity.id @@ -635,8 +750,9 @@ test "it returns the tags timeline", %{conn: conn} do {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about #2hu"}) {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) - conn = conn - |> get("/api/statusnet/tags/timeline/2hu.json") + conn = + conn + |> get("/api/statusnet/tags/timeline/2hu.json") assert [status] = json_response(conn, 200) assert status["id"] == activity.id diff --git a/test/web/twitter_api/twitter_api_test.exs b/test/web/twitter_api/twitter_api_test.exs index 715a746ef..d4341a867 100644 --- a/test/web/twitter_api/twitter_api_test.exs +++ b/test/web/twitter_api/twitter_api_test.exs @@ -28,23 +28,32 @@ test "create a status" do object = Repo.insert!(%Object{data: object_data}) input = %{ - "status" => "Hello again, @shp.\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric", + "status" => + "Hello again, @shp.\nThis is on another :moominmamma: line. #2hu #epic #phantasmagoric", "media_ids" => [object.id] } - { :ok, activity = %Activity{} } = TwitterAPI.create_status(user, input) + {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) + + expected_text = + "Hello again, @shp.<script></script>
This is on another :moominmamma: line.
image.jpg" - expected_text = "Hello again, @shp.<script></script>
This is on another :moominmamma: line.
image.jpg" assert get_in(activity.data, ["object", "content"]) == expected_text assert get_in(activity.data, ["object", "type"]) == "Note" assert get_in(activity.data, ["object", "actor"]) == user.ap_id assert get_in(activity.data, ["actor"]) == user.ap_id assert Enum.member?(get_in(activity.data, ["cc"]), User.ap_followers(user)) - assert Enum.member?(get_in(activity.data, ["to"]), "https://www.w3.org/ns/activitystreams#Public") + + assert Enum.member?( + get_in(activity.data, ["to"]), + "https://www.w3.org/ns/activitystreams#Public" + ) + assert Enum.member?(get_in(activity.data, ["cc"]), "shp") assert activity.local == true - assert %{"moominmamma" => "http://localhost:4001/finmoji/128px/moominmamma-128.png"} = activity.data["object"]["emoji"] + assert %{"moominmamma" => "http://localhost:4001/finmoji/128px/moominmamma-128.png"} = + activity.data["object"]["emoji"] # hashtags assert activity.data["object"]["tag"] == ["2hu", "epic", "phantasmagoric"] @@ -64,27 +73,31 @@ test "create a status" do test "create a status that is a reply" do user = insert(:user) + input = %{ "status" => "Hello again." } - { :ok, activity = %Activity{} } = TwitterAPI.create_status(user, input) + {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) input = %{ "status" => "Here's your (you).", "in_reply_to_status_id" => activity.id } - { :ok, reply = %Activity{} } = TwitterAPI.create_status(user, input) + {:ok, reply = %Activity{}} = TwitterAPI.create_status(user, input) assert get_in(reply.data, ["context"]) == get_in(activity.data, ["context"]) - assert get_in(reply.data, ["object", "context"]) == get_in(activity.data, ["object", "context"]) + + assert get_in(reply.data, ["object", "context"]) == + get_in(activity.data, ["object", "context"]) + assert get_in(reply.data, ["object", "inReplyTo"]) == get_in(activity.data, ["object", "id"]) assert get_in(reply.data, ["object", "inReplyToStatusId"]) == activity.id end test "fetch public statuses, excluding remote ones." do - %{ public: activity, user: user } = ActivityBuilder.public_and_non_public + %{public: activity, user: user} = ActivityBuilder.public_and_non_public() insert(:note_activity, %{local: false}) follower = insert(:user, following: [User.ap_followers(user)]) @@ -92,11 +105,13 @@ test "fetch public statuses, excluding remote ones." do statuses = TwitterAPI.fetch_public_statuses(follower) assert length(statuses) == 1 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: user, for: follower}) + + assert Enum.at(statuses, 0) == + ActivityRepresenter.to_map(activity, %{user: user, for: follower}) end test "fetch whole known network statuses" do - %{ public: activity, user: user } = ActivityBuilder.public_and_non_public + %{public: activity, user: user} = ActivityBuilder.public_and_non_public() insert(:note_activity, %{local: false}) follower = insert(:user, following: [user.follower_address]) @@ -104,7 +119,9 @@ test "fetch whole known network statuses" do statuses = TwitterAPI.fetch_public_and_external_statuses(follower) assert length(statuses) == 2 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: user, for: follower}) + + assert Enum.at(statuses, 0) == + ActivityRepresenter.to_map(activity, %{user: user, for: follower}) end test "fetch friends' statuses" do @@ -119,7 +136,12 @@ test "fetch friends' statuses" do assert length(statuses) == 2 assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user}) - assert Enum.at(statuses, 1) == ActivityRepresenter.to_map(direct_activity, %{user: direct_activity_user, mentioned: [user]}) + + assert Enum.at(statuses, 1) == + ActivityRepresenter.to_map(direct_activity, %{ + user: direct_activity_user, + mentioned: [user] + }) end test "fetch user's mentions" do @@ -130,12 +152,16 @@ test "fetch user's mentions" do statuses = TwitterAPI.fetch_mentions(user) assert length(statuses) == 1 - assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user, mentioned: [user]}) + + assert Enum.at(statuses, 0) == + ActivityRepresenter.to_map(activity, %{user: activity_user, mentioned: [user]}) end test "get a user by params" do user1_result = {:ok, user1} = UserBuilder.insert(%{ap_id: "some id", email: "test@pleroma"}) - {:ok, user2} = UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) + + {:ok, user2} = + UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) assert {:error, "You need to specify screen_name or user_id"} == TwitterAPI.get_user(nil, nil) assert user1_result == TwitterAPI.get_user(nil, %{"user_id" => user1.id}) @@ -144,13 +170,18 @@ test "get a user by params" do assert user1_result == TwitterAPI.get_user(user1, nil) assert user1_result == TwitterAPI.get_user(user2, %{"user_id" => user1.id}) assert user1_result == TwitterAPI.get_user(user2, %{"screen_name" => user1.nickname}) - assert {:error, "No user with such screen_name"} == TwitterAPI.get_user(nil, %{"screen_name" => "Satan"}) + + assert {:error, "No user with such screen_name"} == + TwitterAPI.get_user(nil, %{"screen_name" => "Satan"}) + assert {:error, "No user with such user_id"} == TwitterAPI.get_user(nil, %{"user_id" => 666}) end test "fetch user's statuses" do {:ok, user1} = UserBuilder.insert(%{ap_id: "some id", email: "test@pleroma"}) - {:ok, user2} = UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) + + {:ok, user2} = + UserBuilder.insert(%{ap_id: "some other id", nickname: "testname2", email: "test2@pleroma"}) {:ok, status1} = ActivityBuilder.insert(%{"id" => 1}, %{user: user1}) {:ok, status2} = ActivityBuilder.insert(%{"id" => 2}, %{user: user2}) @@ -180,10 +211,10 @@ test "Follow another user using user_id" do user = insert(:user) followed = insert(:user) - {:ok, user, followed, _activity } = TwitterAPI.follow(user, %{"user_id" => followed.id}) + {:ok, user, followed, _activity} = TwitterAPI.follow(user, %{"user_id" => followed.id}) assert User.ap_followers(followed) in user.following - { :error, msg } = TwitterAPI.follow(user, %{"user_id" => followed.id}) + {:error, msg} = TwitterAPI.follow(user, %{"user_id" => followed.id}) assert msg == "Could not follow user: #{followed.nickname} is already on your list." end @@ -191,13 +222,15 @@ test "Follow another user using screen_name" do user = insert(:user) followed = insert(:user) - {:ok, user, followed, _activity } = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) + {:ok, user, followed, _activity} = + TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) + assert User.ap_followers(followed) in user.following followed = User.get_by_ap_id(followed.ap_id) assert followed.info["follower_count"] == 1 - { :error, msg } = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) + {:error, msg} = TwitterAPI.follow(user, %{"screen_name" => followed.nickname}) assert msg == "Could not follow user: #{followed.nickname} is already on your list." end @@ -206,10 +239,10 @@ test "Unfollow another user using user_id" do user = insert(:user, %{following: [User.ap_followers(unfollowed)]}) ActivityPub.follow(user, unfollowed) - {:ok, user, unfollowed } = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) + {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) assert user.following == [] - { :error, msg } = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) + {:error, msg} = TwitterAPI.unfollow(user, %{"user_id" => unfollowed.id}) assert msg == "Not subscribed!" end @@ -219,10 +252,10 @@ test "Unfollow another user using screen_name" do ActivityPub.follow(user, unfollowed) - {:ok, user, unfollowed } = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) + {:ok, user, unfollowed} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) assert user.following == [] - { :error, msg } = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) + {:error, msg} = TwitterAPI.unfollow(user, %{"screen_name" => unfollowed.nickname}) assert msg == "Not subscribed!" end @@ -266,7 +299,7 @@ test "fetch statuses in a context using the conversation id" do {:ok, activity_two} = ActivityBuilder.insert(%{"type" => "Create", "context" => "2hu"}) {:ok, _activity_three} = ActivityBuilder.insert(%{"type" => "Create", "context" => "3hu"}) - {:ok, object} = Object.context_mapping("2hu") |> Repo.insert + {:ok, object} = Object.context_mapping("2hu") |> Repo.insert() statuses = TwitterAPI.fetch_conversation(user, object.id) @@ -276,7 +309,11 @@ test "fetch statuses in a context using the conversation id" do end test "upload a file" do - file = %Plug.Upload{content_type: "image/jpg", path: Path.absname("test/fixtures/image.jpg"), filename: "an_image.jpg"} + file = %Plug.Upload{ + content_type: "image/jpg", + path: Path.absname("test/fixtures/image.jpg"), + filename: "an_image.jpg" + } response = TwitterAPI.upload(file) @@ -291,7 +328,8 @@ test "it favorites a status, returns the updated status" do {:ok, status} = TwitterAPI.fav(user, note_activity.id) updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert status == ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) + assert status == + ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) end test "it unfavorites a status, returns the updated status" do @@ -300,9 +338,12 @@ test "it unfavorites a status, returns the updated status" do activity_user = Repo.get_by!(User, ap_id: note_activity.data["actor"]) object = Object.get_by_ap_id(note_activity.data["object"]["id"]) - {:ok, _like_activity, _object } = ActivityPub.like(user, object) + {:ok, _like_activity, _object} = ActivityPub.like(user, object) updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user})["fave_num"] == 1 + + assert ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user})[ + "fave_num" + ] == 1 {:ok, status} = TwitterAPI.unfav(user, note_activity.id) @@ -317,7 +358,8 @@ test "it retweets a status and returns the retweet" do {:ok, status} = TwitterAPI.repeat(user, note_activity.id) updated_activity = Activity.get_by_ap_id(note_activity.data["id"]) - assert status == ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) + assert status == + ActivityRepresenter.to_map(updated_activity, %{user: activity_user, for: user}) end test "it registers a new user and returns the user." do @@ -333,7 +375,9 @@ test "it registers a new user and returns the user." do {:ok, user} = TwitterAPI.register_user(data) fetched_user = Repo.get_by(User, nickname: "lain") - assert UserView.render("show.json", %{user: user}) == UserView.render("show.json", %{user: fetched_user}) + + assert UserView.render("show.json", %{user: user}) == + UserView.render("show.json", %{user: fetched_user}) end test "it returns the error on registration problems" do @@ -374,7 +418,7 @@ test "creates a mapping object" do end test "returns an existing mapping for an existing object" do - {:ok, object} = Object.context_mapping("random context") |> Repo.insert + {:ok, object} = Object.context_mapping("random context") |> Repo.insert() conversation_id = TwitterAPI.context_to_conversation_id("random context") assert conversation_id == object.id diff --git a/test/web/twitter_api/views/user_view_test.exs b/test/web/twitter_api/views/user_view_test.exs index 9e0a8a532..dd55c0b7e 100644 --- a/test/web/twitter_api/views/user_view_test.exs +++ b/test/web/twitter_api/views/user_view_test.exs @@ -15,7 +15,7 @@ defmodule Pleroma.Web.TwitterAPI.UserViewTest do test "A user with an avatar object", %{user: user} do image = "image" - user = %{ user | avatar: %{ "url" => [%{"href" => image}] }} + user = %{user | avatar: %{"url" => [%{"href" => image}]}} represented = UserView.render("show.json", %{user: user}) assert represented["profile_image_url"] == image end @@ -41,7 +41,7 @@ test "A user" do "name" => user.name, "screen_name" => user.nickname, "description" => HtmlSanitizeEx.strip_tags(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime, + "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "favourites_count" => 0, "statuses_count" => 1, "friends_count" => 1, @@ -76,7 +76,7 @@ test "A user for a given other follower", %{user: user} do "name" => user.name, "screen_name" => user.nickname, "description" => HtmlSanitizeEx.strip_tags(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime, + "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "favourites_count" => 0, "statuses_count" => 0, "friends_count" => 0, @@ -112,7 +112,7 @@ test "A user that follows you", %{user: user} do "name" => follower.name, "screen_name" => follower.nickname, "description" => HtmlSanitizeEx.strip_tags(follower.bio), - "created_at" => follower.inserted_at |> Utils.format_naive_asctime, + "created_at" => follower.inserted_at |> Utils.format_naive_asctime(), "favourites_count" => 0, "statuses_count" => 0, "friends_count" => 1, @@ -155,7 +155,7 @@ test "A blocked user for the blocker" do "name" => user.name, "screen_name" => user.nickname, "description" => HtmlSanitizeEx.strip_tags(user.bio), - "created_at" => user.inserted_at |> Utils.format_naive_asctime, + "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "favourites_count" => 0, "statuses_count" => 0, "friends_count" => 0, diff --git a/test/web/views/error_view_test.exs b/test/web/views/error_view_test.exs index 48cdc5159..1d443b187 100644 --- a/test/web/views/error_view_test.exs +++ b/test/web/views/error_view_test.exs @@ -5,17 +5,16 @@ defmodule Pleroma.Web.ErrorViewTest do import Phoenix.View test "renders 404.json" do - assert render(Pleroma.Web.ErrorView, "404.json", []) == - %{errors: %{detail: "Page not found"}} + assert render(Pleroma.Web.ErrorView, "404.json", []) == %{errors: %{detail: "Page not found"}} end test "render 500.json" do assert render(Pleroma.Web.ErrorView, "500.json", []) == - %{errors: %{detail: "Internal server error"}} + %{errors: %{detail: "Internal server error"}} end test "render any other" do assert render(Pleroma.Web.ErrorView, "505.json", []) == - %{errors: %{detail: "Internal server error"}} + %{errors: %{detail: "Internal server error"}} end end diff --git a/test/web/web_finger/web_finger_test.exs b/test/web/web_finger/web_finger_test.exs index c7ad206eb..69216f393 100644 --- a/test/web/web_finger/web_finger_test.exs +++ b/test/web/web_finger/web_finger_test.exs @@ -7,7 +7,7 @@ defmodule Pleroma.Web.WebFingerTest do test "returns a link to the xml lrdd" do host_info = WebFinger.host_meta() - assert String.contains?(host_info, Pleroma.Web.base_url) + assert String.contains?(host_info, Pleroma.Web.base_url()) end end @@ -15,7 +15,9 @@ test "returns a link to the xml lrdd" do test "works for fqns" do user = insert(:user) - {:ok, result} = WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host}", "XML") + {:ok, result} = + WebFinger.webfinger("#{user.nickname}@#{Pleroma.Web.Endpoint.host()}", "XML") + assert is_binary(result) end @@ -33,7 +35,9 @@ test "returns the info for an OStatus user" do {:ok, data} = WebFinger.finger(user) - assert data["magic_key"] == "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB" + assert data["magic_key"] == + "RSA.wQ3i9UA0qmAxZ0WTIp4a-waZn_17Ez1pEEmqmqoooRsG1_BvpmOvLN0G2tEcWWxl2KOtdQMCiPptmQObeZeuj48mdsDZ4ArQinexY2hCCTcbV8Xpswpkb8K05RcKipdg07pnI7tAgQ0VWSZDImncL6YUGlG5YN8b5TjGOwk2VG8=.AQAB" + assert data["topic"] == "https://social.heldscal.la/api/statuses/user_timeline/29191.atom" assert data["subject"] == "acct:shp@social.heldscal.la" assert data["salmon"] == "https://social.heldscal.la/main/salmon/user/29191" @@ -50,7 +54,9 @@ test "returns the correctly for json ostatus users" do {:ok, data} = WebFinger.finger(user) - assert data["magic_key"] == "RSA.qfYaxztz7ZELrE4v5WpJrPM99SKI3iv9Y3Tw6nfLGk-4CRljNYqV8IYX2FXjeucC_DKhPNnlF6fXyASpcSmA_qupX9WC66eVhFhZ5OuyBOeLvJ1C4x7Hi7Di8MNBxY3VdQuQR0tTaS_YAZCwASKp7H6XEid3EJpGt0EQZoNzRd8=.AQAB" + assert data["magic_key"] == + "RSA.qfYaxztz7ZELrE4v5WpJrPM99SKI3iv9Y3Tw6nfLGk-4CRljNYqV8IYX2FXjeucC_DKhPNnlF6fXyASpcSmA_qupX9WC66eVhFhZ5OuyBOeLvJ1C4x7Hi7Di8MNBxY3VdQuQR0tTaS_YAZCwASKp7H6XEid3EJpGt0EQZoNzRd8=.AQAB" + assert data["topic"] == "https://gnusocial.de/api/statuses/user_timeline/249296.atom" assert data["subject"] == "acct:winterdienst@gnusocial.de" assert data["salmon"] == "https://gnusocial.de/main/salmon/user/249296" diff --git a/test/web/websub/websub_controller_test.exs b/test/web/websub/websub_controller_test.exs index f6b86eca0..d861c241f 100644 --- a/test/web/websub/websub_controller_test.exs +++ b/test/web/websub/websub_controller_test.exs @@ -18,8 +18,9 @@ test "websub subscription request", %{conn: conn} do "hub.lease_seconds": "100" } - conn = conn - |> post(path, data) + conn = + conn + |> post(path, data) assert response(conn, 202) == "Accepted" end @@ -34,14 +35,15 @@ test "websub subscription confirmation", %{conn: conn} do "hub.lease_seconds" => "100" } - conn = conn - |> get("/push/subscriptions/#{websub.id}", params) + conn = + conn + |> get("/push/subscriptions/#{websub.id}", params) websub = Repo.get(WebsubClientSubscription, websub.id) assert response(conn, 200) == "some challenge" assert websub.state == "accepted" - assert_in_delta NaiveDateTime.diff(websub.valid_until, NaiveDateTime.utc_now), 100, 5 + assert_in_delta NaiveDateTime.diff(websub.valid_until, NaiveDateTime.utc_now()), 100, 5 end test "handles incoming feed updates", %{conn: conn} do @@ -49,10 +51,11 @@ test "handles incoming feed updates", %{conn: conn} do doc = "some stuff" signature = Websub.sign(websub.secret, doc) - conn = conn - |> put_req_header("x-hub-signature", "sha1=" <> signature) - |> put_req_header("content-type", "application/atom+xml") - |> post("/push/subscriptions/#{websub.id}", doc) + conn = + conn + |> put_req_header("x-hub-signature", "sha1=" <> signature) + |> put_req_header("content-type", "application/atom+xml") + |> post("/push/subscriptions/#{websub.id}", doc) assert response(conn, 200) == "OK" @@ -64,10 +67,11 @@ test "rejects incoming feed updates with the wrong signature", %{conn: conn} do doc = "some stuff" signature = Websub.sign("wrong secret", doc) - conn = conn - |> put_req_header("x-hub-signature", "sha1=" <> signature) - |> put_req_header("content-type", "application/atom+xml") - |> post("/push/subscriptions/#{websub.id}", doc) + conn = + conn + |> put_req_header("x-hub-signature", "sha1=" <> signature) + |> put_req_header("content-type", "application/atom+xml") + |> post("/push/subscriptions/#{websub.id}", doc) assert response(conn, 500) == "Error" diff --git a/test/web/websub/websub_test.exs b/test/web/websub/websub_test.exs index 566ce7fa5..5914a37fc 100644 --- a/test/web/websub/websub_test.exs +++ b/test/web/websub/websub_test.exs @@ -15,7 +15,7 @@ test "a verification of a request that is accepted" do sub = insert(:websub_subscription) topic = sub.topic - getter = fn (_path, _headers, options) -> + getter = fn _path, _headers, options -> %{ "hub.challenge": challenge, "hub.lease_seconds": seconds, @@ -25,10 +25,11 @@ test "a verification of a request that is accepted" do assert String.to_integer(seconds) > 0 - {:ok, %HTTPoison.Response{ - status_code: 200, - body: challenge - }} + {:ok, + %HTTPoison.Response{ + status_code: 200, + body: challenge + }} end {:ok, sub} = Websub.verify(sub, getter) @@ -38,11 +39,12 @@ test "a verification of a request that is accepted" do test "a verification of a request that doesn't return 200" do sub = insert(:websub_subscription) - getter = fn (_path, _headers, _options) -> - {:ok, %HTTPoison.Response{ - status_code: 500, - body: "" - }} + getter = fn _path, _headers, _options -> + {:ok, + %HTTPoison.Response{ + status_code: 500, + body: "" + }} end {:error, sub} = Websub.verify(sub, getter) @@ -61,7 +63,7 @@ test "an incoming subscription request" do "hub.lease_seconds" => "100" } - {:ok, subscription } = Websub.incoming_subscription_request(user, data) + {:ok, subscription} = Websub.incoming_subscription_request(user, data) assert subscription.topic == Pleroma.Web.OStatus.feed_path(user) assert subscription.state == "requested" assert subscription.secret == "a random secret" @@ -70,7 +72,9 @@ test "an incoming subscription request" do test "an incoming subscription request for an existing subscription" do user = insert(:user) - sub = insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user)) + + sub = + insert(:websub_subscription, state: "accepted", topic: Pleroma.Web.OStatus.feed_path(user)) data = %{ "hub.callback" => sub.callback, @@ -80,7 +84,7 @@ test "an incoming subscription request for an existing subscription" do "hub.lease_seconds" => "100" } - {:ok, subscription } = Websub.incoming_subscription_request(user, data) + {:ok, subscription} = Websub.incoming_subscription_request(user, data) assert subscription.topic == Pleroma.Web.OStatus.feed_path(user) assert subscription.state == sub.state assert subscription.secret == "a random secret" @@ -90,12 +94,12 @@ test "an incoming subscription request for an existing subscription" do end def accepting_verifier(subscription) do - {:ok, %{ subscription | state: "accepted" }} + {:ok, %{subscription | state: "accepted"}} end test "initiate a subscription for a given user and topic" do subscriber = insert(:user) - user = insert(:user, %{info: %{ "topic" => "some_topic", "hub" => "some_hub"}}) + user = insert(:user, %{info: %{"topic" => "some_topic", "hub" => "some_hub"}}) {:ok, websub} = Websub.subscribe(subscriber, user, &accepting_verifier/1) assert websub.subscribers == [subscriber.ap_id] @@ -109,12 +113,13 @@ test "initiate a subscription for a given user and topic" do test "discovers the hub and canonical url" do topic = "https://mastodon.social/users/lambadalambda.atom" - getter = fn(^topic) -> + getter = fn ^topic -> doc = File.read!("test/fixtures/lambadalambda.atom") {:ok, %{status_code: 200, body: doc}} end {:ok, discovered} = Websub.gather_feed_data(topic, getter) + expected = %{ "hub" => "https://mastodon.social/api/push", "uri" => "https://mastodon.social/users/lambadalambda", @@ -122,7 +127,17 @@ test "discovers the hub and canonical url" do "name" => "Critical Value", "host" => "mastodon.social", "bio" => "a cool dude.", - "avatar" => %{"type" => "Image", "url" => [%{"href" => "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244", "mediaType" => "image/gif", "type" => "Link"}]} + "avatar" => %{ + "type" => "Image", + "url" => [ + %{ + "href" => + "https://files.mastodon.social/accounts/avatars/000/000/264/original/1429214160519.gif?1492379244", + "mediaType" => "image/gif", + "type" => "Link" + } + ] + } } assert expected == discovered @@ -133,9 +148,16 @@ test "calls the hub, requests topic" do topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom" websub = insert(:websub_client_subscription, %{hub: hub, topic: topic}) - poster = fn (^hub, {:form, data}, _headers) -> + poster = fn ^hub, {:form, data}, _headers -> assert Keyword.get(data, :"hub.mode") == "subscribe" - assert Keyword.get(data, :"hub.callback") == Helpers.websub_url(Pleroma.Web.Endpoint, :websub_subscription_confirmation, websub.id) + + assert Keyword.get(data, :"hub.callback") == + Helpers.websub_url( + Pleroma.Web.Endpoint, + :websub_subscription_confirmation, + websub.id + ) + {:ok, %{status_code: 202}} end @@ -154,7 +176,7 @@ test "rejects the subscription if it can't be accepted" do topic = "https://social.heldscal.la/api/statuses/user_timeline/23211.atom" websub = insert(:websub_client_subscription, %{hub: hub, topic: topic}) - poster = fn (^hub, {:form, _data}, _headers) -> + poster = fn ^hub, {:form, _data}, _headers -> {:ok, %{status_code: 202}} end @@ -162,7 +184,8 @@ test "rejects the subscription if it can't be accepted" do assert websub.state == "rejected" websub = insert(:websub_client_subscription, %{hub: hub, topic: topic}) - poster = fn (^hub, {:form, _data}, _headers) -> + + poster = fn ^hub, {:form, _data}, _headers -> {:ok, %{status_code: 400}} end @@ -172,7 +195,7 @@ test "rejects the subscription if it can't be accepted" do test "sign a text" do signed = Websub.sign("secret", "text") - assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase + assert signed == "B8392C23690CCF871F37EC270BE1582DEC57A503" |> String.downcase() _signed = Websub.sign("secret", [["て"], ['す']]) end @@ -180,9 +203,21 @@ test "sign a text" do describe "renewing subscriptions" do test "it renews subscriptions that have less than a day of time left" do day = 60 * 60 * 24 - now = NaiveDateTime.utc_now - still_good = insert(:websub_client_subscription, %{valid_until: NaiveDateTime.add(now, 2 * day), topic: "http://example.org/still_good", state: "accepted"}) - needs_refresh = insert(:websub_client_subscription, %{valid_until: NaiveDateTime.add(now, day - 100), topic: "http://example.org/needs_refresh", state: "accepted"}) + now = NaiveDateTime.utc_now() + + still_good = + insert(:websub_client_subscription, %{ + valid_until: NaiveDateTime.add(now, 2 * day), + topic: "http://example.org/still_good", + state: "accepted" + }) + + needs_refresh = + insert(:websub_client_subscription, %{ + valid_until: NaiveDateTime.add(now, day - 100), + topic: "http://example.org/needs_refresh", + state: "accepted" + }) _refresh = Websub.refresh_subscriptions() diff --git a/test/xml_builder_test.exs b/test/xml_builder_test.exs index f502a0f0e..4be7bbd01 100644 --- a/test/xml_builder_test.exs +++ b/test/xml_builder_test.exs @@ -3,7 +3,7 @@ defmodule Pleroma.XmlBuilderTest do alias Pleroma.XmlBuilder test "Build a basic xml string from a tuple" do - data = { :feed, %{ xmlns: "http://www.w3.org/2005/Atom"}, "Some content" } + data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} expected_xml = "Some content" @@ -11,9 +11,10 @@ test "Build a basic xml string from a tuple" do end test "returns a complete document" do - data = { :feed, %{ xmlns: "http://www.w3.org/2005/Atom"}, "Some content" } + data = {:feed, %{xmlns: "http://www.w3.org/2005/Atom"}, "Some content"} - expected_xml = "Some content" + expected_xml = + "Some content" assert XmlBuilder.to_doc(data) == expected_xml end @@ -34,11 +35,12 @@ test "It works with nested tuples" do :feed, [ {:guy, "brush"}, - {:lament, %{ configuration: "puzzle" }, "pinhead" } + {:lament, %{configuration: "puzzle"}, "pinhead"} ] } - expected_xml = ~s[brushpinhead] + expected_xml = + ~s[brushpinhead] assert XmlBuilder.to_xml(data) == expected_xml end @@ -50,7 +52,7 @@ test "Represents NaiveDateTime as iso8601" do test "Uses self-closing tags when no content is giving" do data = { :link, - %{ rel: "self" } + %{rel: "self"} } expected_xml = ~s[]