Format the code.

This commit is contained in:
lain 2018-03-30 15:01:53 +02:00
parent 480932c8e5
commit 4afbef39f4
111 changed files with 4912 additions and 2769 deletions

View file

@ -6,14 +6,11 @@
use Mix.Config use Mix.Config
# General application configuration # General application configuration
config :pleroma, config :pleroma, ecto_repos: [Pleroma.Repo]
ecto_repos: [Pleroma.Repo]
config :pleroma, Pleroma.Repo, config :pleroma, Pleroma.Repo, types: Pleroma.PostgresTypes
types: Pleroma.PostgresTypes
config :pleroma, Pleroma.Upload, config :pleroma, Pleroma.Upload, uploads: "uploads"
uploads: "uploads"
# Configures the endpoint # Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
@ -21,8 +18,7 @@ config :pleroma, Pleroma.Web.Endpoint,
protocol: "https", protocol: "https",
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl", secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)], render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
pubsub: [name: Pleroma.PubSub, pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2]
adapter: Phoenix.PubSub.PG2]
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, :console, config :logger, :console,
@ -38,15 +34,15 @@ config :pleroma, :websub, Pleroma.Web.Websub
config :pleroma, :ostatus, Pleroma.Web.OStatus config :pleroma, :ostatus, Pleroma.Web.OStatus
config :pleroma, :httpoison, Pleroma.HTTP config :pleroma, :httpoison, Pleroma.HTTP
version = with {version, 0} <- System.cmd("git", ["rev-parse", "HEAD"]) do version =
"Pleroma #{Mix.Project.config[:version]} #{String.trim(version)}" with {version, 0} <- System.cmd("git", ["rev-parse", "HEAD"]) do
"Pleroma #{Mix.Project.config()[:version]} #{String.trim(version)}"
else else
_ -> "Pleroma #{Mix.Project.config[:version]} dev" _ -> "Pleroma #{Mix.Project.config()[:version]} dev"
end end
# Configures http settings, upstream proxy etc. # Configures http settings, upstream proxy etc.
config :pleroma, :http, config :pleroma, :http, proxy_url: nil
proxy_url: nil
config :pleroma, :instance, config :pleroma, :instance,
version: version, version: version,
@ -59,16 +55,15 @@ config :pleroma, :instance,
config :pleroma, :media_proxy, config :pleroma, :media_proxy,
enabled: false, enabled: false,
redirect_on_failure: true redirect_on_failure: true
# base_url: "https://cache.pleroma.social" # base_url: "https://cache.pleroma.social"
config :pleroma, :chat, config :pleroma, :chat, enabled: true
enabled: true
config :ecto, json_library: Jason config :ecto, json_library: Jason
config :phoenix, :format_encoders, config :phoenix, :format_encoders, json: Jason
json: Jason
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env}.exs" import_config "#{Mix.env()}.exs"

View file

@ -7,7 +7,10 @@ use Mix.Config
# watchers to your application. For example, we use it # watchers to your application. For example, we use it
# with brunch.io to recompile .js and .css sources. # with brunch.io to recompile .js and .css sources.
config :pleroma, Pleroma.Web.Endpoint, 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", protocol: "http",
debug_errors: true, debug_errors: true,
code_reloader: true, code_reloader: true,
@ -49,5 +52,8 @@ config :pleroma, Pleroma.Repo,
try do try do
import_config "dev.secret.exs" import_config "dev.secret.exs"
rescue 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 end

View file

@ -9,8 +9,7 @@ config :pleroma, Pleroma.Web.Endpoint,
# Print only warnings and errors during test # Print only warnings and errors during test
config :logger, level: :warn config :logger, level: :warn
config :pleroma, Pleroma.Upload, config :pleroma, Pleroma.Upload, uploads: "test/uploads"
uploads: "test/uploads"
# Configure your database # Configure your database
config :pleroma, Pleroma.Repo, config :pleroma, Pleroma.Repo,
@ -21,7 +20,6 @@ config :pleroma, Pleroma.Repo,
hostname: System.get_env("DB_HOST") || "localhost", hostname: System.get_env("DB_HOST") || "localhost",
pool: Ecto.Adapters.SQL.Sandbox pool: Ecto.Adapters.SQL.Sandbox
# Reduce hash rounds for testing # Reduce hash rounds for testing
config :comeonin, :pbkdf2_rounds, 1 config :comeonin, :pbkdf2_rounds, 1

View file

@ -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
)

View file

@ -8,12 +8,16 @@ defmodule Mix.Tasks.FixApUsers do
def run([]) do def run([]) do
Mix.Task.run("app.start") Mix.Task.run("app.start")
q = from u in User, q =
from(
u in User,
where: fragment("? @> ?", u.info, ^%{"ap_enabled" => true}), where: fragment("? @> ?", u.info, ^%{"ap_enabled" => true}),
where: u.local == false where: u.local == false
)
users = Repo.all(q) users = Repo.all(q)
Enum.each(users, fn(user) -> Enum.each(users, fn user ->
try do try do
IO.puts("Fetching #{user.nickname}") IO.puts("Fetching #{user.nickname}")
Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(user.ap_id, false) Pleroma.Web.ActivityPub.Transmogrifier.upgrade_user_from_ap_id(user.ap_id, false)

View file

@ -5,27 +5,51 @@ defmodule Mix.Tasks.GenerateConfig do
def run(_) do def run(_) do
IO.puts("Answer a few questions to generate a new config\n") IO.puts("Answer a few questions to generate a new config\n")
IO.puts("--- THIS WILL OVERWRITE YOUR config/generated_config.exs! ---\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 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 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 email = IO.gets("What's your admin email address: ") |> String.trim()
mediaproxy = IO.gets("Do you want to activate the mediaproxy? (y/N): ")
mediaproxy =
IO.gets("Do you want to activate the mediaproxy? (y/N): ")
|> String.trim() |> String.trim()
|> String.downcase() |> String.downcase()
|> String.starts_with?("y") |> 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 proxy_url =
if mediaproxy do
IO.gets("What is the mediaproxy's URL? (e.g. https://cache.example.com): ")
|> String.trim()
else else
"https://cache.example.com" "https://cache.example.com"
end 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]) secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
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]) 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) 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) File.write("config/setup_db.psql", resultSql)
end end
end end

View file

@ -9,11 +9,20 @@ defmodule Mix.Tasks.GeneratePasswordReset do
with %User{local: true} = user <- User.get_by_nickname(nickname), with %User{local: true} = user <- User.get_by_nickname(nickname),
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do {:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
IO.puts "Generated password reset token for #{user.nickname}" 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(
"Url: #{
Pleroma.Web.Router.Helpers.util_url(
Pleroma.Web.Endpoint,
:show_password_reset,
token.token
)
}"
)
else else
_ -> _ ->
IO.puts "No local user #{nickname}" IO.puts("No local user #{nickname}")
end end
end end
end end

View file

@ -7,21 +7,24 @@ defmodule Mix.Tasks.SetModerator do
def run([nickname | rest]) do def run([nickname | rest]) do
ensure_started(Repo, []) ensure_started(Repo, [])
moderator = case rest do moderator =
case rest do
[moderator] -> moderator == "true" [moderator] -> moderator == "true"
_ -> true _ -> true
end end
with %User{local: true} = user <- User.get_by_nickname(nickname) do with %User{local: true} = user <- User.get_by_nickname(nickname) do
info = user.info info =
user.info
|> Map.put("is_moderator", !!moderator) |> Map.put("is_moderator", !!moderator)
cng = User.info_changeset(user, %{info: info}) cng = User.info_changeset(user, %{info: info})
user = Repo.update!(cng) 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 else
_ -> _ ->
IO.puts "No local user #{nickname}" IO.puts("No local user #{nickname}")
end end
end end
end end

View file

@ -6,15 +6,15 @@ defmodule Pleroma.PasswordResetToken do
alias Pleroma.{User, PasswordResetToken, Repo} alias Pleroma.{User, PasswordResetToken, Repo}
schema "password_reset_tokens" do schema "password_reset_tokens" do
belongs_to :user, User belongs_to(:user, User)
field :token, :string field(:token, :string)
field :used, :boolean, default: false field(:used, :boolean, default: false)
timestamps() timestamps()
end end
def create_token(%User{} = user) do 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{ token = %PasswordResetToken{
user_id: user.id, user_id: user.id,

View file

@ -4,33 +4,53 @@ defmodule Pleroma.Activity do
import Ecto.Query import Ecto.Query
schema "activities" do schema "activities" do
field :data, :map field(:data, :map)
field :local, :boolean, default: true field(:local, :boolean, default: true)
field :actor, :string field(:actor, :string)
field :recipients, {:array, :string} field(:recipients, {:array, :string})
has_many :notifications, Notification, on_delete: :delete_all has_many(:notifications, Notification, on_delete: :delete_all)
timestamps() timestamps()
end end
def get_by_ap_id(ap_id) do def get_by_ap_id(ap_id) do
Repo.one(from activity in Activity, Repo.one(
where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id))) from(
activity in Activity,
where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id))
)
)
end end
# TODO: # TODO:
# Go through these and fix them everywhere. # Go through these and fix them everywhere.
# Wrong name, only returns create activities # Wrong name, only returns create activities
def all_by_object_ap_id_q(ap_id) do def all_by_object_ap_id_q(ap_id) do
from activity in Activity, from(
where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^to_string(ap_id)), activity in Activity,
where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
activity.data,
activity.data,
^to_string(ap_id)
),
where: fragment("(?)->>'type' = 'Create'", activity.data) where: fragment("(?)->>'type' = 'Create'", activity.data)
)
end end
# Wrong name, returns all. # Wrong name, returns all.
def all_non_create_by_object_ap_id_q(ap_id) do def all_non_create_by_object_ap_id_q(ap_id) do
from activity in Activity, from(
where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^to_string(ap_id)) activity in Activity,
where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
activity.data,
activity.data,
^to_string(ap_id)
)
)
end end
# Wrong name plz fix thx # Wrong name plz fix thx
@ -39,13 +59,21 @@ defmodule Pleroma.Activity do
end end
def create_activity_by_object_id_query(ap_ids) do def create_activity_by_object_id_query(ap_ids) do
from activity in Activity, from(
where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)", activity.data, activity.data, ^ap_ids), activity in Activity,
where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ANY(?)",
activity.data,
activity.data,
^ap_ids
),
where: fragment("(?)->>'type' = 'Create'", activity.data) where: fragment("(?)->>'type' = 'Create'", activity.data)
)
end end
def get_create_activity_by_object_ap_id(ap_id) do def get_create_activity_by_object_ap_id(ap_id) do
create_activity_by_object_id_query([ap_id]) create_activity_by_object_id_query([ap_id])
|> Repo.one |> Repo.one()
end end
end end

View file

@ -7,23 +7,34 @@ defmodule Pleroma.Application do
import Supervisor.Spec import Supervisor.Spec
# Define workers and child supervisors to be supervised # Define workers and child supervisors to be supervised
children = [ children =
[
# Start the Ecto repository # Start the Ecto repository
supervisor(Pleroma.Repo, []), supervisor(Pleroma.Repo, []),
# Start the endpoint when the application starts # Start the endpoint when the application starts
supervisor(Pleroma.Web.Endpoint, []), supervisor(Pleroma.Web.Endpoint, []),
# Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3) # Start your own worker by calling: Pleroma.Worker.start_link(arg1, arg2, arg3)
# worker(Pleroma.Worker, [arg1, arg2, arg3]), # worker(Pleroma.Worker, [arg1, arg2, arg3]),
worker(Cachex, [:user_cache, [ worker(Cachex, [
:user_cache,
[
default_ttl: 25000, default_ttl: 25000,
ttl_interval: 1000, ttl_interval: 1000,
limit: 2500 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, [])] 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 # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
# for other strategies and supported options # for other strategies and supported options

View file

@ -5,19 +5,26 @@ defmodule Pleroma.Formatter do
@tag_regex ~r/\#\w+/u @tag_regex ~r/\#\w+/u
def parse_tags(text, data \\ %{}) do def parse_tags(text, data \\ %{}) do
Regex.scan(@tag_regex, text) Regex.scan(@tag_regex, text)
|> Enum.map(fn (["#" <> tag = full_tag]) -> {full_tag, String.downcase(tag)} 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).() |> (fn map ->
if data["sensitive"] in [true, "True", "true", "1"],
do: [{"#nsfw", "nsfw"}] ++ map,
else: map
end).()
end end
def parse_mentions(text) do def parse_mentions(text) do
# Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address # 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) Regex.scan(regex, text)
|> List.flatten |> List.flatten()
|> Enum.uniq |> Enum.uniq()
|> Enum.map(fn ("@" <> match = full_match) -> {full_match, User.get_cached_by_nickname(match)} end) |> Enum.map(fn "@" <> match = full_match ->
|> Enum.filter(fn ({_match, user}) -> user end) {full_match, User.get_cached_by_nickname(match)}
end)
|> Enum.filter(fn {_match, user} -> user end)
end end
@finmoji [ @finmoji [
@ -86,7 +93,7 @@ defmodule Pleroma.Formatter do
"woollysocks" "woollysocks"
] ]
@finmoji_with_filenames Enum.map(@finmoji, fn (finmoji) -> @finmoji_with_filenames Enum.map(@finmoji, fn finmoji ->
{finmoji, "/finmoji/128px/#{finmoji}-128.png"} {finmoji, "/finmoji/128px/#{finmoji}-128.png"}
end) end)
@ -97,10 +104,11 @@ defmodule Pleroma.Formatter do
else else
_e -> "" _e -> ""
end end
(default <> "\n" <> custom) (default <> "\n" <> custom)
|> String.trim() |> String.trim()
|> String.split(~r/\n+/) |> String.split(~r/\n+/)
|> Enum.map(fn(line) -> |> Enum.map(fn line ->
[name, file] = String.split(line, ~r/,\s*/) [name, file] = String.split(line, ~r/,\s*/)
{name, file} {name, file}
end) end)
@ -112,16 +120,24 @@ defmodule Pleroma.Formatter do
def emojify(text, emoji \\ @emoji) def emojify(text, emoji \\ @emoji)
def emojify(text, nil), do: text def emojify(text, nil), do: text
def emojify(text, emoji) do 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) emoji = HtmlSanitizeEx.strip_tags(emoji)
file = HtmlSanitizeEx.strip_tags(file) file = HtmlSanitizeEx.strip_tags(file)
String.replace(text, ":#{emoji}:", "<img height='32px' width='32px' alt='#{emoji}' title='#{emoji}' src='#{MediaProxy.url(file)}' />")
String.replace(
text,
":#{emoji}:",
"<img height='32px' width='32px' alt='#{emoji}' title='#{emoji}' src='#{
MediaProxy.url(file)
}' />"
)
end) end)
end end
def get_emoji(text) do 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 end
def get_custom_emoji() do def get_custom_emoji() do
@ -141,13 +157,17 @@ defmodule Pleroma.Formatter do
@doc "changes http:... links to html links" @doc "changes http:... links to html links"
def add_links({subs, text}) do def add_links({subs, text}) do
links = Regex.scan(@link_regex, text) links =
|> Enum.map(fn ([url]) -> {Ecto.UUID.generate, url} end) Regex.scan(@link_regex, text)
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end)
uuid_text = links uuid_text =
|> Enum.reduce(text, fn({uuid, url}, acc) -> String.replace(acc, url, uuid) end) links
|> Enum.reduce(text, fn {uuid, url}, acc -> String.replace(acc, url, uuid) end)
subs = subs ++ Enum.map(links, fn({uuid, url}) -> subs =
subs ++
Enum.map(links, fn {uuid, url} ->
{uuid, "<a href='#{url}'>#{url}</a>"} {uuid, "<a href='#{url}'>#{url}</a>"}
end) end)
@ -156,16 +176,20 @@ defmodule Pleroma.Formatter do
@doc "Adds the links to mentioned users" @doc "Adds the links to mentioned users"
def add_user_links({subs, text}, mentions) do def add_user_links({subs, text}, mentions) do
mentions = mentions mentions =
|> Enum.sort_by(fn ({name, _}) -> -String.length(name) end) mentions
|> Enum.map(fn({name, user}) -> {name, user, Ecto.UUID.generate} end) |> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|> Enum.map(fn {name, user} -> {name, user, Ecto.UUID.generate()} end)
uuid_text = mentions uuid_text =
|> Enum.reduce(text, fn ({match, _user, uuid}, text) -> mentions
|> Enum.reduce(text, fn {match, _user, uuid}, text ->
String.replace(text, match, uuid) String.replace(text, match, uuid)
end) end)
subs = subs ++ Enum.map(mentions, fn ({match, %User{ap_id: ap_id}, uuid}) -> subs =
subs ++
Enum.map(mentions, fn {match, %User{ap_id: ap_id}, uuid} ->
short_match = String.split(match, "@") |> tl() |> hd() short_match = String.split(match, "@") |> tl() |> hd()
{uuid, "<span><a href='#{ap_id}'>@<span>#{short_match}</span></a></span>"} {uuid, "<span><a href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
end) end)
@ -175,17 +199,21 @@ defmodule Pleroma.Formatter do
@doc "Adds the hashtag links" @doc "Adds the hashtag links"
def add_hashtag_links({subs, text}, tags) do def add_hashtag_links({subs, text}, tags) do
tags = tags tags =
|> Enum.sort_by(fn ({name, _}) -> -String.length(name) end) tags
|> Enum.map(fn({name, short}) -> {name, short, Ecto.UUID.generate} end) |> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|> Enum.map(fn {name, short} -> {name, short, Ecto.UUID.generate()} end)
uuid_text = tags uuid_text =
|> Enum.reduce(text, fn ({match, _short, uuid}, text) -> tags
|> Enum.reduce(text, fn {match, _short, uuid}, text ->
String.replace(text, match, uuid) String.replace(text, match, uuid)
end) end)
subs = subs ++ Enum.map(tags, fn ({_, tag, uuid}) -> subs =
url = "<a href='#{Pleroma.Web.base_url}/tag/#{tag}' rel='tag'>##{tag}</a>" subs ++
Enum.map(tags, fn {_, tag, uuid} ->
url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>##{tag}</a>"
{uuid, url} {uuid, url}
end) end)
@ -193,7 +221,7 @@ defmodule Pleroma.Formatter do
end end
def finalize({subs, text}) do 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) String.replace(result_text, uuid, replacement)
end) end)
end end

View file

@ -1,14 +1,13 @@
defmodule Pleroma.HTTP do defmodule Pleroma.HTTP do
use HTTPoison.Base use HTTPoison.Base
def process_request_options(options) do def process_request_options(options) do
config = Application.get_env(:pleroma, :http, []) config = Application.get_env(:pleroma, :http, [])
proxy = Keyword.get(config, :proxy_url, nil) proxy = Keyword.get(config, :proxy_url, nil)
case proxy do case proxy do
nil -> options nil -> options
_ -> options ++ [proxy: proxy] _ -> options ++ [proxy: proxy]
end end
end end
end end

View file

@ -4,32 +4,38 @@ defmodule Pleroma.Notification do
import Ecto.Query import Ecto.Query
schema "notifications" do schema "notifications" do
field :seen, :boolean, default: false field(:seen, :boolean, default: false)
belongs_to :user, Pleroma.User belongs_to(:user, Pleroma.User)
belongs_to :activity, Pleroma.Activity belongs_to(:activity, Pleroma.Activity)
timestamps() timestamps()
end end
# TODO: Make generic and unify (see activity_pub.ex) # TODO: Make generic and unify (see activity_pub.ex)
defp restrict_max(query, %{"max_id" => max_id}) do 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 end
defp restrict_max(query, _), do: query defp restrict_max(query, _), do: query
defp restrict_since(query, %{"since_id" => since_id}) do 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 end
defp restrict_since(query, _), do: query defp restrict_since(query, _), do: query
def for_user(user, opts \\ %{}) do def for_user(user, opts \\ %{}) do
query = from n in Notification, query =
from(
n in Notification,
where: n.user_id == ^user.id, where: n.user_id == ^user.id,
order_by: [desc: n.id], order_by: [desc: n.id],
preload: [:activity], preload: [:activity],
limit: 20 limit: 20
)
query = query query =
query
|> restrict_since(opts) |> restrict_since(opts)
|> restrict_max(opts) |> restrict_max(opts)
@ -37,42 +43,50 @@ defmodule Pleroma.Notification do
end end
def get(%{id: user_id} = _user, id) do def get(%{id: user_id} = _user, id) do
query = from n in Notification, query =
from(
n in Notification,
where: n.id == ^id, where: n.id == ^id,
preload: [:activity] preload: [:activity]
)
notification = Repo.one(query) notification = Repo.one(query)
case notification do case notification do
%{user_id: ^user_id} -> %{user_id: ^user_id} ->
{:ok, notification} {:ok, notification}
_ -> _ ->
{:error, "Cannot get notification"} {:error, "Cannot get notification"}
end end
end end
def clear(user) do def clear(user) do
query = from n in Notification, query = from(n in Notification, where: n.user_id == ^user.id)
where: n.user_id == ^user.id
Repo.delete_all(query) Repo.delete_all(query)
end end
def dismiss(%{id: user_id} = _user, id) do def dismiss(%{id: user_id} = _user, id) do
notification = Repo.get(Notification, id) notification = Repo.get(Notification, id)
case notification do case notification do
%{user_id: ^user_id} -> %{user_id: ^user_id} ->
Repo.delete(notification) Repo.delete(notification)
_ -> _ ->
{:error, "Cannot dismiss notification"} {:error, "Cannot dismiss notification"}
end end
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) 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} {:ok, notifications}
end end
def create_notifications(_), do: {:ok, []} def create_notifications(_), do: {:ok, []}
# TODO move to sql, too. # TODO move to sql, too.
@ -85,4 +99,3 @@ defmodule Pleroma.Notification do
end end
end end
end end

View file

@ -4,14 +4,14 @@ defmodule Pleroma.Object do
import Ecto.{Query, Changeset} import Ecto.{Query, Changeset}
schema "objects" do schema "objects" do
field :data, :map field(:data, :map)
timestamps() timestamps()
end end
def create(data) do def create(data) do
Object.change(%Object{}, %{data: data}) Object.change(%Object{}, %{data: data})
|> Repo.insert |> Repo.insert()
end end
def change(struct, params \\ %{}) do def change(struct, params \\ %{}) do
@ -22,24 +22,30 @@ defmodule Pleroma.Object do
end end
def get_by_ap_id(nil), do: nil def get_by_ap_id(nil), do: nil
def get_by_ap_id(ap_id) do def get_by_ap_id(ap_id) do
Repo.one(from object in Object, Repo.one(from(object in Object, where: fragment("(?)->>'id' = ?", object.data, ^ap_id)))
where: fragment("(?)->>'id' = ?", object.data, ^ap_id))
end end
def get_cached_by_ap_id(ap_id) do 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) get_by_ap_id(ap_id)
else else
key = "object:#{ap_id}" key = "object:#{ap_id}"
Cachex.get!(:user_cache, key, fallback: fn(_) ->
Cachex.get!(
:user_cache,
key,
fallback: fn _ ->
object = get_by_ap_id(ap_id) object = get_by_ap_id(ap_id)
if object do if object do
{:commit, object} {:commit, object}
else else
{:ignore, object} {:ignore, object}
end end
end) end
)
end end
end end

View file

@ -14,8 +14,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
{:ok, user} <- opts[:fetcher].(username), {:ok, user} <- opts[:fetcher].(username),
false <- !!user.info["deactivated"], false <- !!user.info["deactivated"],
saved_user_id <- get_session(conn, :user_id), saved_user_id <- get_session(conn, :user_id),
{:ok, verified_user} <- verify(user, password, saved_user_id) {:ok, verified_user} <- verify(user, password, saved_user_id) do
do
conn conn
|> assign(:user, verified_user) |> assign(:user, verified_user)
|> put_session(:user_id, verified_user.id) |> put_session(:user_id, verified_user.id)
@ -30,7 +29,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
end end
defp verify(nil, _password, _user_id) do defp verify(nil, _password, _user_id) do
Pbkdf2.dummy_checkpw Pbkdf2.dummy_checkpw()
:error :error
end end
@ -45,8 +44,7 @@ defmodule Pleroma.Plugs.AuthenticationPlug do
defp decode_header(conn) do defp decode_header(conn) do
with ["Basic " <> header] <- get_req_header(conn, "authorization"), with ["Basic " <> header] <- get_req_header(conn, "authorization"),
{:ok, userinfo} <- Base.decode64(header), {:ok, userinfo} <- Base.decode64(header),
[username, password] <- String.split(userinfo, ":", parts: 2) [username, password] <- String.split(userinfo, ":", parts: 2) do
do
{:ok, username, password} {:ok, username, password}
end end
end end

View file

@ -9,11 +9,14 @@ defmodule Pleroma.Plugs.OAuthPlug do
end end
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
def call(conn, _) do def call(conn, _) do
token = case get_req_header(conn, "authorization") do token =
case get_req_header(conn, "authorization") do
["Bearer " <> header] -> header ["Bearer " <> header] -> header
_ -> get_session(conn, :oauth_token) _ -> get_session(conn, :oauth_token)
end end
with token when not is_nil(token) <- token, with token when not is_nil(token) <- token,
%Token{user_id: user_id} <- Repo.get_by(Token, token: token), %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id), %User{} = user <- Repo.get(User, user_id),

View file

@ -18,22 +18,31 @@ defmodule Pleroma.Stats do
def schedule_update do def schedule_update do
spawn(fn -> spawn(fn ->
Process.sleep(1000 * 60 * 60 * 1) # 1 hour # 1 hour
Process.sleep(1000 * 60 * 60 * 1)
schedule_update() schedule_update()
end) end)
update_stats() update_stats()
end end
def update_stats do def update_stats do
peers = from(u in Pleroma.User, peers =
from(
u in Pleroma.User,
select: fragment("distinct ?->'host'", u.info), select: fragment("distinct ?->'host'", u.info),
where: u.local != ^true) where: u.local != ^true
)
|> Repo.all() |> Repo.all()
domain_count = Enum.count(peers) 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) 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 _ -> Agent.update(__MODULE__, fn _ ->
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} {peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}
end) end)

View file

@ -1,15 +1,17 @@
defmodule Pleroma.Upload do defmodule Pleroma.Upload do
alias Ecto.UUID alias Ecto.UUID
alias Pleroma.Web alias Pleroma.Web
def store(%Plug.Upload{} = file) do def store(%Plug.Upload{} = file) do
uuid = UUID.generate uuid = UUID.generate()
upload_folder = Path.join(upload_path(), uuid) upload_folder = Path.join(upload_path(), uuid)
File.mkdir_p!(upload_folder) File.mkdir_p!(upload_folder)
result_file = Path.join(upload_folder, file.filename) result_file = Path.join(upload_folder, file.filename)
File.cp!(file.path, result_file) File.cp!(file.path, result_file)
# fix content type on some image uploads # fix content type on some image uploads
content_type = if file.content_type in [nil, "application/octet-stream"] do content_type =
if file.content_type in [nil, "application/octet-stream"] do
get_content_type(file.path) get_content_type(file.path)
else else
file.content_type file.content_type
@ -17,11 +19,13 @@ defmodule Pleroma.Upload do
%{ %{
"type" => "Image", "type" => "Image",
"url" => [%{ "url" => [
%{
"type" => "Link", "type" => "Link",
"mediaType" => content_type, "mediaType" => content_type,
"href" => url_for(Path.join(uuid, :cow_uri.urlencode(file.filename))) "href" => url_for(Path.join(uuid, :cow_uri.urlencode(file.filename)))
}], }
],
"name" => file.filename, "name" => file.filename,
"uuid" => uuid "uuid" => uuid
} }
@ -30,7 +34,7 @@ defmodule Pleroma.Upload do
def store(%{"img" => "data:image/" <> image_data}) do def store(%{"img" => "data:image/" <> image_data}) do
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data) parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
data = Base.decode64!(parsed["data"]) data = Base.decode64!(parsed["data"])
uuid = UUID.generate uuid = UUID.generate()
upload_folder = Path.join(upload_path(), uuid) upload_folder = Path.join(upload_path(), uuid)
File.mkdir_p!(upload_folder) File.mkdir_p!(upload_folder)
filename = Base.encode16(:crypto.hash(:sha256, data)) <> ".#{parsed["filetype"]}" filename = Base.encode16(:crypto.hash(:sha256, data)) <> ".#{parsed["filetype"]}"
@ -42,11 +46,13 @@ defmodule Pleroma.Upload do
%{ %{
"type" => "Image", "type" => "Image",
"url" => [%{ "url" => [
%{
"type" => "Link", "type" => "Link",
"mediaType" => content_type, "mediaType" => content_type,
"href" => url_for(Path.join(uuid, :cow_uri.urlencode(filename))) "href" => url_for(Path.join(uuid, :cow_uri.urlencode(filename)))
}], }
],
"name" => filename, "name" => filename,
"uuid" => uuid "uuid" => uuid
} }
@ -62,24 +68,33 @@ defmodule Pleroma.Upload do
end end
def get_content_type(file) do def get_content_type(file) do
match = File.open(file, [:read], fn(f) -> match =
File.open(file, [:read], fn f ->
case IO.binread(f, 8) do case IO.binread(f, 8) do
<<0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a>> -> <<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A>> ->
"image/png" "image/png"
<<0x47, 0x49, 0x46, 0x38, _, 0x61, _, _>> -> <<0x47, 0x49, 0x46, 0x38, _, 0x61, _, _>> ->
"image/gif" "image/gif"
<<0xff, 0xd8, 0xff, _, _, _, _, _>> ->
<<0xFF, 0xD8, 0xFF, _, _, _, _, _>> ->
"image/jpeg" "image/jpeg"
<<0x1a, 0x45, 0xdf, 0xa3, _, _, _, _>> ->
<<0x1A, 0x45, 0xDF, 0xA3, _, _, _, _>> ->
"video/webm" "video/webm"
<<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70>> -> <<0x00, 0x00, 0x00, _, 0x66, 0x74, 0x79, 0x70>> ->
"video/mp4" "video/mp4"
<<0x49, 0x44, 0x33, _, _, _, _, _>> -> <<0x49, 0x44, 0x33, _, _, _, _, _>> ->
"audio/mpeg" "audio/mpeg"
<<0x4f, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> ->
<<0x4F, 0x67, 0x67, 0x53, 0x00, 0x02, 0x00, 0x00>> ->
"audio/ogg" "audio/ogg"
<<0x52, 0x49, 0x46, 0x46, _, _, _, _>> -> <<0x52, 0x49, 0x46, 0x46, _, _, _, _>> ->
"audio/wav" "audio/wav"
_ -> _ ->
"application/octet-stream" "application/octet-stream"
end end

View file

@ -8,20 +8,20 @@ defmodule Pleroma.User do
alias Pleroma.Web.ActivityPub.{Utils, ActivityPub} alias Pleroma.Web.ActivityPub.{Utils, ActivityPub}
schema "users" do schema "users" do
field :bio, :string field(:bio, :string)
field :email, :string field(:email, :string)
field :name, :string field(:name, :string)
field :nickname, :string field(:nickname, :string)
field :password_hash, :string field(:password_hash, :string)
field :password, :string, virtual: true field(:password, :string, virtual: true)
field :password_confirmation, :string, virtual: true field(:password_confirmation, :string, virtual: true)
field :following, {:array, :string}, default: [] field(:following, {:array, :string}, default: [])
field :ap_id, :string field(:ap_id, :string)
field :avatar, :map field(:avatar, :map)
field :local, :boolean, default: true field(:local, :boolean, default: true)
field :info, :map, default: %{} field(:info, :map, default: %{})
field :follower_address, :string field(:follower_address, :string)
has_many :notifications, Notification has_many(:notifications, Notification)
timestamps() timestamps()
end end
@ -41,7 +41,7 @@ defmodule Pleroma.User do
end end
def ap_id(%User{nickname: nickname}) do def ap_id(%User{nickname: nickname}) do
"#{Web.base_url}/users/#{nickname}" "#{Web.base_url()}/users/#{nickname}"
end end
def ap_followers(%User{} = user) do def ap_followers(%User{} = user) do
@ -62,6 +62,7 @@ defmodule Pleroma.User do
def user_info(%User{} = user) do def user_info(%User{} = user) do
oneself = if user.local, do: 1, else: 0 oneself = if user.local, do: 1, else: 0
%{ %{
following_count: length(user.following) - oneself, following_count: length(user.following) - oneself,
note_count: user.info["note_count"] || 0, note_count: user.info["note_count"] || 0,
@ -71,7 +72,8 @@ defmodule Pleroma.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])?)*$/ @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 def remote_user_creation(params) do
changes = %User{} changes =
%User{}
|> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar]) |> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar])
|> validate_required([:name, :ap_id, :nickname]) |> validate_required([:name, :ap_id, :nickname])
|> unique_constraint(:nickname) |> unique_constraint(:nickname)
@ -79,13 +81,16 @@ defmodule Pleroma.User do
|> validate_length(:bio, max: 5000) |> validate_length(:bio, max: 5000)
|> validate_length(:name, max: 100) |> validate_length(:name, max: 100)
|> put_change(:local, false) |> put_change(:local, false)
if changes.valid? do if changes.valid? do
case changes.changes[:info]["source_data"] do case changes.changes[:info]["source_data"] do
%{"followers" => followers} -> %{"followers" => followers} ->
changes changes
|> put_change(:follower_address, followers) |> put_change(:follower_address, followers)
_ -> _ ->
followers = User.ap_followers(%User{nickname: changes.changes[:nickname]}) followers = User.ap_followers(%User{nickname: changes.changes[:nickname]})
changes changes
|> put_change(:follower_address, followers) |> put_change(:follower_address, followers)
end end
@ -113,13 +118,15 @@ defmodule Pleroma.User do
end end
def password_update_changeset(struct, params) do def password_update_changeset(struct, params) do
changeset = struct changeset =
struct
|> cast(params, [:password, :password_confirmation]) |> cast(params, [:password, :password_confirmation])
|> validate_required([:password, :password_confirmation]) |> validate_required([:password, :password_confirmation])
|> validate_confirmation(:password) |> validate_confirmation(:password)
if changeset.valid? do if changeset.valid? do
hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
changeset changeset
|> put_change(:password_hash, hashed) |> put_change(:password_hash, hashed)
else else
@ -132,7 +139,8 @@ defmodule Pleroma.User do
end end
def register_changeset(struct, params \\ %{}) do def register_changeset(struct, params \\ %{}) do
changeset = struct changeset =
struct
|> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation]) |> cast(params, [:bio, :email, :name, :nickname, :password, :password_confirmation])
|> validate_required([:email, :name, :nickname, :password, :password_confirmation]) |> validate_required([:email, :name, :nickname, :password, :password_confirmation])
|> validate_confirmation(:password) |> validate_confirmation(:password)
@ -147,6 +155,7 @@ defmodule Pleroma.User do
hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) hashed = Pbkdf2.hashpwsalt(changeset.changes[:password])
ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]}) ap_id = User.ap_id(%User{nickname: changeset.changes[:nickname]})
followers = User.ap_followers(%User{nickname: changeset.changes[:nickname]}) followers = User.ap_followers(%User{nickname: changeset.changes[:nickname]})
changeset changeset
|> put_change(:password_hash, hashed) |> put_change(:password_hash, hashed)
|> put_change(:ap_id, ap_id) |> put_change(:ap_id, ap_id)
@ -161,17 +170,18 @@ defmodule Pleroma.User do
ap_followers = followed.follower_address ap_followers = followed.follower_address
if following?(follower, followed) or info["deactivated"] do if following?(follower, followed) or info["deactivated"] do
{:error, {:error, "Could not follow user: #{followed.nickname} is already on your list."}
"Could not follow user: #{followed.nickname} is already on your list."}
else else
if !followed.local && follower.local && !ap_enabled?(followed) do if !followed.local && follower.local && !ap_enabled?(followed) do
Websub.subscribe(follower, followed) Websub.subscribe(follower, followed)
end end
following = [ap_followers | follower.following] following =
|> Enum.uniq [ap_followers | follower.following]
|> Enum.uniq()
follower = follower follower =
follower
|> follow_changeset(%{following: following}) |> follow_changeset(%{following: following})
|> update_and_set_cache |> update_and_set_cache
@ -183,11 +193,14 @@ defmodule Pleroma.User do
def unfollow(%User{} = follower, %User{} = followed) do def unfollow(%User{} = follower, %User{} = followed) do
ap_followers = followed.follower_address ap_followers = followed.follower_address
if following?(follower, followed) and follower.ap_id != followed.ap_id do if following?(follower, followed) and follower.ap_id != followed.ap_id do
following = follower.following following =
follower.following
|> List.delete(ap_followers) |> List.delete(ap_followers)
{ :ok, follower } = follower {:ok, follower} =
follower
|> follow_changeset(%{following: following}) |> follow_changeset(%{following: following})
|> update_and_set_cache |> update_and_set_cache
@ -225,12 +238,12 @@ defmodule Pleroma.User do
def get_cached_by_ap_id(ap_id) do def get_cached_by_ap_id(ap_id) do
key = "ap_id:#{ap_id}" 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 end
def get_cached_by_nickname(nickname) do def get_cached_by_nickname(nickname) do
key = "nickname:#{nickname}" 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 end
def get_by_nickname(nickname) do def get_by_nickname(nickname) do
@ -239,7 +252,7 @@ defmodule Pleroma.User do
def get_cached_user_info(user) do def get_cached_user_info(user) do
key = "user_info:#{user.id}" 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 end
def fetch_by_nickname(nickname) do def fetch_by_nickname(nickname) do
@ -254,27 +267,35 @@ defmodule Pleroma.User do
def get_or_fetch_by_nickname(nickname) do 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 user
else _e -> else
_e ->
with [_nick, _domain] <- String.split(nickname, "@"), with [_nick, _domain] <- String.split(nickname, "@"),
{:ok, user} <- fetch_by_nickname(nickname) do {:ok, user} <- fetch_by_nickname(nickname) do
user user
else _e -> nil else
_e -> nil
end end
end end
end end
def get_followers(%User{id: id, follower_address: follower_address}) do def get_followers(%User{id: id, follower_address: follower_address}) do
q = from u in User, q =
from(
u in User,
where: fragment("? <@ ?", ^[follower_address], u.following), where: fragment("? <@ ?", ^[follower_address], u.following),
where: u.id != ^id where: u.id != ^id
)
{:ok, Repo.all(q)} {:ok, Repo.all(q)}
end end
def get_friends(%User{id: id, following: following}) do def get_friends(%User{id: id, following: following}) do
q = from u in User, q =
from(
u in User,
where: u.follower_address in ^following, where: u.follower_address in ^following,
where: u.id != ^id where: u.id != ^id
)
{:ok, Repo.all(q)} {:ok, Repo.all(q)}
end end
@ -289,9 +310,12 @@ defmodule Pleroma.User do
end end
def update_note_count(%User{} = user) do def update_note_count(%User{} = user) do
note_count_query = from a in Object, note_count_query =
from(
a in Object,
where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data), where: fragment("?->>'actor' = ? and ?->>'type' = 'Note'", a.data, ^user.ap_id, a.data),
select: count(a.id) select: count(a.id)
)
note_count = Repo.one(note_count_query) note_count = Repo.one(note_count_query)
@ -303,10 +327,13 @@ defmodule Pleroma.User do
end end
def update_follower_count(%User{} = user) do def update_follower_count(%User{} = user) do
follower_count_query = from u in User, follower_count_query =
from(
u in User,
where: ^user.follower_address in u.following, where: ^user.follower_address in u.following,
where: u.id != ^user.id, where: u.id != ^user.id,
select: count(u.id) select: count(u.id)
)
follower_count = Repo.one(follower_count_query) follower_count = Repo.one(follower_count_query)
@ -318,20 +345,25 @@ defmodule Pleroma.User do
end end
def get_notified_from_activity(%Activity{recipients: to}) do def get_notified_from_activity(%Activity{recipients: to}) do
query = from u in User, query =
from(
u in User,
where: u.ap_id in ^to, where: u.ap_id in ^to,
where: u.local == true where: u.local == true
)
Repo.all(query) Repo.all(query)
end end
def get_recipients_from_activity(%Activity{recipients: to}) do def get_recipients_from_activity(%Activity{recipients: to}) do
query = from u in User, query =
from(
u in User,
where: u.ap_id in ^to, where: u.ap_id in ^to,
or_where: fragment("? && ?", u.following, ^to) or_where: fragment("? && ?", u.following, ^to)
)
query = from u in query, query = from(u in query, where: u.local == true)
where: u.local == true
Repo.all(query) Repo.all(query)
end end
@ -340,9 +372,20 @@ defmodule Pleroma.User do
if resolve do if resolve do
User.get_or_fetch_by_nickname(query) User.get_or_fetch_by_nickname(query)
end end
q = from u in User,
where: fragment("(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)", u.nickname, u.name, ^query), q =
from(
u in User,
where:
fragment(
"(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)",
u.nickname,
u.name,
^query
),
limit: 20 limit: 20
)
Repo.all(q) Repo.all(q)
end end
@ -370,8 +413,7 @@ defmodule Pleroma.User do
end end
def local_user_query() do def local_user_query() do
from u in User, from(u in User, where: u.local == true)
where: u.local == true
end end
def deactivate(%User{} = user) do def deactivate(%User{} = user) do
@ -385,21 +427,26 @@ defmodule Pleroma.User do
# Remove all relationships # Remove all relationships
{:ok, followers} = User.get_followers(user) {:ok, followers} = User.get_followers(user)
followers 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) {:ok, friends} = User.get_friends(user)
friends
|> Enum.each(fn (followed) -> User.unfollow(user, followed) end)
query = from a in Activity, friends
where: a.actor == ^user.ap_id |> Enum.each(fn followed -> User.unfollow(user, followed) end)
query = from(a in Activity, where: a.actor == ^user.ap_id)
Repo.all(query) Repo.all(query)
|> Enum.each(fn (activity) -> |> Enum.each(fn activity ->
case activity.data["type"] do case activity.data["type"] do
"Create" -> ActivityPub.delete(Object.get_by_ap_id(activity.data["object"]["id"])) "Create" ->
_ -> "Doing nothing" # TODO: Do something with likes, follows, repeats. ActivityPub.delete(Object.get_by_ap_id(activity.data["object"]["id"]))
# TODO: Do something with likes, follows, repeats.
_ ->
"Doing nothing"
end end
end) end)
@ -413,7 +460,9 @@ defmodule Pleroma.User do
ap_try = ActivityPub.make_user_from_ap_id(ap_id) ap_try = ActivityPub.make_user_from_ap_id(ap_id)
case ap_try do case ap_try do
{:ok, user} -> user {:ok, user} ->
user
_ -> _ ->
case OStatus.make_user(ap_id) do case OStatus.make_user(ap_id) do
{:ok, user} -> user {:ok, user} -> user
@ -424,8 +473,11 @@ defmodule Pleroma.User do
end end
# AP style # AP style
def public_key_from_info(%{"source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}}}) do def public_key_from_info(%{
key = :public_key.pem_decode(public_key_pem) "source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}}
}) do
key =
:public_key.pem_decode(public_key_pem)
|> hd() |> hd()
|> :public_key.pem_entry_decode() |> :public_key.pem_entry_decode()
@ -450,8 +502,10 @@ defmodule Pleroma.User do
defp blank?(n), do: n defp blank?(n), do: n
def insert_or_update_user(data) do def insert_or_update_user(data) do
data = data data =
data
|> Map.put(:name, blank?(data[:name]) || data[:nickname]) |> Map.put(:name, blank?(data[:name]) || data[:nickname])
cs = User.remote_user_creation(data) cs = User.remote_user_creation(data)
Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname) Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname)
end end

View file

@ -18,7 +18,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
with nil <- Activity.get_by_ap_id(map["id"]), with nil <- Activity.get_by_ap_id(map["id"]),
map <- lazy_put_activity_defaults(map), map <- lazy_put_activity_defaults(map),
:ok <- insert_full_object(map) do :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) Notification.create_notifications(activity)
stream_out(activity) stream_out(activity)
{:ok, activity} {:ok, activity}
@ -31,8 +38,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def stream_out(activity) do def stream_out(activity) do
if activity.data["type"] in ["Create", "Announce"] do if activity.data["type"] in ["Create", "Announce"] do
Pleroma.Web.Streamer.stream("user", activity) Pleroma.Web.Streamer.stream("user", activity)
if Enum.member?(activity.data["to"], "https://www.w3.org/ns/activitystreams#Public") do if Enum.member?(activity.data["to"], "https://www.w3.org/ns/activitystreams#Public") do
Pleroma.Web.Streamer.stream("public", activity) Pleroma.Web.Streamer.stream("public", activity)
if activity.local do if activity.local do
Pleroma.Web.Streamer.stream("public:local", activity) Pleroma.Web.Streamer.stream("public:local", activity)
end end
@ -42,10 +51,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def create(%{to: to, actor: actor, context: context, object: object} = params) do def create(%{to: to, actor: actor, context: context, object: object} = params) do
additional = params[:additional] || %{} 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] 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, activity} <- insert(create_data, local),
:ok <- maybe_federate(activity) do :ok <- maybe_federate(activity) do
{:ok, activity} {:ok, activity}
@ -53,7 +67,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def accept(%{to: to, actor: actor, object: object} = params) do 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}, with data <- %{"to" => to, "type" => "Accept", "actor" => actor, "object" => object},
{:ok, activity} <- insert(data, local), {:ok, activity} <- insert(data, local),
@ -63,9 +78,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def update(%{to: to, cc: cc, actor: actor, object: object} = params) do 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, activity} <- insert(data, local),
:ok <- maybe_federate(activity) do :ok <- maybe_federate(activity) do
{:ok, activity} {:ok, activity}
@ -73,7 +95,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
# TODO: This is weird, maybe we shouldn't check here if we can make the activity. # 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), with nil <- get_existing_like(ap_id, object),
like_data <- make_like_data(user, object, activity_id), like_data <- make_like_data(user, object, activity_id),
{:ok, activity} <- insert(like_data, local), {:ok, activity} <- insert(like_data, local),
@ -91,11 +118,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, _activity} <- Repo.delete(activity), {:ok, _activity} <- Repo.delete(activity),
{:ok, object} <- remove_like_from_object(activity, object) do {:ok, object} <- remove_like_from_object(activity, object) do
{:ok, object} {:ok, object}
else _e -> {:ok, object} else
_e -> {:ok, object}
end end
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), with true <- is_public?(object),
announce_data <- make_announce_data(user, object, activity_id), announce_data <- make_announce_data(user, object, activity_id),
{:ok, activity} <- insert(announce_data, local), {:ok, activity} <- insert(announce_data, local),
@ -119,19 +152,22 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed), with %Activity{} = follow_activity <- fetch_latest_follow(follower, followed),
unfollow_data <- make_unfollow_data(follower, followed, follow_activity), unfollow_data <- make_unfollow_data(follower, followed, follow_activity),
{:ok, activity} <- insert(unfollow_data, local), {:ok, activity} <- insert(unfollow_data, local),
:ok, maybe_federate(activity) do :ok,
maybe_federate(activity) do
{:ok, activity} {:ok, activity}
end end
end end
def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ true) do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
data = %{ data = %{
"type" => "Delete", "type" => "Delete",
"actor" => actor, "actor" => actor,
"object" => id, "object" => id,
"to" => [user.follower_address, "https://www.w3.org/ns/activitystreams#Public"] "to" => [user.follower_address, "https://www.w3.org/ns/activitystreams#Public"]
} }
with Repo.delete(object), with Repo.delete(object),
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)), Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
{:ok, activity} <- insert(data, local), {:ok, activity} <- insert(data, local),
@ -142,112 +178,147 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_activities_for_context(context, opts \\ %{}) do def fetch_activities_for_context(context, opts \\ %{}) do
public = ["https://www.w3.org/ns/activitystreams#Public"] 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 recipients =
query = query 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_blocked(opts)
|> restrict_recipients(recipients, opts["user"]) |> restrict_recipients(recipients, opts["user"])
query = from activity in query, query =
where: fragment("?->>'type' = ? and ?->>'context' = ?", activity.data, "Create", activity.data, ^context), from(
activity in query,
where:
fragment(
"?->>'type' = ? and ?->>'context' = ?",
activity.data,
"Create",
activity.data,
^context
),
order_by: [desc: :id] order_by: [desc: :id]
)
Repo.all(query) Repo.all(query)
end end
# TODO: Make this work properly with unlisted. # TODO: Make this work properly with unlisted.
def fetch_public_activities(opts \\ %{}) do def fetch_public_activities(opts \\ %{}) do
q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts) q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
q q
|> Repo.all |> Repo.all()
|> Enum.reverse |> Enum.reverse()
end end
defp restrict_since(query, %{"since_id" => since_id}) do 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 end
defp restrict_since(query, _), do: query defp restrict_since(query, _), do: query
defp restrict_tag(query, %{"tag" => tag}) do defp restrict_tag(query, %{"tag" => tag}) do
from activity in query, from(
activity in query,
where: fragment("? <@ (? #> '{\"object\",\"tag\"}')", ^tag, activity.data) where: fragment("? <@ (? #> '{\"object\",\"tag\"}')", ^tag, activity.data)
)
end end
defp restrict_tag(query, _), do: query defp restrict_tag(query, _), do: query
defp restrict_recipients(query, [], user), do: query defp restrict_recipients(query, [], user), do: query
defp restrict_recipients(query, recipients, nil) do defp restrict_recipients(query, recipients, nil) do
from activity in query, from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
where: fragment("? && ?", ^recipients, activity.recipients)
end end
defp restrict_recipients(query, recipients, user) do defp restrict_recipients(query, recipients, user) do
from activity in query, from(
activity in query,
where: fragment("? && ?", ^recipients, activity.recipients), where: fragment("? && ?", ^recipients, activity.recipients),
or_where: activity.actor == ^user.ap_id or_where: activity.actor == ^user.ap_id
)
end end
defp restrict_limit(query, %{"limit" => limit}) do defp restrict_limit(query, %{"limit" => limit}) do
from activity in query, from(activity in query, limit: ^limit)
limit: ^limit
end end
defp restrict_limit(query, _), do: query defp restrict_limit(query, _), do: query
defp restrict_local(query, %{"local_only" => true}) do 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 end
defp restrict_local(query, _), do: query defp restrict_local(query, _), do: query
defp restrict_max(query, %{"max_id" => max_id}) do 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 end
defp restrict_max(query, _), do: query defp restrict_max(query, _), do: query
defp restrict_actor(query, %{"actor_id" => actor_id}) do defp restrict_actor(query, %{"actor_id" => actor_id}) do
from activity in query, from(activity in query, where: activity.actor == ^actor_id)
where: activity.actor == ^actor_id
end end
defp restrict_actor(query, _), do: query defp restrict_actor(query, _), do: query
defp restrict_type(query, %{"type" => type}) when is_binary(type) do defp restrict_type(query, %{"type" => type}) when is_binary(type) do
restrict_type(query, %{"type" => [type]}) restrict_type(query, %{"type" => [type]})
end end
defp restrict_type(query, %{"type" => type}) do defp restrict_type(query, %{"type" => type}) do
from activity in query, from(activity in query, where: fragment("?->>'type' = ANY(?)", activity.data, ^type))
where: fragment("?->>'type' = ANY(?)", activity.data, ^type)
end end
defp restrict_type(query, _), do: query defp restrict_type(query, _), do: query
defp restrict_favorited_by(query, %{"favorited_by" => ap_id}) do 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) where: fragment("? <@ (? #> '{\"object\",\"likes\"}')", ^ap_id, activity.data)
)
end end
defp restrict_favorited_by(query, _), do: query defp restrict_favorited_by(query, _), do: query
defp restrict_media(query, %{"only_media" => val}) when val == "true" or val == "1" do 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, ^[]) where: fragment("not (? #> '{\"object\",\"attachment\"}' = ?)", activity.data, ^[])
)
end end
defp restrict_media(query, _), do: query defp restrict_media(query, _), do: query
# Only search through last 100_000 activities by default # Only search through last 100_000 activities by default
defp restrict_recent(query, %{"whole_db" => true}), do: query defp restrict_recent(query, %{"whole_db" => true}), do: query
defp restrict_recent(query, _) do defp restrict_recent(query, _) do
since = (Repo.aggregate(Activity, :max, :id) || 0) - 100_000 since = (Repo.aggregate(Activity, :max, :id) || 0) - 100_000
from activity in query, from(activity in query, where: activity.id > ^since)
where: activity.id > ^since
end end
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
blocks = info["blocks"] || [] blocks = info["blocks"] || []
from activity in query, from(activity in query, where: fragment("not (? = ANY(?))", activity.actor, ^blocks))
where: fragment("not (? = ANY(?))", activity.actor, ^blocks)
end end
defp restrict_blocked(query, _), do: query defp restrict_blocked(query, _), do: query
def fetch_activities_query(recipients, opts \\ %{}) do def fetch_activities_query(recipients, opts \\ %{}) do
base_query = from activity in Activity, base_query =
from(
activity in Activity,
limit: 20, limit: 20,
order_by: [fragment("? desc nulls last", activity.id)] order_by: [fragment("? desc nulls last", activity.id)]
)
base_query base_query
|> restrict_recipients(recipients, opts["user"]) |> restrict_recipients(recipients, opts["user"])
@ -266,8 +337,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_activities(recipients, opts \\ %{}) do def fetch_activities(recipients, opts \\ %{}) do
fetch_activities_query(recipients, opts) fetch_activities_query(recipients, opts)
|> Repo.all |> Repo.all()
|> Enum.reverse |> Enum.reverse()
end end
def upload(file) do def upload(file) do
@ -276,12 +347,16 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def user_data_from_user_object(data) do def user_data_from_user_object(data) do
avatar = data["icon"]["url"] && %{ avatar =
data["icon"]["url"] &&
%{
"type" => "Image", "type" => "Image",
"url" => [%{"href" => data["icon"]["url"]}] "url" => [%{"href" => data["icon"]["url"]}]
} }
banner = data["image"]["url"] && %{ banner =
data["image"]["url"] &&
%{
"type" => "Image", "type" => "Image",
"url" => [%{"href" => data["image"]["url"]}] "url" => [%{"href" => data["image"]["url"]}]
} }
@ -304,7 +379,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def fetch_and_prepare_user_from_ap_id(ap_id) do 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"]), with {:ok, %{status_code: 200, body: body}} <-
@httpoison.get(ap_id, Accept: "application/activity+json"),
{:ok, data} <- Jason.decode(body) do {:ok, data} <- Jason.decode(body) do
user_data_from_user_object(data) user_data_from_user_object(data)
else else
@ -333,32 +409,48 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def publish(actor, activity) do def publish(actor, activity) do
followers = if actor.follower_address in activity.recipients do followers =
if actor.follower_address in activity.recipients do
{:ok, followers} = User.get_followers(actor) {:ok, followers} = User.get_followers(actor)
followers |> Enum.filter(&(!&1.local)) followers |> Enum.filter(&(!&1.local))
else else
[] []
end end
remote_inboxes = (Pleroma.Web.Salmon.remote_users(activity) ++ followers) remote_inboxes =
|> Enum.filter(fn (user) -> User.ap_enabled?(user) end) (Pleroma.Web.Salmon.remote_users(activity) ++ followers)
|> Enum.map(fn (%{info: %{"source_data" => data}}) -> |> Enum.filter(fn user -> User.ap_enabled?(user) end)
|> Enum.map(fn %{info: %{"source_data" => data}} ->
(data["endpoints"] && data["endpoints"]["sharedInbox"]) || data["inbox"] (data["endpoints"] && data["endpoints"]["sharedInbox"]) || data["inbox"]
end) end)
|> Enum.uniq |> Enum.uniq()
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data) {:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
json = Jason.encode!(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"]}) Enum.each(remote_inboxes, fn inbox ->
end Federator.enqueue(:publish_single_ap, %{
inbox: inbox,
json: json,
actor: actor,
id: activity.data["id"]
})
end)
end end
def publish_one(%{inbox: inbox, json: json, actor: actor, id: id}) do def publish_one(%{inbox: inbox, json: json, actor: actor, id: id}) do
Logger.info("Federating #{id} to #{inbox}") Logger.info("Federating #{id} to #{inbox}")
host = URI.parse(inbox).host 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 end
# TODO: # TODO:
@ -368,17 +460,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
{:ok, object} {:ok, object}
else else
Logger.info("Fetching #{id} via AP") Logger.info("Fetching #{id} via AP")
with true <- String.starts_with?(id, "http"), 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), {:ok, data} <- Jason.decode(body),
nil <- Object.get_by_ap_id(data["id"]), 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, activity} <- Transmogrifier.handle_incoming(params) do
{:ok, Object.get_by_ap_id(activity.data["object"]["id"])} {:ok, Object.get_by_ap_id(activity.data["object"]["id"])}
else else
object = %Object{} -> {:ok, object} object = %Object{} ->
{:ok, object}
e -> e ->
Logger.info("Couldn't get object via AP, trying out OStatus fetching...") Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
case OStatus.fetch_activity_from_url(id) do case OStatus.fetch_activity_from_url(id) do
{:ok, [activity | _]} -> {:ok, Object.get_by_ap_id(activity.data["object"]["id"])} {:ok, [activity | _]} -> {:ok, Object.get_by_ap_id(activity.data["object"]["id"])}
e -> e e -> e
@ -388,15 +497,17 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
def is_public?(activity) do 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 end
def visible_for_user?(activity, nil) do def visible_for_user?(activity, nil) do
is_public?(activity) is_public?(activity)
end end
def visible_for_user?(activity, user) do def visible_for_user?(activity, user) do
x = [user.ap_id | user.following] 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)) visible_for_user?(activity, nil) || Enum.any?(x, &(&1 in y))
end end
end end

View file

@ -7,7 +7,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
require Logger require Logger
action_fallback :errors action_fallback(:errors)
def user(conn, %{"nickname" => nickname}) do def user(conn, %{"nickname" => nickname}) do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
@ -31,6 +31,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
{page, _} = Integer.parse(page) {page, _} = Integer.parse(page)
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("following.json", %{user: user, page: page})) |> json(UserView.render("following.json", %{user: user, page: page}))
@ -50,6 +51,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
with %User{} = user <- User.get_cached_by_nickname(nickname), with %User{} = user <- User.get_cached_by_nickname(nickname),
{:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do
{page, _} = Integer.parse(page) {page, _} = Integer.parse(page)
conn conn
|> put_resp_header("content-type", "application/activity+json") |> put_resp_header("content-type", "application/activity+json")
|> json(UserView.render("followers.json", %{user: user, page: page})) |> json(UserView.render("followers.json", %{user: user, page: page}))
@ -74,7 +76,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end end
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 # TODO: Ensure that this inbox is a recipient of the message
def inbox(%{assigns: %{valid_signature: true}} = conn, params) do def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
@ -84,7 +88,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
def inbox(conn, params) do def inbox(conn, params) do
headers = Enum.into(conn.req_headers, %{}) 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") Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_object_from_id(params["object"]["id"]) ActivityPub.fetch_object_from_id(params["object"]["id"])
else else

View file

@ -25,21 +25,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_tag |> fix_tag
end 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 case ActivityPub.fetch_object_from_id(in_reply_to_id) do
{:ok, replied_object} -> {:ok, replied_object} ->
activity = Activity.get_create_activity_by_object_ap_id(replied_object.data["id"]) activity = Activity.get_create_activity_by_object_ap_id(replied_object.data["id"])
object object
|> Map.put("inReplyTo", replied_object.data["id"]) |> Map.put("inReplyTo", replied_object.data["id"])
|> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id) |> Map.put("inReplyToAtomUri", object["inReplyToAtomUri"] || in_reply_to_id)
|> Map.put("inReplyToStatusId", activity.id) |> Map.put("inReplyToStatusId", activity.id)
|> Map.put("conversation", replied_object.data["context"] || object["conversation"]) |> Map.put("conversation", replied_object.data["context"] || object["conversation"])
|> Map.put("context", replied_object.data["context"] || object["conversation"]) |> Map.put("context", replied_object.data["context"] || object["conversation"])
e -> e ->
Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}") Logger.error("Couldn't fetch #{object["inReplyTo"]} #{inspect(e)}")
object object
end end
end end
def fix_in_reply_to(object), do: object def fix_in_reply_to(object), do: object
def fix_context(object) do def fix_context(object) do
@ -48,8 +52,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def fix_attachments(object) do def fix_attachments(object) do
attachments = (object["attachment"] || []) attachments =
|> Enum.map(fn (data) -> (object["attachment"] || [])
|> Enum.map(fn data ->
url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}]
Map.put(data, "url", url) Map.put(data, "url", url)
end) end)
@ -59,10 +64,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def fix_emoji(object) do def fix_emoji(object) do
tags = (object["tag"] || []) tags = object["tag"] || []
emoji = tags |> Enum.filter(fn (data) -> data["type"] == "Emoji" and data["icon"] end) emoji = tags |> Enum.filter(fn data -> data["type"] == "Emoji" and data["icon"] end)
emoji = emoji |> Enum.reduce(%{}, fn (data, mapping) ->
emoji =
emoji
|> Enum.reduce(%{}, fn data, mapping ->
name = data["name"] name = data["name"]
if String.starts_with?(name, ":") do if String.starts_with?(name, ":") do
name = name |> String.slice(1..-2) name = name |> String.slice(1..-2)
end end
@ -78,9 +87,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def fix_tag(object) do def fix_tag(object) do
tags = (object["tag"] || []) tags =
|> Enum.filter(fn (data) -> data["type"] == "Hashtag" and data["name"] end) (object["tag"] || [])
|> Enum.map(fn (data) -> String.slice(data["name"], 1..-1) end) |> 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 combined = (object["tag"] || []) ++ tags
@ -103,13 +113,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
context: object["conversation"], context: object["conversation"],
local: false, local: false,
published: data["published"], published: data["published"],
additional: Map.take(data, [ additional:
Map.take(data, [
"cc", "cc",
"id" "id"
]) ])
} }
ActivityPub.create(params) ActivityPub.create(params)
else else
%Activity{} = activity -> {:ok, activity} %Activity{} = activity -> {:ok, activity}
@ -117,11 +127,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
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), with %User{local: true} = followed <- User.get_cached_by_ap_id(followed),
%User{} = follower <- User.get_or_fetch_by_ap_id(follower), %User{} = follower <- User.get_or_fetch_by_ap_id(follower),
{:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do {:ok, activity} <- ActivityPub.follow(follower, followed, id, false) do
ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true}) ActivityPub.accept(%{to: [follower.ap_id], actor: followed.ap_id, object: data, local: true})
User.follow(follower, followed) User.follow(follower, followed)
{:ok, activity} {:ok, activity}
else else
@ -129,7 +142,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
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), 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, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do {:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do
@ -139,7 +154,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
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), 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, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do {:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do
@ -149,12 +166,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
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 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) {:ok, new_user_data} = ActivityPub.user_data_from_user_object(object)
banner = new_user_data[:info]["banner"] banner = new_user_data[:info]["banner"]
update_data = new_user_data
update_data =
new_user_data
|> Map.take([:name, :bio, :avatar]) |> Map.take([:name, :bio, :avatar])
|> Map.put(:info, Map.merge(actor.info, %{"banner" => banner})) |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner}))
@ -162,7 +184,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> User.upgrade_changeset(update_data) |> User.upgrade_changeset(update_data)
|> User.update_and_set_cache() |> 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 else
e -> e ->
Logger.error(e) Logger.error(e)
@ -171,11 +199,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
# TODO: Make secure. # TODO: Make secure.
def handle_incoming(%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data) do def handle_incoming(
object_id = case object_id do %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data
) do
object_id =
case object_id do
%{"id" => id} -> id %{"id" => id} -> id
id -> id id -> id
end end
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), 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, object} <- get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity} <- ActivityPub.delete(object, false) do {:ok, activity} <- ActivityPub.delete(object, false) do
@ -203,6 +235,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
_e -> object _e -> object
end end
end end
def set_reply_to_uri(obj), do: obj def set_reply_to_uri(obj), do: obj
# Prepares the object of an outgoing create activity. # Prepares the object of an outgoing create activity.
@ -222,10 +255,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
""" """
internal -> Mastodon internal -> Mastodon
""" """
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
object = object object =
object
|> prepare_object |> prepare_object
data = data
data =
data
|> Map.put("object", object) |> Map.put("object", object)
|> Map.put("@context", "https://www.w3.org/ns/activitystreams") |> Map.put("@context", "https://www.w3.org/ns/activitystreams")
@ -233,7 +270,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def prepare_outgoing(%{"type" => type} = data) do def prepare_outgoing(%{"type" => type} = data) do
data = data data =
data
|> maybe_fix_object_url |> maybe_fix_object_url
|> Map.put("@context", "https://www.w3.org/ns/activitystreams") |> Map.put("@context", "https://www.w3.org/ns/activitystreams")
@ -245,11 +283,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
case ActivityPub.fetch_object_from_id(data["object"]) do case ActivityPub.fetch_object_from_id(data["object"]) do
{:ok, relative_object} -> {:ok, relative_object} ->
if relative_object.data["external_url"] do if relative_object.data["external_url"] do
data = data data =
data
|> Map.put("object", relative_object.data["external_url"]) |> Map.put("object", relative_object.data["external_url"])
else else
data data
end end
e -> e ->
Logger.error("Couldn't fetch #{data["object"]} #{inspect(e)}") Logger.error("Couldn't fetch #{data["object"]} #{inspect(e)}")
data data
@ -260,8 +300,15 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def add_hashtags(object) do def add_hashtags(object) do
tags = (object["tag"] || []) tags =
|> Enum.map fn (tag) -> %{"href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}", "name" => "##{tag}", "type" => "Hashtag"} end (object["tag"] || [])
|> Enum.map(fn tag ->
%{
"href" => Pleroma.Web.Endpoint.url() <> "/tags/#{tag}",
"name" => "##{tag}",
"type" => "Hashtag"
}
end)
object object
|> Map.put("tag", tags) |> Map.put("tag", tags)
@ -269,10 +316,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def add_mention_tags(object) do def add_mention_tags(object) do
recipients = object["to"] ++ (object["cc"] || []) recipients = object["to"] ++ (object["cc"] || [])
mentions = recipients
|> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) mentions =
|> Enum.filter(&(&1)) recipients
|> Enum.map(fn(user) -> %{"type" => "Mention", "href" => user.ap_id, "name" => "@#{user.nickname}"} end) |> 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"] || [] tags = object["tag"] || []
@ -284,12 +335,17 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def add_emoji_tags(object) do def add_emoji_tags(object) do
tags = object["tag"] || [] tags = object["tag"] || []
emoji = object["emoji"] || [] emoji = object["emoji"] || []
out = emoji |> Enum.map(fn {name, url} ->
%{"icon" => %{"url" => url, "type" => "Image"}, out =
emoji
|> Enum.map(fn {name, url} ->
%{
"icon" => %{"url" => url, "type" => "Image"},
"name" => ":" <> name <> ":", "name" => ":" <> name <> ":",
"type" => "Emoji", "type" => "Emoji",
"updated" => "1970-01-01T00:00:00Z", "updated" => "1970-01-01T00:00:00Z",
"id" => url} "id" => url
}
end) end)
object object
@ -313,8 +369,9 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def prepare_attachments(object) do def prepare_attachments(object) do
attachments = (object["attachment"] || []) attachments =
|> Enum.map(fn (data) -> (object["attachment"] || [])
|> Enum.map(fn data ->
[%{"mediaType" => media_type, "href" => href} | _] = data["url"] [%{"mediaType" => media_type, "href" => href} | _] = data["url"]
%{"url" => href, "mediaType" => media_type, "name" => data["name"], "type" => "Document"} %{"url" => href, "mediaType" => media_type, "name" => data["name"], "type" => "Document"}
end) end)
@ -325,9 +382,24 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
defp user_upgrade_task(user) do defp user_upgrade_task(user) do
old_follower_address = User.ap_followers(user) old_follower_address = User.ap_followers(user)
q = from u in User,
q =
from(
u in User,
where: ^old_follower_address in u.following, where: ^old_follower_address in u.following,
update: [set: [following: fragment("array_replace(?,?,?)", u.following, ^old_follower_address, ^user.follower_address)]] update: [
set: [
following:
fragment(
"array_replace(?,?,?)",
u.following,
^old_follower_address,
^user.follower_address
)
]
]
)
Repo.update_all(q, []) Repo.update_all(q, [])
maybe_retire_websub(user.ap_id) maybe_retire_websub(user.ap_id)
@ -335,21 +407,39 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# Only do this for recent activties, don't go through the whole db. # Only do this for recent activties, don't go through the whole db.
# Only look at the last 1000 activities. # Only look at the last 1000 activities.
since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000 since = (Repo.aggregate(Activity, :max, :id) || 0) - 1_000
q = from a in Activity,
q =
from(
a in Activity,
where: ^old_follower_address in a.recipients, where: ^old_follower_address in a.recipients,
where: a.id > ^since, where: a.id > ^since,
update: [set: [recipients: fragment("array_replace(?,?,?)", a.recipients, ^old_follower_address, ^user.follower_address)]] update: [
set: [
recipients:
fragment(
"array_replace(?,?,?)",
a.recipients,
^old_follower_address,
^user.follower_address
)
]
]
)
Repo.update_all(q, []) Repo.update_all(q, [])
end end
def upgrade_user_from_ap_id(ap_id, async \\ true) do def upgrade_user_from_ap_id(ap_id, async \\ true) do
with %User{local: false} = user <- User.get_by_ap_id(ap_id), 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 {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do
data = data data =
data
|> Map.put(:info, Map.merge(user.info, data[:info])) |> Map.put(:info, Map.merge(user.info, data[:info]))
already_ap = User.ap_enabled?(user) already_ap = User.ap_enabled?(user)
{:ok, user} = User.upgrade_changeset(user, data)
{:ok, user} =
User.upgrade_changeset(user, data)
|> Repo.update() |> Repo.update()
if !already_ap do if !already_ap do
@ -371,9 +461,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def maybe_retire_websub(ap_id) do def maybe_retire_websub(ap_id) do
# some sanity checks # some sanity checks
if is_binary(ap_id) && (String.length(ap_id) > 8) do if is_binary(ap_id) && String.length(ap_id) > 8 do
q = from ws in Pleroma.Web.Websub.WebsubClientSubscription, q =
from(
ws in Pleroma.Web.Websub.WebsubClientSubscription,
where: fragment("? like ?", ws.topic, ^"#{ap_id}%") where: fragment("? like ?", ws.topic, ^"#{ap_id}%")
)
Repo.delete_all(q) Repo.delete_all(q)
end end
end end

View file

@ -26,7 +26,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def make_date do def make_date do
DateTime.utc_now() |> DateTime.to_iso8601 DateTime.utc_now() |> DateTime.to_iso8601()
end end
def generate_activity_id do def generate_activity_id do
@ -38,25 +38,28 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def generate_object_id do def generate_object_id do
Helpers.o_status_url(Endpoint, :object, UUID.generate) Helpers.o_status_url(Endpoint, :object, UUID.generate())
end end
def generate_id(type) do def generate_id(type) do
"#{Web.base_url()}/#{type}/#{UUID.generate}" "#{Web.base_url()}/#{type}/#{UUID.generate()}"
end end
@doc """ @doc """
Enqueues an activity for federation if it's local Enqueues an activity for federation if it's local
""" """
def maybe_federate(%Activity{local: true} = activity) do def maybe_federate(%Activity{local: true} = activity) do
priority = case activity.data["type"] do priority =
case activity.data["type"] do
"Delete" -> 10 "Delete" -> 10
"Create" -> 1 "Create" -> 1
_ -> 5 _ -> 5
end end
Pleroma.Web.Federator.enqueue(:publish, activity, priority) Pleroma.Web.Federator.enqueue(:publish, activity, priority)
:ok :ok
end end
def maybe_federate(_), do: :ok def maybe_federate(_), do: :ok
@doc """ @doc """
@ -64,7 +67,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
also adds it to an included object also adds it to an included object
""" """
def lazy_put_activity_defaults(map) do def lazy_put_activity_defaults(map) do
map = map map =
map
|> Map.put_new_lazy("id", &generate_activity_id/0) |> Map.put_new_lazy("id", &generate_activity_id/0)
|> Map.put_new_lazy("published", &make_date/0) |> Map.put_new_lazy("published", &make_date/0)
@ -88,11 +92,13 @@ defmodule Pleroma.Web.ActivityPub.Utils do
@doc """ @doc """
Inserts a full object if it is contained in an activity. 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 with {:ok, _} <- Object.create(object_data) do
:ok :ok
end end
end end
def insert_full_object(_), do: :ok def insert_full_object(_), do: :ok
def update_object_in_activities(%{data: %{"id" => id}} = object) do def update_object_in_activities(%{data: %{"id" => id}} = object) do
@ -101,7 +107,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
# Alternatively, just don't do this and fetch the current object each time. Most # Alternatively, just don't do this and fetch the current object each time. Most
# could probably be taken from cache. # could probably be taken from cache.
relevant_activities = Activity.all_by_object_ap_id(id) 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) new_activity_data = activity.data |> Map.put("object", object.data)
changeset = Changeset.change(activity, data: new_activity_data) changeset = Changeset.change(activity, data: new_activity_data)
Repo.update(changeset) Repo.update(changeset)
@ -114,11 +121,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Returns an existing like if a user already liked an object Returns an existing like if a user already liked an object
""" """
def get_existing_like(actor, %{data: %{"id" => id}}) do def get_existing_like(actor, %{data: %{"id" => id}}) do
query = from activity in Activity, query =
from(
activity in Activity,
where: fragment("(?)->>'actor' = ?", activity.data, ^actor), where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
# this is to use the index # this is to use the index
where: fragment("coalesce((?)->'object'->>'id', (?)->>'object') = ?", activity.data, activity.data, ^id), where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
activity.data,
activity.data,
^id
),
where: fragment("(?)->>'type' = 'Like'", activity.data) where: fragment("(?)->>'type' = 'Like'", activity.data)
)
Repo.one(query) Repo.one(query)
end end
@ -137,7 +153,9 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def update_element_in_object(property, element, object) do 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), changeset <- Changeset.change(object, data: new_data),
{:ok, object} <- Repo.update(changeset), {:ok, object} <- Repo.update(changeset),
_ <- update_object_in_activities(object) do _ <- update_object_in_activities(object) do
@ -150,7 +168,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do 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) update_likes_in_object(likes, object)
end end
end end
@ -178,13 +196,20 @@ defmodule Pleroma.Web.ActivityPub.Utils do
if activity_id, do: Map.put(data, "id", activity_id), else: data if activity_id, do: Map.put(data, "id", activity_id), else: data
end end
def fetch_latest_follow(%User{ap_id: follower_id}, def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
%User{ap_id: followed_id}) do query =
query = from activity in Activity, from(
where: fragment("? @> ?", activity.data, ^%{type: "Follow", actor: follower_id, activity in Activity,
object: followed_id}), where:
fragment(
"? @> ?",
activity.data,
^%{type: "Follow", actor: follower_id, object: followed_id}
),
order_by: [desc: :id], order_by: [desc: :id],
limit: 1 limit: 1
)
Repo.one(query) Repo.one(query)
end end
@ -193,7 +218,11 @@ defmodule Pleroma.Web.ActivityPub.Utils do
@doc """ @doc """
Make announce activity data for the given actor and object 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 = %{ data = %{
"type" => "Announce", "type" => "Announce",
"actor" => ap_id, "actor" => ap_id,
@ -207,7 +236,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do 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) update_element_in_object("announcement", announcements, object)
end end
end end
@ -223,14 +252,14 @@ defmodule Pleroma.Web.ActivityPub.Utils do
} }
end end
#### Create-related helpers #### Create-related helpers
def make_create_data(params, additional) do def make_create_data(params, additional) do
published = params.published || make_date() published = params.published || make_date()
%{ %{
"type" => "Create", "type" => "Create",
"to" => params.to |> Enum.uniq, "to" => params.to |> Enum.uniq(),
"actor" => params.actor.ap_id, "actor" => params.actor.ap_id,
"object" => params.object, "object" => params.object,
"published" => published, "published" => published,

View file

@ -12,6 +12,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
{:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"]) {: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_entry_encode(:RSAPublicKey, public_key)
public_key = :public_key.pem_encode([public_key]) public_key = :public_key.pem_encode([public_key])
%{ %{
"id" => user.ap_id, "id" => user.ap_id,
"type" => "Person", "type" => "Person",
@ -30,7 +31,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"publicKeyPem" => public_key "publicKeyPem" => public_key
}, },
"endpoints" => %{ "endpoints" => %{
"sharedInbox" => "#{Pleroma.Web.Endpoint.url}/inbox" "sharedInbox" => "#{Pleroma.Web.Endpoint.url()}/inbox"
}, },
"icon" => %{ "icon" => %{
"type" => "Image", "type" => "Image",
@ -47,7 +48,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def collection(collection, iri, page) do def collection(collection, iri, page) do
offset = (page - 1) * 10 offset = (page - 1) * 10
items = Enum.slice(collection, offset, 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 = %{ map = %{
"id" => "#{iri}?page=#{page}", "id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage", "type" => "OrderedCollectionPage",
@ -55,6 +57,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"totalItems" => length(collection), "totalItems" => length(collection),
"orderedItems" => items "orderedItems" => items
} }
if offset < length(collection) do if offset < length(collection) do
Map.put(map, "next", "#{iri}?page=#{page + 1}") Map.put(map, "next", "#{iri}?page=#{page + 1}")
end end
@ -62,12 +65,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("following.json", %{user: user, page: page}) do def render("following.json", %{user: user, page: page}) do
{:ok, following} = User.get_friends(user) {:ok, following} = User.get_friends(user)
collection(following, "#{user.ap_id}/following", page) collection(following, "#{user.ap_id}/following", page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
def render("following.json", %{user: user}) do def render("following.json", %{user: user}) do
{:ok, following} = User.get_friends(user) {:ok, following} = User.get_friends(user)
%{ %{
"id" => "#{user.ap_id}/following", "id" => "#{user.ap_id}/following",
"type" => "OrderedCollection", "type" => "OrderedCollection",
@ -79,12 +84,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("followers.json", %{user: user, page: page}) do def render("followers.json", %{user: user, page: page}) do
{:ok, followers} = User.get_followers(user) {:ok, followers} = User.get_followers(user)
collection(followers, "#{user.ap_id}/followers", page) collection(followers, "#{user.ap_id}/followers", page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
def render("followers.json", %{user: user}) do def render("followers.json", %{user: user}) do
{:ok, followers} = User.get_followers(user) {:ok, followers} = User.get_followers(user)
%{ %{
"id" => "#{user.ap_id}/followers", "id" => "#{user.ap_id}/followers",
"type" => "OrderedCollection", "type" => "OrderedCollection",
@ -115,19 +122,21 @@ defmodule Pleroma.Web.ActivityPub.UserView do
activities = Enum.reverse(activities) activities = Enum.reverse(activities)
max_id = Enum.at(activities, 0).id max_id = Enum.at(activities, 0).id
collection = Enum.map(activities, fn (act) -> collection =
Enum.map(activities, fn act ->
{:ok, data} = Transmogrifier.prepare_outgoing(act.data) {:ok, data} = Transmogrifier.prepare_outgoing(act.data)
data data
end) end)
iri = "#{user.ap_id}/outbox" iri = "#{user.ap_id}/outbox"
page = %{ page = %{
"id" => "#{iri}?max_id=#{max_id}", "id" => "#{iri}?max_id=#{max_id}",
"type" => "OrderedCollectionPage", "type" => "OrderedCollectionPage",
"partOf" => iri, "partOf" => iri,
"totalItems" => info.note_count, "totalItems" => info.note_count,
"orderedItems" => collection, "orderedItems" => collection,
"next" => "#{iri}?max_id=#{min_id-1}", "next" => "#{iri}?max_id=#{min_id - 1}"
} }
if max_qid == nil do if max_qid == nil do

View file

@ -6,11 +6,11 @@ defmodule Pleroma.Web.UserSocket do
## Channels ## Channels
# channel "room:*", Pleroma.Web.RoomChannel # channel "room:*", Pleroma.Web.RoomChannel
if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do
channel "chat:*", Pleroma.Web.ChatChannel channel("chat:*", Pleroma.Web.ChatChannel)
end end
## Transports ## Transports
transport :websocket, Phoenix.Transports.WebSocket transport(:websocket, Phoenix.Transports.WebSocket)
# transport :longpoll, Phoenix.Transports.LongPoll # transport :longpoll, Phoenix.Transports.LongPoll
# Socket params are passed from the client and can # Socket params are passed from the client and can

View file

@ -9,19 +9,21 @@ defmodule Pleroma.Web.ChatChannel do
end end
def handle_info(:after_join, socket) do def handle_info(:after_join, socket) do
push socket, "messages", %{messages: ChatChannelState.messages()} push(socket, "messages", %{messages: ChatChannelState.messages()})
{:noreply, socket} {:noreply, socket}
end end
def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do def handle_in("new_msg", %{"text" => text}, %{assigns: %{user_name: user_name}} = socket) do
text = String.trim(text) text = String.trim(text)
if String.length(text) > 0 do if String.length(text) > 0 do
author = User.get_cached_by_nickname(user_name) author = User.get_cached_by_nickname(user_name)
author = Pleroma.Web.MastodonAPI.AccountView.render("account.json", user: author) author = Pleroma.Web.MastodonAPI.AccountView.render("account.json", user: author)
message = ChatChannelState.add_message(%{text: text, author: author}) message = ChatChannelState.add_message(%{text: text, author: author})
broadcast! socket, "new_msg", message broadcast!(socket, "new_msg", message)
end end
{:noreply, socket} {:noreply, socket}
end end
end end
@ -43,6 +45,6 @@ defmodule Pleroma.Web.ChatChannel.ChatChannelState do
end end
def messages() do 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
end end

View file

@ -8,7 +8,7 @@ defmodule Pleroma.Web.CommonAPI do
def delete(activity_id, user) do def delete(activity_id, user) do
with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id), with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id),
%Object{} = object <- Object.get_by_ap_id(object_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} <- ActivityPub.delete(object) do
{:ok, delete} {:ok, delete}
end end
@ -46,17 +46,22 @@ defmodule Pleroma.Web.CommonAPI do
end end
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 def get_visibility(%{"in_reply_to_status_id" => status_id}) when not is_nil(status_id) do
inReplyTo = get_replied_to_activity(status_id) inReplyTo = get_replied_to_activity(status_id)
Pleroma.Web.MastodonAPI.StatusView.get_visibility(inReplyTo.data["object"]) Pleroma.Web.MastodonAPI.StatusView.get_visibility(inReplyTo.data["object"])
end end
def get_visibility(_), do: "public" def get_visibility(_), do: "public"
@instance Application.get_env(:pleroma, :instance) @instance Application.get_env(:pleroma, :instance)
@limit Keyword.get(@instance, :limit) @limit Keyword.get(@instance, :limit)
def post(user, %{"status" => status} = data) do def post(user, %{"status" => status} = data) do
visibility = get_visibility(data) visibility = get_visibility(data)
with status <- String.trim(status), with status <- String.trim(status),
length when length in 1..@limit <- String.length(status), length when length in 1..@limit <- String.length(status),
attachments <- attachments_from_ids(data["media_ids"]), attachments <- attachments_from_ids(data["media_ids"]),
@ -64,18 +69,52 @@ defmodule Pleroma.Web.CommonAPI do
inReplyTo <- get_replied_to_activity(data["in_reply_to_status_id"]), inReplyTo <- get_replied_to_activity(data["in_reply_to_status_id"]),
{to, cc} <- to_for_user_and_mentions(user, mentions, inReplyTo, visibility), {to, cc} <- to_for_user_and_mentions(user, mentions, inReplyTo, visibility),
tags <- Formatter.parse_tags(status, data), 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), context <- make_context(inReplyTo),
cw <- data["spoiler_text"], cw <- data["spoiler_text"],
object <- make_note_data(user.ap_id, to, context, content_html, attachments, inReplyTo, tags, cw, cc), object <-
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 make_note_data(
res = ActivityPub.create(%{to: to, actor: user, context: context, object: object, additional: %{"cc" => cc}}) 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) User.increase_note_count(user)
res res
end end
end end
def update(user) do 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
end end

View file

@ -6,6 +6,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
# This is a hack for twidere. # This is a hack for twidere.
def get_by_id_or_ap_id(id) do def get_by_id_or_ap_id(id) do
activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id) activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
if activity.data["type"] == "Create" do if activity.data["type"] == "Create" do
activity activity
else else
@ -16,10 +17,11 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def get_replied_to_activity(id) when not is_nil(id) do def get_replied_to_activity(id) when not is_nil(id) do
Repo.get(Activity, id) Repo.get(Activity, id)
end end
def get_replied_to_activity(_), do: nil def get_replied_to_activity(_), do: nil
def attachments_from_ids(ids) do def attachments_from_ids(ids) do
Enum.map(ids || [], fn (media_id) -> Enum.map(ids || [], fn media_id ->
Repo.get(Object, media_id).data Repo.get(Object, media_id).data
end) end)
end end
@ -27,8 +29,9 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do def to_for_user_and_mentions(user, mentions, inReplyTo, "public") do
to = ["https://www.w3.org/ns/activitystreams#Public"] 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] cc = [user.follower_address | mentioned_users]
if inReplyTo do if inReplyTo do
{to, Enum.uniq([inReplyTo.data["actor"] | cc])} {to, Enum.uniq([inReplyTo.data["actor"] | cc])}
else else
@ -47,7 +50,8 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end
def to_for_user_and_mentions(user, mentions, inReplyTo, "direct") do 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 if inReplyTo do
{Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []} {Enum.uniq([inReplyTo.data["actor"] | mentioned_users]), []}
else else
@ -62,44 +66,61 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end
def make_context(%Activity{data: %{"context" => context}}), do: context 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 = true), do: text
def maybe_add_attachments(text, attachments, _no_links) do def maybe_add_attachments(text, attachments, _no_links) do
add_attachments(text, attachments) add_attachments(text, attachments)
end end
def add_attachments(text, attachments) do def add_attachments(text, attachments) do
attachment_text = Enum.map(attachments, fn attachment_text =
(%{"url" => [%{"href" => href} | _]}) -> Enum.map(attachments, fn
%{"url" => [%{"href" => href} | _]} ->
name = URI.decode(Path.basename(href)) name = URI.decode(Path.basename(href))
"<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>" "<a href=\"#{href}\" class='attachment'>#{shortname(name)}</a>"
_ -> ""
_ ->
""
end) end)
Enum.join([text | attachment_text], "<br>") Enum.join([text | attachment_text], "<br>")
end end
def format_input(text, mentions, tags) do def format_input(text, mentions, tags) do
text text
|> Formatter.html_escape |> Formatter.html_escape()
|> String.replace("\n", "<br>") |> String.replace("\n", "<br>")
|> (&({[], &1})).() |> (&{[], &1}).()
|> Formatter.add_links |> Formatter.add_links()
|> Formatter.add_user_links(mentions) |> Formatter.add_user_links(mentions)
|> Formatter.add_hashtag_links(tags) |> Formatter.add_hashtag_links(tags)
|> Formatter.finalize |> Formatter.finalize()
end end
def add_tag_links(text, tags) do def add_tag_links(text, tags) do
tags = tags tags =
|> Enum.sort_by(fn ({tag, _}) -> -String.length(tag) end) tags
|> Enum.sort_by(fn {tag, _} -> -String.length(tag) end)
Enum.reduce(tags, text, fn({full, tag}, text) -> Enum.reduce(tags, text, fn {full, tag}, text ->
url = "#<a href='#{Pleroma.Web.base_url}/tag/#{tag}' rel='tag'>#{tag}</a>" url = "#<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag}</a>"
String.replace(text, full, url) String.replace(text, full, url)
end) end)
end end
def make_note_data(actor, to, context, content_html, attachments, inReplyTo, tags, cw \\ nil, cc \\ []) do def make_note_data(
actor,
to,
context,
content_html,
attachments,
inReplyTo,
tags,
cw \\ nil,
cc \\ []
) do
object = %{ object = %{
"type" => "Note", "type" => "Note",
"to" => to, "to" => to,
@ -109,7 +130,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
"context" => context, "context" => context,
"attachment" => attachments, "attachment" => attachments,
"actor" => actor, "actor" => actor,
"tag" => tags |> Enum.map(fn ({_, tag}) -> tag end) "tag" => tags |> Enum.map(fn {_, tag} -> tag end)
} }
if inReplyTo do if inReplyTo do
@ -130,24 +151,25 @@ defmodule Pleroma.Web.CommonAPI.Utils do
end end
def date_to_asctime(date) do 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) format_asctime(date)
else _e -> else
_e ->
"" ""
end end
end end
def to_masto_date(%NaiveDateTime{} = date) do def to_masto_date(%NaiveDateTime{} = date) do
date date
|> NaiveDateTime.to_iso8601 |> NaiveDateTime.to_iso8601()
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
end end
def to_masto_date(date) do def to_masto_date(date) do
try do try do
date date
|> NaiveDateTime.from_iso8601! |> NaiveDateTime.from_iso8601!()
|> NaiveDateTime.to_iso8601 |> NaiveDateTime.to_iso8601()
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
rescue rescue
_e -> "" _e -> ""

View file

@ -2,47 +2,55 @@ defmodule Pleroma.Web.Endpoint do
use Phoenix.Endpoint, otp_app: :pleroma use Phoenix.Endpoint, otp_app: :pleroma
if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do if Application.get_env(:pleroma, :chat) |> Keyword.get(:enabled) do
socket "/socket", Pleroma.Web.UserSocket socket("/socket", Pleroma.Web.UserSocket)
end end
socket "/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket
socket("/api/v1", Pleroma.Web.MastodonAPI.MastodonSocket)
# Serve at "/" the static files from "priv/static" directory. # Serve at "/" the static files from "priv/static" directory.
# #
# You should set gzip to true if you are running phoenix.digest # You should set gzip to true if you are running phoenix.digest
# when deploying your static files in production. # when deploying your static files in production.
plug Plug.Static, plug(Plug.Static, at: "/media", from: "uploads", gzip: false)
at: "/media", from: "uploads", gzip: false
plug Plug.Static, plug(
at: "/", from: :pleroma, Plug.Static,
at: "/",
from: :pleroma,
only: ~w(index.html static finmoji emoji packs sounds images instance sw.js) only: ~w(index.html static finmoji emoji packs sounds images instance sw.js)
)
# Code reloading can be explicitly enabled under the # Code reloading can be explicitly enabled under the
# :code_reloader configuration of your endpoint. # :code_reloader configuration of your endpoint.
if code_reloading? do if code_reloading? do
plug Phoenix.CodeReloader plug(Phoenix.CodeReloader)
end end
plug TrailingFormatPlug plug(TrailingFormatPlug)
plug Plug.RequestId plug(Plug.RequestId)
plug Plug.Logger plug(Plug.Logger)
plug Plug.Parsers, plug(
Plug.Parsers,
parsers: [:urlencoded, :multipart, :json], parsers: [:urlencoded, :multipart, :json],
pass: ["*/*"], pass: ["*/*"],
json_decoder: Jason json_decoder: Jason
)
plug Plug.MethodOverride plug(Plug.MethodOverride)
plug Plug.Head plug(Plug.Head)
# The session will be stored in the cookie and signed, # The session will be stored in the cookie and signed,
# this means its contents can be read but not tampered with. # this means its contents can be read but not tampered with.
# Set :encryption_salt if you would also like to encrypt it. # Set :encryption_salt if you would also like to encrypt it.
plug Plug.Session, plug(
Plug.Session,
store: :cookie, store: :cookie,
key: "_pleroma_key", key: "_pleroma_key",
signing_salt: "CqaoopA2" signing_salt: "CqaoopA2"
)
plug Pleroma.Web.Router plug(Pleroma.Web.Router)
@doc """ @doc """
Dynamically loads configuration from the system environment Dynamically loads configuration from the system environment

View file

@ -16,26 +16,35 @@ defmodule Pleroma.Web.Federator do
def start_link do def start_link do
spawn(fn -> spawn(fn ->
Process.sleep(1000 * 60 * 1) # 1 minute # 1 minute
Process.sleep(1000 * 60 * 1)
enqueue(:refresh_subscriptions, nil) enqueue(:refresh_subscriptions, nil)
end) end)
GenServer.start_link(__MODULE__, %{
GenServer.start_link(
__MODULE__,
%{
in: {:sets.new(), []}, in: {:sets.new(), []},
out: {:sets.new(), []} out: {:sets.new(), []}
}, name: __MODULE__) },
name: __MODULE__
)
end end
def handle(:refresh_subscriptions, _) do def handle(:refresh_subscriptions, _) do
Logger.debug("Federator running refresh subscriptions") Logger.debug("Federator running refresh subscriptions")
Websub.refresh_subscriptions() Websub.refresh_subscriptions()
spawn(fn -> spawn(fn ->
Process.sleep(1000 * 60 * 60 * 6) # 6 hours # 6 hours
Process.sleep(1000 * 60 * 60 * 6)
enqueue(:refresh_subscriptions, nil) enqueue(:refresh_subscriptions, nil)
end) end)
end end
def handle(:request_subscription, websub) do def handle(:request_subscription, websub) do
Logger.debug("Refreshing #{websub.topic}") 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}") Logger.debug("Successfully refreshed #{websub.topic}")
else else
@ -45,8 +54,10 @@ defmodule Pleroma.Web.Federator do
def handle(:publish, activity) do def handle(:publish, activity) do
Logger.debug(fn -> "Running publish for #{activity.data["id"]}" end) 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 with actor when not is_nil(actor) <- User.get_cached_by_ap_id(activity.data["actor"]) do
{:ok, actor} = WebFinger.ensure_keys_present(actor) {:ok, actor} = WebFinger.ensure_keys_present(actor)
if ActivityPub.is_public?(activity) do if ActivityPub.is_public?(activity) do
Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end) Logger.info(fn -> "Sending #{activity.data["id"]} out via WebSub" end)
Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity) Websub.publish(Pleroma.Web.OStatus.feed_path(actor), actor, activity)
@ -61,7 +72,10 @@ defmodule Pleroma.Web.Federator do
end end
def handle(:verify_websub, websub) do 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) @websub.verify(websub)
end end
@ -72,16 +86,18 @@ defmodule Pleroma.Web.Federator do
def handle(:incoming_ap_doc, params) do def handle(:incoming_ap_doc, params) do
Logger.info("Handling incoming AP activity") Logger.info("Handling incoming AP activity")
with {:ok, _user} <- ap_enabled_actor(params["actor"]), with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.get_by_ap_id(params["id"]), nil <- Activity.get_by_ap_id(params["id"]),
{:ok, activity} <- Transmogrifier.handle_incoming(params) do {:ok, activity} <- Transmogrifier.handle_incoming(params) do
else else
%Activity{} -> %Activity{} ->
Logger.info("Already had #{params["id"]}") Logger.info("Already had #{params["id"]}")
e -> e ->
# Just drop those for now # Just drop those for now
Logger.info("Unhandled activity") Logger.info("Unhandled activity")
Logger.info(Poison.encode!(params, [pretty: 2])) Logger.info(Poison.encode!(params, pretty: 2))
end end
end end
@ -93,12 +109,21 @@ defmodule Pleroma.Web.Federator do
signature = @websub.sign(secret || "", xml) signature = @websub.sign(secret || "", xml)
Logger.debug(fn -> "Pushing #{topic} to #{callback}" end) Logger.debug(fn -> "Pushing #{topic} to #{callback}" end)
with {:ok, %{status_code: code}} <- @httpoison.post(callback, xml, [ with {:ok, %{status_code: code}} <-
@httpoison.post(
callback,
xml,
[
{"Content-Type", "application/atom+xml"}, {"Content-Type", "application/atom+xml"},
{"X-Hub-Signature", "sha1=#{signature}"} {"X-Hub-Signature", "sha1=#{signature}"}
], timeout: 10000, recv_timeout: 20000, hackney: [pool: :default]) do ],
timeout: 10000,
recv_timeout: 20000,
hackney: [pool: :default]
) do
Logger.debug(fn -> "Pushed to #{callback}, code #{code}" end) Logger.debug(fn -> "Pushed to #{callback}, code #{code}" end)
else e -> else
e ->
Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(e)}" end) Logger.debug(fn -> "Couldn't push to #{callback}, #{inspect(e)}" end)
end end
end end
@ -110,7 +135,7 @@ defmodule Pleroma.Web.Federator do
def enqueue(type, payload, priority \\ 1) do def enqueue(type, payload, priority \\ 1) do
if @federating do if @federating do
if Mix.env == :test do if Mix.env() == :test do
handle(type, payload) handle(type, payload)
else else
GenServer.cast(__MODULE__, {:enqueue, type, payload, priority}) GenServer.cast(__MODULE__, {:enqueue, type, payload, priority})
@ -119,7 +144,7 @@ defmodule Pleroma.Web.Federator do
end end
def maybe_start_job(running_jobs, queue) do 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) {{type, payload}, queue} = queue_pop(queue)
{:ok, pid} = Task.start(fn -> handle(type, payload) end) {:ok, pid} = Task.start(fn -> handle(type, payload) end)
mref = Process.monitor(pid) mref = Process.monitor(pid)
@ -129,7 +154,8 @@ defmodule Pleroma.Web.Federator do
end end
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 %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
i_queue = enqueue_sorted(i_queue, {type, payload}, 1) i_queue = enqueue_sorted(i_queue, {type, payload}, 1)
{i_running_jobs, i_queue} = maybe_start_job(i_running_jobs, i_queue) {i_running_jobs, i_queue} = maybe_start_job(i_running_jobs, i_queue)
@ -160,7 +186,7 @@ defmodule Pleroma.Web.Federator do
def enqueue_sorted(queue, element, priority) do def enqueue_sorted(queue, element, priority) do
[%{item: element, priority: priority} | queue] [%{item: element, priority: priority} | queue]
|> Enum.sort_by(fn (%{priority: priority}) -> priority end) |> Enum.sort_by(fn %{priority: priority} -> priority end)
end end
def queue_pop([%{item: element} | queue]) do def queue_pop([%{item: element} | queue]) do
@ -169,6 +195,7 @@ defmodule Pleroma.Web.Federator do
def ap_enabled_actor(id) do def ap_enabled_actor(id) do
user = User.get_by_ap_id(id) user = User.get_by_ap_id(id)
if User.ap_enabled?(user) do if User.ap_enabled?(user) do
{:ok, user} {:ok, user}
else else

View file

@ -11,8 +11,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
require Logger require Logger
def create_app(conn, params) do def create_app(conn, params) do
with cs <- App.register_changeset(%App{}, params) |> IO.inspect, with cs <- App.register_changeset(%App{}, params) |> IO.inspect(),
{:ok, app} <- Repo.insert(cs) |> IO.inspect do {:ok, app} <- Repo.insert(cs) |> IO.inspect() do
res = %{ res = %{
id: app.id, id: app.id,
client_id: app.client_id, client_id: app.client_id,
@ -25,19 +25,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def update_credentials(%{assigns: %{user: user}} = conn, params) do def update_credentials(%{assigns: %{user: user}} = conn, params) do
original_user = user original_user = user
params = if bio = params["note"] do
params =
if bio = params["note"] do
Map.put(params, "bio", bio) Map.put(params, "bio", bio)
else else
params params
end end
params = if name = params["display_name"] do params =
if name = params["display_name"] do
Map.put(params, "name", name) Map.put(params, "name", name)
else else
params params
end end
user = if avatar = params["avatar"] do user =
if avatar = params["avatar"] do
with %Plug.Upload{} <- avatar, with %Plug.Upload{} <- avatar,
{:ok, object} <- ActivityPub.upload(avatar), {:ok, object} <- ActivityPub.upload(avatar),
change = Ecto.Changeset.change(user, %{avatar: object.data}), change = Ecto.Changeset.change(user, %{avatar: object.data}),
@ -50,7 +54,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
user user
end end
user = if banner = params["header"] do user =
if banner = params["header"] do
with %Plug.Upload{} <- banner, with %Plug.Upload{} <- banner,
{:ok, object} <- ActivityPub.upload(banner), {:ok, object} <- ActivityPub.upload(banner),
new_info <- Map.put(user.info, "banner", object.data), new_info <- Map.put(user.info, "banner", object.data),
@ -69,7 +74,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
if original_user != user do if original_user != user do
CommonAPI.update(user) CommonAPI.update(user)
end end
json conn, AccountView.render("account.json", %{user: user})
json(conn, AccountView.render("account.json", %{user: user}))
else else
_e -> _e ->
conn conn
@ -88,7 +94,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
account = AccountView.render("account.json", %{user: user}) account = AccountView.render("account.json", %{user: user})
json(conn, account) json(conn, account)
else else
_e -> conn _e ->
conn
|> put_status(404) |> put_status(404)
|> json(%{error: "Can't find user"}) |> json(%{error: "Can't find user"})
end end
@ -98,16 +105,16 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def masto_instance(conn, _params) do def masto_instance(conn, _params) do
response = %{ response = %{
uri: Web.base_url, uri: Web.base_url(),
title: Keyword.get(@instance, :name), title: Keyword.get(@instance, :name),
description: "A Pleroma instance, an alternative fediverse server", description: "A Pleroma instance, an alternative fediverse server",
version: Keyword.get(@instance, :version), version: Keyword.get(@instance, :version),
email: Keyword.get(@instance, :email), email: Keyword.get(@instance, :email),
urls: %{ 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, stats: Stats.get_stats(),
thumbnail: Web.base_url <> "/instance/thumbnail.jpeg", thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
max_toot_chars: Keyword.get(@instance, :limit) max_toot_chars: Keyword.get(@instance, :limit)
} }
@ -115,13 +122,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
def peers(conn, _params) do def peers(conn, _params) do
json(conn, Stats.get_peers) json(conn, Stats.get_peers())
end end
defp mastodonized_emoji do defp mastodonized_emoji do
Pleroma.Formatter.get_custom_emoji() Pleroma.Formatter.get_custom_emoji()
|> Enum.map(fn {shortcode, relative_url} -> |> 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, "shortcode" => shortcode,
"static_url" => url, "static_url" => url,
@ -132,16 +140,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def custom_emojis(conn, _params) do def custom_emojis(conn, _params) do
mastodon_emoji = mastodonized_emoji() mastodon_emoji = mastodonized_emoji()
json conn, mastodon_emoji json(conn, mastodon_emoji)
end end
defp add_link_headers(conn, method, activities, param \\ false) do defp add_link_headers(conn, method, activities, param \\ false) do
last = List.last(activities) last = List.last(activities)
first = List.first(activities) first = List.first(activities)
if last do if last do
min = last.id min = last.id
max = first.id max = first.id
{next_url, prev_url} = if param do
{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, max_id: min),
mastodon_api_url(Pleroma.Web.Endpoint, method, param, since_id: max) mastodon_api_url(Pleroma.Web.Endpoint, method, param, since_id: max)
@ -152,6 +163,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
mastodon_api_url(Pleroma.Web.Endpoint, method, since_id: max) mastodon_api_url(Pleroma.Web.Endpoint, method, since_id: max)
} }
end end
conn conn
|> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"") |> put_resp_header("link", "<#{next_url}>; rel=\"next\", <#{prev_url}>; rel=\"prev\"")
else else
@ -160,13 +172,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
def home_timeline(%{assigns: %{user: user}} = conn, params) do def home_timeline(%{assigns: %{user: user}} = conn, params) do
params = params params =
params
|> Map.put("type", ["Create", "Announce"]) |> Map.put("type", ["Create", "Announce"])
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("user", user) |> Map.put("user", user)
activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) activities =
|> Enum.reverse ActivityPub.fetch_activities([user.ap_id | user.following], params)
|> Enum.reverse()
conn conn
|> add_link_headers(:home_timeline, activities) |> add_link_headers(:home_timeline, activities)
@ -174,13 +188,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
def public_timeline(%{assigns: %{user: user}} = conn, params) do def public_timeline(%{assigns: %{user: user}} = conn, params) do
params = params params =
params
|> Map.put("type", ["Create", "Announce"]) |> Map.put("type", ["Create", "Announce"])
|> Map.put("local_only", params["local"] in [true, "True", "true", "1"]) |> Map.put("local_only", params["local"] in [true, "True", "true", "1"])
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
activities = ActivityPub.fetch_public_activities(params) activities =
|> Enum.reverse ActivityPub.fetch_public_activities(params)
|> Enum.reverse()
conn conn
|> add_link_headers(:public_timeline, activities) |> add_link_headers(:public_timeline, activities)
@ -189,13 +205,15 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def user_statuses(%{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 with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do
params = params params =
params
|> Map.put("type", ["Create", "Announce"]) |> Map.put("type", ["Create", "Announce"])
|> Map.put("actor_id", ap_id) |> Map.put("actor_id", ap_id)
|> Map.put("whole_db", true) |> Map.put("whole_db", true)
activities = ActivityPub.fetch_public_activities(params) activities =
|> Enum.reverse ActivityPub.fetch_public_activities(params)
|> Enum.reverse()
conn conn
|> add_link_headers(:user_statuses, activities, params["id"]) |> add_link_headers(:user_statuses, activities, params["id"])
@ -206,19 +224,39 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def get_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id), with %Activity{} = activity <- Repo.get(Activity, id),
true <- ActivityPub.visible_for_user?(activity, user) do 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
end end
def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do def get_context(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id), with %Activity{} = activity <- Repo.get(Activity, id),
activities <- ActivityPub.fetch_activities_for_context(activity.data["context"], %{"blocking_user" => user, "user" => user}), activities <-
activities <- activities |> Enum.filter(fn (%{id: aid}) -> to_string(aid) != to_string(id) end), ActivityPub.fetch_activities_for_context(activity.data["context"], %{
activities <- activities |> Enum.filter(fn (%{data: %{"type" => type}}) -> type == "Create" end), "blocking_user" => user,
grouped_activities <- Enum.group_by(activities, fn (%{id: id}) -> id < activity.id end) do "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 = %{ result = %{
ancestors: StatusView.render("index.json", for: user, activities: grouped_activities[true] || [], as: :activity) |> Enum.reverse, ancestors:
descendants: StatusView.render("index.json", for: user, activities: grouped_activities[false] || [], as: :activity) |> Enum.reverse, 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) json(conn, result)
@ -226,12 +264,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
params = params params =
params
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"]) |> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
|> Map.put("no_attachment_links", true) |> Map.put("no_attachment_links", true)
{:ok, activity} = CommonAPI.post(user, params) {: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 end
def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def delete_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
@ -247,30 +286,32 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_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 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
end end
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do 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), 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 %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
end end
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do 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), with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do %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
end end
def notifications(%{assigns: %{user: user}} = conn, params) do def notifications(%{assigns: %{user: user}} = conn, params) do
notifications = Notification.for_user(user, params) notifications = Notification.for_user(user, params)
result = Enum.map(notifications, fn x ->
result =
Enum.map(notifications, fn x ->
render_notification(user, x) render_notification(user, x)
end) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
conn conn
|> add_link_headers(:notifications, notifications) |> add_link_headers(:notifications, notifications)
@ -306,27 +347,26 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do def relationships(%{assigns: %{user: user}} = conn, %{"id" => id}) do
id = List.wrap(id) id = List.wrap(id)
q = from u in User, q = from(u in User, where: u.id in ^id)
where: u.id in ^id
targets = Repo.all(q) targets = Repo.all(q)
render conn, AccountView, "relationships.json", %{user: user, targets: targets} render(conn, AccountView, "relationships.json", %{user: user, targets: targets})
end end
def upload(%{assigns: %{user: _}} = conn, %{"file" => file}) do def upload(%{assigns: %{user: _}} = conn, %{"file" => file}) do
with {:ok, object} <- ActivityPub.upload(file) do with {:ok, object} <- ActivityPub.upload(file) do
data = object.data data =
object.data
|> Map.put("id", object.id) |> Map.put("id", object.id)
render conn, StatusView, "attachment.json", %{attachment: data} render(conn, StatusView, "attachment.json", %{attachment: data})
end end
end end
def favourited_by(conn, %{"id" => id}) do def favourited_by(conn, %{"id" => id}) do
with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do with %Activity{data: %{"object" => %{"likes" => likes}}} <- Repo.get(Activity, id) do
q = from u in User, q = from(u in User, where: u.ap_id in ^likes)
where: u.ap_id in ^likes
users = Repo.all(q) users = Repo.all(q)
render conn, AccountView, "accounts.json", %{users: users, as: :user} render(conn, AccountView, "accounts.json", %{users: users, as: :user})
else else
_ -> json(conn, []) _ -> json(conn, [])
end end
@ -334,23 +374,24 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def reblogged_by(conn, %{"id" => id}) do def reblogged_by(conn, %{"id" => id}) do
with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do with %Activity{data: %{"object" => %{"announcements" => announces}}} <- Repo.get(Activity, id) do
q = from u in User, q = from(u in User, where: u.ap_id in ^announces)
where: u.ap_id in ^announces
users = Repo.all(q) users = Repo.all(q)
render conn, AccountView, "accounts.json", %{users: users, as: :user} render(conn, AccountView, "accounts.json", %{users: users, as: :user})
else else
_ -> json(conn, []) _ -> json(conn, [])
end end
end end
def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
params = params params =
params
|> Map.put("type", "Create") |> Map.put("type", "Create")
|> Map.put("local_only", !!params["local"]) |> Map.put("local_only", !!params["local"])
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
activities = ActivityPub.fetch_public_activities(params) activities =
|> Enum.reverse ActivityPub.fetch_public_activities(params)
|> Enum.reverse()
conn conn
|> add_link_headers(:hashtag_timeline, activities, params["tag"]) |> add_link_headers(:hashtag_timeline, activities, params["tag"])
@ -361,14 +402,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def followers(conn, %{"id" => id}) do def followers(conn, %{"id" => id}) do
with %User{} = user <- Repo.get(User, id), with %User{} = user <- Repo.get(User, id),
{:ok, followers} <- User.get_followers(user) do {: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
end end
def following(conn, %{"id" => id}) do def following(conn, %{"id" => id}) do
with %User{} = user <- Repo.get(User, id), with %User{} = user <- Repo.get(User, id),
{:ok, followers} <- User.get_friends(user) do {: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
end end
@ -376,7 +417,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = followed <- Repo.get(User, id), with %User{} = followed <- Repo.get(User, id),
{:ok, follower} <- User.follow(follower, followed), {:ok, follower} <- User.follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed) do {: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 else
{:error, message} -> {:error, message} ->
conn conn
@ -389,7 +430,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
with %User{} = followed <- Repo.get_by(User, nickname: uri), with %User{} = followed <- Repo.get_by(User, nickname: uri),
{:ok, follower} <- User.follow(follower, followed), {:ok, follower} <- User.follow(follower, followed),
{:ok, _activity} <- ActivityPub.follow(follower, followed) do {:ok, _activity} <- ActivityPub.follow(follower, followed) do
render conn, AccountView, "account.json", %{user: followed} render(conn, AccountView, "account.json", %{user: followed})
else else
{:error, message} -> {:error, message} ->
conn conn
@ -402,19 +443,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do def unfollow(%{assigns: %{user: follower}} = conn, %{"id" => id}) do
with %User{} = followed <- Repo.get(User, id), with %User{} = followed <- Repo.get(User, id),
{:ok, follower, follow_activity} <- User.unfollow(follower, followed), {:ok, follower, follow_activity} <- User.unfollow(follower, followed),
{ :ok, _activity } <- ActivityPub.insert(%{ {:ok, _activity} <-
ActivityPub.insert(%{
"type" => "Undo", "type" => "Undo",
"actor" => follower.ap_id, "actor" => follower.ap_id,
"object" => follow_activity.data["id"] # get latest Follow for these users # get latest Follow for these users
"object" => follow_activity.data["id"]
}) do }) do
render conn, AccountView, "relationship.json", %{user: follower, target: followed} render(conn, AccountView, "relationship.json", %{user: follower, target: followed})
end end
end end
def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do def block(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
with %User{} = blocked <- Repo.get(User, id), with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.block(blocker, blocked) do {: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 else
{:error, message} -> {:error, message} ->
conn conn
@ -426,7 +469,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do def unblock(%{assigns: %{user: blocker}} = conn, %{"id" => id}) do
with %User{} = blocked <- Repo.get(User, id), with %User{} = blocked <- Repo.get(User, id),
{:ok, blocker} <- User.unblock(blocker, blocked) do {: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 else
{:error, message} -> {:error, message} ->
conn conn
@ -438,7 +481,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
# TODO: Use proper query # TODO: Use proper query
def blocks(%{assigns: %{user: user}} = conn, _) do def blocks(%{assigns: %{user: user}} = conn, _) do
with blocked_users <- user.info["blocks"] || [], 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) res = AccountView.render("accounts.json", users: accounts, for: user, as: :user)
json(conn, res) json(conn, res)
end end
@ -447,7 +490,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do
accounts = User.search(query, params["resolve"] == "true") accounts = User.search(query, params["resolve"] == "true")
fetched = if Regex.match?(~r/https?:/, query) do fetched =
if Regex.match?(~r/https?:/, query) do
with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do
activities activities
else else
@ -455,15 +499,25 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
end || [] end || []
q = from a in Activity, q =
from(
a in Activity,
where: fragment("?->>'type' = 'Create'", a.data), where: fragment("?->>'type' = 'Create'", a.data),
where: fragment("to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", a.data, ^query), where:
fragment(
"to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)",
a.data,
^query
),
limit: 20 limit: 20
)
statuses = Repo.all(q) ++ fetched statuses = Repo.all(q) ++ fetched
res = %{ res = %{
"accounts" => AccountView.render("accounts.json", users: accounts, for: user, as: :user), "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" => [] "hashtags" => []
} }
@ -479,28 +533,34 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
end end
def favourites(%{assigns: %{user: user}} = conn, _) do def favourites(%{assigns: %{user: user}} = conn, _) do
params = %{} params =
%{}
|> Map.put("type", "Create") |> Map.put("type", "Create")
|> Map.put("favorited_by", user.ap_id) |> Map.put("favorited_by", user.ap_id)
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
activities = ActivityPub.fetch_public_activities(params) activities =
|> Enum.reverse ActivityPub.fetch_public_activities(params)
|> Enum.reverse()
conn conn
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity}) |> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
end end
def index(%{assigns: %{user: user}} = conn, _params) do def index(%{assigns: %{user: user}} = conn, _params) do
token = conn token =
conn
|> get_session(:oauth_token) |> get_session(:oauth_token)
if user && token do if user && token do
mastodon_emoji = mastodonized_emoji() mastodon_emoji = mastodonized_emoji()
accounts = Map.put(%{}, user.id, AccountView.render("account.json", %{user: user})) accounts = Map.put(%{}, user.id, AccountView.render("account.json", %{user: user}))
initial_state = %{
initial_state =
%{
meta: %{ meta: %{
streaming_api_base_url: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"), streaming_api_base_url:
String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws"),
access_token: token, access_token: token,
locale: "en", locale: "en",
domain: Pleroma.Web.Endpoint.host(), domain: Pleroma.Web.Endpoint.host(),
@ -566,7 +626,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
accounts: accounts, accounts: accounts,
custom_emojis: mastodon_emoji, custom_emojis: mastodon_emoji,
char_limit: Keyword.get(@instance, :limit) char_limit: Keyword.get(@instance, :limit)
} |> Jason.encode! }
|> Jason.encode!()
conn conn
|> put_layout(false) |> put_layout(false)
|> render(MastodonView, "index.html", %{initial_state: initial_state}) |> render(MastodonView, "index.html", %{initial_state: initial_state})
@ -586,7 +648,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
{:ok, app} {:ok, app}
else else
_e -> _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) Repo.insert(cs)
end end
end end
@ -615,8 +683,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do def relationship_noop(%{assigns: %{user: user}} = conn, %{"id" => id}) do
Logger.debug("Unimplemented, returning unmodified relationship") Logger.debug("Unimplemented, returning unmodified relationship")
with %User{} = target <- Repo.get(User, id) do 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
end end
@ -632,20 +701,53 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
def render_notification(user, %{id: id, activity: activity, inserted_at: created_at} = _params) 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"]) actor = User.get_cached_by_ap_id(activity.data["actor"])
created_at = NaiveDateTime.to_iso8601(created_at)
created_at =
NaiveDateTime.to_iso8601(created_at)
|> String.replace(~r/(\.\d+)?$/, ".000Z", global: false) |> String.replace(~r/(\.\d+)?$/, ".000Z", global: false)
case activity.data["type"] do case activity.data["type"] do
"Create" -> "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" -> "Like" ->
liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) 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" -> "Announce" ->
announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) 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" -> "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 end
end end

View file

@ -4,17 +4,23 @@ defmodule Pleroma.Web.MastodonAPI.MastodonSocket do
alias Pleroma.Web.OAuth.Token alias Pleroma.Web.OAuth.Token
alias Pleroma.{User, Repo} alias Pleroma.{User, Repo}
transport :streaming, Phoenix.Transports.WebSocket.Raw, transport(
timeout: :infinity # We never receive data. :streaming,
Phoenix.Transports.WebSocket.Raw,
# We never receive data.
timeout: :infinity
)
def connect(params, socket) do def connect(params, socket) do
with token when not is_nil(token) <- params["access_token"], with token when not is_nil(token) <- params["access_token"],
%Token{user_id: user_id} <- Repo.get_by(Token, token: token), %Token{user_id: user_id} <- Repo.get_by(Token, token: token),
%User{} = user <- Repo.get(User, user_id), %User{} = user <- Repo.get(User, user_id),
stream when stream in ["public", "public:local", "user"] <- params["stream"] do stream when stream in ["public", "public:local", "user"] <- params["stream"] do
socket = socket socket =
socket
|> assign(:topic, params["stream"]) |> assign(:topic, params["stream"])
|> assign(:user, user) |> assign(:user, user)
Pleroma.Web.Streamer.add_socket(params["stream"], socket) Pleroma.Web.Streamer.add_socket(params["stream"], socket)
{:ok, socket} {:ok, socket}
else else

View file

@ -10,37 +10,52 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
defp get_replied_to_activities(activities) do defp get_replied_to_activities(activities) do
activities activities
|> Enum.map(fn |> Enum.map(fn
(%{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}}) -> %{data: %{"type" => "Create", "object" => %{"inReplyTo" => inReplyTo}}} ->
(inReplyTo != "") && inReplyTo inReplyTo != "" && inReplyTo
_ -> nil
_ ->
nil
end) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
|> Activity.create_activity_by_object_id_query() |> Activity.create_activity_by_object_id_query()
|> Repo.all |> Repo.all()
|> Enum.reduce(%{}, fn(activity, acc) -> Map.put(acc,activity.data["object"]["id"], activity) end) |> Enum.reduce(%{}, fn activity, acc ->
Map.put(acc, activity.data["object"]["id"], activity)
end)
end end
def render("index.json", opts) do def render("index.json", opts) do
replied_to_activities = get_replied_to_activities(opts.activities) 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 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"]) user = User.get_cached_by_ap_id(activity.data["actor"])
created_at = Utils.to_masto_date(activity.data["published"]) created_at = Utils.to_masto_date(activity.data["published"])
reblogged = Activity.get_create_activity_by_object_ap_id(object) reblogged = Activity.get_create_activity_by_object_ap_id(object)
reblogged = render("status.json", Map.put(opts, :activity, reblogged)) reblogged = render("status.json", Map.put(opts, :activity, reblogged))
mentions = activity.recipients mentions =
|> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) activity.recipients
|> Enum.filter(&(&1)) |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.map(fn (user) -> AccountView.render("mention.json", %{user: user}) end) |> Enum.filter(& &1)
|> Enum.map(fn user -> AccountView.render("mention.json", %{user: user}) end)
%{ %{
id: to_string(activity.id), id: to_string(activity.id),
uri: object, 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}), account: AccountView.render("account.json", %{user: user}),
in_reply_to_id: nil, in_reply_to_id: nil,
in_reply_to_account_id: nil, in_reply_to_account_id: nil,
@ -89,22 +104,25 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
tags = object["tag"] || [] tags = object["tag"] || []
sensitive = object["sensitive"] || Enum.member?(tags, "nsfw") sensitive = object["sensitive"] || Enum.member?(tags, "nsfw")
mentions = activity.recipients mentions =
|> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) activity.recipients
|> Enum.filter(&(&1)) |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.map(fn (user) -> AccountView.render("mention.json", %{user: user}) 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"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) 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"]) created_at = Utils.to_masto_date(object["published"])
reply_to = get_reply_to(activity, opts) reply_to = get_reply_to(activity, opts)
reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"]) reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"])
emojis = (activity.data["object"]["emoji"] || []) emojis =
(activity.data["object"]["emoji"] || [])
|> Enum.map(fn {name, url} -> |> Enum.map(fn {name, url} ->
name = HtmlSanitizeEx.strip_tags(name) name = HtmlSanitizeEx.strip_tags(name)
url = HtmlSanitizeEx.strip_tags(url) url = HtmlSanitizeEx.strip_tags(url)
@ -131,7 +149,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
visibility: get_visibility(object), visibility: get_visibility(object),
media_attachments: attachments |> Enum.take(4), media_attachments: attachments |> Enum.take(4),
mentions: mentions, mentions: mentions,
tags: [], # fix, # fix,
tags: [],
application: %{ application: %{
name: "Web", name: "Web",
website: nil website: nil
@ -145,10 +164,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
public = "https://www.w3.org/ns/activitystreams#Public" public = "https://www.w3.org/ns/activitystreams#Public"
to = object["to"] || [] to = object["to"] || []
cc = object["cc"] || [] cc = object["cc"] || []
cond do cond do
public in to -> "public" public in to -> "public"
public in cc -> "unlisted" public in cc -> "unlisted"
Enum.any?(to, &(String.contains?(&1, "/followers"))) -> "private" Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
true -> "direct" true -> "direct"
end end
end end
@ -156,7 +176,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
def render("attachment.json", %{attachment: attachment}) do def render("attachment.json", %{attachment: attachment}) do
[%{"mediaType" => media_type, "href" => href} | _] = attachment["url"] [%{"mediaType" => media_type, "href" => href} | _] = attachment["url"]
type = cond do type =
cond do
String.contains?(media_type, "image") -> "image" String.contains?(media_type, "image") -> "image"
String.contains?(media_type, "video") -> "video" String.contains?(media_type, "video") -> "video"
String.contains?(media_type, "audio") -> "audio" String.contains?(media_type, "audio") -> "audio"

View file

@ -4,47 +4,59 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
@httpoison Application.get_env(:pleroma, :httpoison) @httpoison Application.get_env(:pleroma, :httpoison)
@max_body_length 25 * 1048576 @max_body_length 25 * 1_048_576
@cache_control %{ @cache_control %{
default: "public, max-age=1209600", 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 def remote(conn, %{"sig" => sig, "url" => url}) do
config = Application.get_env(:pleroma, :media_proxy, []) config = Application.get_env(:pleroma, :media_proxy, [])
with \
true <- Keyword.get(config, :enabled, false), with true <- Keyword.get(config, :enabled, false),
{:ok, url} <- Pleroma.Web.MediaProxy.decode_url(sig, url), {:ok, url} <- Pleroma.Web.MediaProxy.decode_url(sig, url),
{:ok, content_type, body} <- proxy_request(url) {:ok, content_type, body} <- proxy_request(url) do
do
conn conn
|> put_resp_content_type(content_type) |> put_resp_content_type(content_type)
|> set_cache_header(:default) |> set_cache_header(:default)
|> send_resp(200, body) |> send_resp(200, body)
else else
false -> send_error(conn, 404) false ->
{:error, :invalid_signature} -> send_error(conn, 403) send_error(conn, 404)
{:error, {:http, _, url}} -> redirect_or_error(conn, url, Keyword.get(config, :redirect_on_failure, true))
{:error, :invalid_signature} ->
send_error(conn, 403)
{:error, {:http, _, url}} ->
redirect_or_error(conn, url, Keyword.get(config, :redirect_on_failure, true))
end end
end end
defp proxy_request(link) do defp proxy_request(link) do
headers = [{"user-agent", "Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{Application.get_env(:pleroma, :instance)[:email]}>"}] headers = [
options = @httpoison.process_request_options([:insecure, {:follow_redirect, true}]) ++ [{:pool, :default}] {"user-agent",
with \ "Pleroma/MediaProxy; #{Pleroma.Web.base_url()} <#{
{:ok, 200, headers, client} <- :hackney.request(:get, link, headers, "", options), Application.get_env(:pleroma, :instance)[:email]
headers = Enum.into(headers, Map.new), }>"}
]
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), {:ok, body} <- proxy_request_body(client),
content_type <- proxy_request_content_type(headers, body) content_type <- proxy_request_content_type(headers, body) do
do
{:ok, content_type, body} {:ok, content_type, body}
else else
{:ok, status, _, _} -> {: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, {:http, :bad_status, link}}
{:error, error} -> {: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}} {:error, {:http, error, link}}
end end
end end
@ -63,6 +75,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
end end
defp proxy_request_body(client), do: proxy_request_body(client, <<>>) defp proxy_request_body(client), do: proxy_request_body(client, <<>>)
defp proxy_request_body(client, body) when byte_size(body) < @max_body_length do defp proxy_request_body(client, body) when byte_size(body) < @max_body_length do
case :hackney.stream_body(client) do case :hackney.stream_body(client) do
{:ok, data} -> proxy_request_body(client, <<body::binary, data::binary>>) {:ok, data} -> proxy_request_body(client, <<body::binary, data::binary>>)
@ -70,6 +83,7 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
{:error, reason} -> {:error, reason} {:error, reason} -> {:error, reason}
end end
end end
defp proxy_request_body(client, _) do defp proxy_request_body(client, _) do
:hackney.close(client) :hackney.close(client)
{:error, :body_too_large} {:error, :body_too_large}
@ -80,5 +94,4 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do
defp proxy_request_content_type(headers, _body) do defp proxy_request_content_type(headers, _body) do
headers["Content-Type"] || headers["content-type"] || "image/jpeg" headers["Content-Type"] || headers["content-type"] || "image/jpeg"
end end
end end

View file

@ -7,14 +7,15 @@ defmodule Pleroma.Web.MediaProxy do
def url(url) do def url(url) do
config = Application.get_env(:pleroma, :media_proxy, []) 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 url
else else
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
base64 = Base.url_encode64(url, @base64_opts) base64 = Base.url_encode64(url, @base64_opts)
sig = :crypto.hmac(:sha, secret, base64) sig = :crypto.hmac(:sha, secret, base64)
sig64 = sig |> Base.url_encode64(@base64_opts) 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
end end
@ -22,11 +23,11 @@ defmodule Pleroma.Web.MediaProxy do
secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base] secret = Application.get_env(:pleroma, Pleroma.Web.Endpoint)[:secret_key_base]
sig = Base.url_decode64!(sig, @base64_opts) sig = Base.url_decode64!(sig, @base64_opts)
local_sig = :crypto.hmac(:sha, secret, url) local_sig = :crypto.hmac(:sha, secret, url)
if local_sig == sig do if local_sig == sig do
{:ok, Base.url_decode64!(url, @base64_opts)} {:ok, Base.url_decode64!(url, @base64_opts)}
else else
{:error, :invalid_signature} {:error, :invalid_signature}
end end
end end
end end

View file

@ -3,25 +3,26 @@ defmodule Pleroma.Web.OAuth.App do
import Ecto.{Changeset} import Ecto.{Changeset}
schema "apps" do schema "apps" do
field :client_name, :string field(:client_name, :string)
field :redirect_uris, :string field(:redirect_uris, :string)
field :scopes, :string field(:scopes, :string)
field :website, :string field(:website, :string)
field :client_id, :string field(:client_id, :string)
field :client_secret, :string field(:client_secret, :string)
timestamps() timestamps()
end end
def register_changeset(struct, params \\ %{}) do def register_changeset(struct, params \\ %{}) do
changeset = struct changeset =
struct
|> cast(params, [:client_name, :redirect_uris, :scopes, :website]) |> cast(params, [:client_name, :redirect_uris, :scopes, :website])
|> validate_required([:client_name, :redirect_uris, :scopes]) |> validate_required([:client_name, :redirect_uris, :scopes])
if changeset.valid? do if changeset.valid? do
changeset changeset
|> put_change(:client_id, :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) |> put_change(:client_secret, :crypto.strong_rand_bytes(32) |> Base.url_encode64())
else else
changeset changeset
end end

View file

@ -7,24 +7,24 @@ defmodule Pleroma.Web.OAuth.Authorization do
import Ecto.{Changeset} import Ecto.{Changeset}
schema "oauth_authorizations" do schema "oauth_authorizations" do
field :token, :string field(:token, :string)
field :valid_until, :naive_datetime field(:valid_until, :naive_datetime)
field :used, :boolean, default: false field(:used, :boolean, default: false)
belongs_to :user, Pleroma.User belongs_to(:user, Pleroma.User)
belongs_to :app, Pleroma.App belongs_to(:app, Pleroma.App)
timestamps() timestamps()
end end
def create_authorization(%App{} = app, %User{} = user) do 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{ authorization = %Authorization{
token: token, token: token,
used: false, used: false,
user_id: user.id, user_id: user.id,
app_id: app.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) Repo.insert(authorization)
@ -37,11 +37,12 @@ defmodule Pleroma.Web.OAuth.Authorization do
end end
def use_token(%Authorization{used: false, valid_until: valid_until} = auth) do 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})) Repo.update(use_changeset(auth, %{used: true}))
else else
{:error, "token expired"} {:error, "token expired"}
end end
end end
def use_token(%Authorization{used: true}), do: {:error, "already used"} def use_token(%Authorization{used: true}), do: {:error, "already used"}
end end

View file

@ -8,5 +8,4 @@ defmodule Pleroma.Web.OAuth.FallbackController do
|> put_flash(:error, "Invalid Username/Password") |> put_flash(:error, "Invalid Username/Password")
|> OAuthController.authorize(conn.params) |> OAuthController.authorize(conn.params)
end end
end end

View file

@ -5,38 +5,49 @@ defmodule Pleroma.Web.OAuth.OAuthController do
alias Pleroma.{Repo, User} alias Pleroma.{Repo, User}
alias Comeonin.Pbkdf2 alias Comeonin.Pbkdf2
plug :fetch_session plug(:fetch_session)
plug :fetch_flash plug(:fetch_flash)
action_fallback Pleroma.Web.OAuth.FallbackController action_fallback(Pleroma.Web.OAuth.FallbackController)
def authorize(conn, params) do def authorize(conn, params) do
render conn, "show.html", %{ render(conn, "show.html", %{
response_type: params["response_type"], response_type: params["response_type"],
client_id: params["client_id"], client_id: params["client_id"],
scope: params["scope"], scope: params["scope"],
redirect_uri: params["redirect_uri"], redirect_uri: params["redirect_uri"],
state: params["state"] state: params["state"]
} })
end 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), with %User{} = user <- User.get_cached_by_nickname(name),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
%App{} = app <- Repo.get_by(App, client_id: client_id), %App{} = app <- Repo.get_by(App, client_id: client_id),
{:ok, auth} <- Authorization.create_authorization(app, user) do {:ok, auth} <- Authorization.create_authorization(app, user) do
if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" do if redirect_uri == "urn:ietf:wg:oauth:2.0:oob" do
render conn, "results.html", %{ render(conn, "results.html", %{
auth: auth auth: auth
} })
else else
connector = if String.contains?(redirect_uri, "?"), do: "&", else: "?" connector = if String.contains?(redirect_uri, "?"), do: "&", else: "?"
url = "#{redirect_uri}#{connector}code=#{auth.token}" url = "#{redirect_uri}#{connector}code=#{auth.token}"
url = if params["state"] do
url =
if params["state"] do
url <> "&state=#{params["state"]}" url <> "&state=#{params["state"]}"
else else
url url
end end
redirect(conn, external: url) redirect(conn, external: url)
end end
end end
@ -45,7 +56,12 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# TODO # TODO
# - proper scope handling # - proper scope handling
def token_exchange(conn, %{"grant_type" => "authorization_code"} = params) do 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"]), fixed_token = fix_padding(params["code"]),
%Authorization{} = auth <- Repo.get_by(Authorization, token: fixed_token, app_id: app.id), %Authorization{} = auth <- Repo.get_by(Authorization, token: fixed_token, app_id: app.id),
{:ok, token} <- Token.exchange_token(app, auth) do {:ok, token} <- Token.exchange_token(app, auth) do
@ -56,6 +72,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
expires_in: 60 * 10, expires_in: 60 * 10,
scope: "read write follow" scope: "read write follow"
} }
json(conn, response) json(conn, response)
else else
_error -> json(conn, %{error: "Invalid credentials"}) _error -> json(conn, %{error: "Invalid credentials"})
@ -64,8 +81,16 @@ defmodule Pleroma.Web.OAuth.OAuthController do
# TODO # TODO
# - investigate a way to verify the user wants to grant read/write/follow once scope handling is done # - 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 def token_exchange(
with %App{} = app <- Repo.get_by(App, client_id: params["client_id"], client_secret: params["client_secret"]), 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), %User{} = user <- User.get_cached_by_nickname(name),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
{:ok, auth} <- Authorization.create_authorization(app, user), {:ok, auth} <- Authorization.create_authorization(app, user),
@ -77,6 +102,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do
expires_in: 60 * 10, expires_in: 60 * 10,
scope: "read write follow" scope: "read write follow"
} }
json(conn, response) json(conn, response)
else else
_error -> json(conn, %{error: "Invalid credentials"}) _error -> json(conn, %{error: "Invalid credentials"})
@ -86,6 +112,6 @@ defmodule Pleroma.Web.OAuth.OAuthController do
defp fix_padding(token) do defp fix_padding(token) do
token token
|> Base.url_decode64!(padding: false) |> Base.url_decode64!(padding: false)
|> Base.url_encode64 |> Base.url_encode64()
end end
end end

View file

@ -5,11 +5,11 @@ defmodule Pleroma.Web.OAuth.Token do
alias Pleroma.Web.OAuth.{Token, App, Authorization} alias Pleroma.Web.OAuth.{Token, App, Authorization}
schema "oauth_tokens" do schema "oauth_tokens" do
field :token, :string field(:token, :string)
field :refresh_token, :string field(:refresh_token, :string)
field :valid_until, :naive_datetime field(:valid_until, :naive_datetime)
belongs_to :user, Pleroma.User belongs_to(:user, Pleroma.User)
belongs_to :app, Pleroma.App belongs_to(:app, Pleroma.App)
timestamps() timestamps()
end end
@ -22,15 +22,15 @@ defmodule Pleroma.Web.OAuth.Token do
end end
def create_token(%App{} = app, %User{} = user) do def create_token(%App{} = app, %User{} = user) do
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 refresh_token = :crypto.strong_rand_bytes(32) |> Base.url_encode64()
token = %Token{ token = %Token{
token: token, token: token,
refresh_token: refresh_token, refresh_token: refresh_token,
user_id: user.id, user_id: user.id,
app_id: app.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) Repo.insert(token)

View file

@ -13,42 +13,60 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end end
defp get_in_reply_to(%{"object" => %{"inReplyTo" => in_reply_to}}) do 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 end
defp get_in_reply_to(_), do: [] defp get_in_reply_to(_), do: []
defp get_mentions(to) do defp get_mentions(to) do
Enum.map(to, fn (id) -> Enum.map(to, fn id ->
cond do cond do
# Special handling for the AP/Ostatus public collections # Special handling for the AP/Ostatus public collections
"https://www.w3.org/ns/activitystreams#Public" == id -> "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. # 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 -> 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) end)
end end
defp get_links(%{local: true, data: data}) do 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: ['application/atom+xml'], href: h.(data["object"]["id"]), rel: 'self'], []},
{:link, [type: ['text/html'], href: h.(data["object"]["id"]), rel: 'alternate'], []} {:link, [type: ['text/html'], href: h.(data["object"]["id"]), rel: 'alternate'], []}
] ]
end end
defp get_links(%{local: false, defp get_links(%{
local: false,
data: %{ data: %{
"object" => %{ "object" => %{
"external_url" => external_url "external_url" => external_url
} }
}}) do }
}) 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'], []} {:link, [type: ['text/html'], href: h.(external_url), rel: 'alternate'], []}
] ]
@ -57,40 +75,47 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
defp get_links(_activity), do: [] defp get_links(_activity), do: []
defp get_emoji_links(emojis) 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)], []} {:link, [name: to_charlist(emoji), rel: 'emoji', href: to_charlist(file)], []}
end) end)
end end
def to_simple_form(activity, user, with_author \\ false) def to_simple_form(activity, user, with_author \\ false)
def to_simple_form(%{data: %{"object" => %{"type" => "Note"}}} = activity, user, with_author) do 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"] updated_at = activity.data["object"]["published"]
inserted_at = activity.data["object"]["published"] inserted_at = activity.data["object"]["published"]
attachments = Enum.map(activity.data["object"]["attachment"] || [], fn(attachment) -> attachments =
Enum.map(activity.data["object"]["attachment"] || [], fn attachment ->
url = hd(attachment["url"]) url = hd(attachment["url"])
{:link, [rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])], []}
{:link,
[rel: 'enclosure', href: to_charlist(url["href"]), type: to_charlist(url["mediaType"])],
[]}
end) end)
in_reply_to = get_in_reply_to(activity.data) in_reply_to = get_in_reply_to(activity.data)
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
mentions = activity.recipients |> get_mentions mentions = activity.recipients |> get_mentions
categories = (activity.data["object"]["tag"] || []) categories =
|> Enum.map(fn (tag) -> (activity.data["object"]["tag"] || [])
|> Enum.map(fn tag ->
if is_binary(tag) do if is_binary(tag) do
{:category, [term: to_charlist(tag)], []} {:category, [term: to_charlist(tag)], []}
else else
nil nil
end end
end) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
emoji_links = get_emoji_links(activity.data["object"]["emoji"] || %{}) emoji_links = get_emoji_links(activity.data["object"]["emoji"] || %{})
summary = if activity.data["object"]["summary"] do summary =
if activity.data["object"]["summary"] do
[{:summary, [], h.(activity.data["object"]["summary"])}] [{:summary, [], h.(activity.data["object"]["summary"])}]
else else
[] []
@ -99,18 +124,23 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
[ [
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/post']}, {:"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}']}, {: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)}, {:published, h.(inserted_at)},
{:updated, h.(updated_at)}, {:updated, h.(updated_at)},
{:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])}, {:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])},
{:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}
] ++ summary ++ get_links(activity) ++ categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links ] ++
summary ++
get_links(activity) ++
categories ++ attachments ++ in_reply_to ++ author ++ mentions ++ emoji_links
end end
def to_simple_form(%{data: %{"type" => "Like"}} = activity, user, with_author) do 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"] updated_at = activity.data["published"]
inserted_at = activity.data["published"] inserted_at = activity.data["published"]
@ -126,9 +156,11 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
{:content, [type: 'html'], ['#{user.nickname} favorited something']}, {:content, [type: 'html'], ['#{user.nickname} favorited something']},
{:published, h.(inserted_at)}, {:published, h.(inserted_at)},
{:updated, h.(updated_at)}, {:updated, h.(updated_at)},
{:"activity:object", [ {:"activity:object",
[
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/note']},
{:id, h.(activity.data["object"])}, # For notes, federate the object id. # For notes, federate the object id.
{:id, h.(activity.data["object"])}
]}, ]},
{:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])}, {:"ostatus:conversation", [ref: h.(activity.data["context"])], h.(activity.data["context"])},
{:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []}, {:link, [ref: h.(activity.data["context"]), rel: 'ostatus:conversation'], []},
@ -138,7 +170,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end end
def to_simple_form(%{data: %{"type" => "Announce"}} = activity, user, with_author) do 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"] updated_at = activity.data["published"]
inserted_at = activity.data["published"] inserted_at = activity.data["published"]
@ -152,6 +184,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true) retweeted_xml = to_simple_form(retweeted_activity, retweeted_user, true)
mentions = activity.recipients |> get_mentions mentions = activity.recipients |> get_mentions
[ [
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/share']},
@ -168,7 +201,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
end end
def to_simple_form(%{data: %{"type" => "Follow"}} = activity, user, with_author) do 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"] updated_at = activity.data["published"]
inserted_at = activity.data["published"] inserted_at = activity.data["published"]
@ -176,26 +209,29 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: [] author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
mentions = (activity.recipients || []) |> get_mentions mentions = (activity.recipients || []) |> get_mentions
[ [
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/follow']},
{:id, h.(activity.data["id"])}, {:id, h.(activity.data["id"])},
{:title, ['#{user.nickname} started following #{activity.data["object"]}']}, {: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)}, {:published, h.(inserted_at)},
{:updated, h.(updated_at)}, {:updated, h.(updated_at)},
{:"activity:object", [ {:"activity:object",
[
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
{:id, h.(activity.data["object"])}, {:id, h.(activity.data["object"])},
{:uri, h.(activity.data["object"])}, {:uri, h.(activity.data["object"])}
]}, ]},
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
] ++ mentions ++ author ] ++ mentions ++ author
end end
# Only undos of follow for now. Will need to get redone once there are more # 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 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"] updated_at = activity.data["published"]
inserted_at = activity.data["published"] inserted_at = activity.data["published"]
@ -204,25 +240,28 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
follow_activity = Activity.get_by_ap_id(activity.data["object"]) follow_activity = Activity.get_by_ap_id(activity.data["object"])
mentions = (activity.recipients || []) |> get_mentions mentions = (activity.recipients || []) |> get_mentions
[ [
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']}, {:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
{:id, h.(activity.data["id"])}, {:id, h.(activity.data["id"])},
{:title, ['#{user.nickname} stopped following #{follow_activity.data["object"]}']}, {: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)}, {:published, h.(inserted_at)},
{:updated, h.(updated_at)}, {:updated, h.(updated_at)},
{:"activity:object", [ {:"activity:object",
[
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']}, {:"activity:object-type", ['http://activitystrea.ms/schema/1.0/person']},
{:id, h.(follow_activity.data["object"])}, {:id, h.(follow_activity.data["object"])},
{:uri, h.(follow_activity.data["object"])}, {:uri, h.(follow_activity.data["object"])}
]}, ]},
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}, {:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
] ++ mentions ++ author ] ++ mentions ++ author
end end
def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do 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"] updated_at = activity.data["published"]
inserted_at = activity.data["published"] inserted_at = activity.data["published"]
@ -243,14 +282,18 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenter do
def to_simple_form(_, _, _), do: nil def to_simple_form(_, _, _), do: nil
def wrap_with_entry(simple_form) do def wrap_with_entry(simple_form) do
[{ [
:entry, [ {
:entry,
[
xmlns: 'http://www.w3.org/2005/Atom', xmlns: 'http://www.w3.org/2005/Atom',
"xmlns:thr": 'http://purl.org/syndication/thread/1.0', "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
"xmlns:activity": 'http://activitystrea.ms/spec/1.0/', "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
"xmlns:poco": 'http://portablecontacts.net/spec/1.0', "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
"xmlns:ostatus": 'http://ostatus.org/schema/1.0' "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
], simple_form ],
}] simple_form
}
]
end end
end end

View file

@ -5,44 +5,57 @@ defmodule Pleroma.Web.OStatus.FeedRepresenter do
alias Pleroma.Web.MediaProxy alias Pleroma.Web.MediaProxy
def to_simple_form(user, activities, _users) do def to_simple_form(user, activities, _users) do
most_recent_update = (List.first(activities) || user).updated_at most_recent_update =
|> NaiveDateTime.to_iso8601 (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) last_activity = List.last(activities)
entries = activities entries =
|> Enum.map(fn(activity) -> activities
|> Enum.map(fn activity ->
{:entry, ActivityRepresenter.to_simple_form(activity, user)} {:entry, ActivityRepresenter.to_simple_form(activity, user)}
end) end)
|> Enum.filter(fn ({_, form}) -> form end) |> Enum.filter(fn {_, form} -> form end)
[{ [
:feed, [ {
:feed,
[
xmlns: 'http://www.w3.org/2005/Atom', xmlns: 'http://www.w3.org/2005/Atom',
"xmlns:thr": 'http://purl.org/syndication/thread/1.0', "xmlns:thr": 'http://purl.org/syndication/thread/1.0',
"xmlns:activity": 'http://activitystrea.ms/spec/1.0/', "xmlns:activity": 'http://activitystrea.ms/spec/1.0/',
"xmlns:poco": 'http://portablecontacts.net/spec/1.0', "xmlns:poco": 'http://portablecontacts.net/spec/1.0',
"xmlns:ostatus": 'http://ostatus.org/schema/1.0' "xmlns:ostatus": 'http://ostatus.org/schema/1.0'
], [ ],
[
{:id, h.(OStatus.feed_path(user))}, {:id, h.(OStatus.feed_path(user))},
{:title, ['#{user.nickname}\'s timeline']}, {:title, ['#{user.nickname}\'s timeline']},
{:updated, h.(most_recent_update)}, {:updated, h.(most_recent_update)},
{:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]}, {:logo, [to_charlist(User.avatar_url(user) |> MediaProxy.url())]},
{:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []}, {:link, [rel: 'hub', href: h.(OStatus.pubsub_path(user))], []},
{:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []}, {:link, [rel: 'salmon', href: h.(OStatus.salmon_path(user))], []},
{:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'], []}, {:link, [rel: 'self', href: h.(OStatus.feed_path(user)), type: 'application/atom+xml'],
{:author, UserRepresenter.to_simple_form(user)}, []},
{:author, UserRepresenter.to_simple_form(user)}
] ++ ] ++
if last_activity do if last_activity do
[{:link, [rel: 'next', [
href: to_charlist(OStatus.feed_path(user)) ++ '?max_id=' ++ to_charlist(last_activity.id), {:link,
type: 'application/atom+xml'], []}] [
rel: 'next',
href:
to_charlist(OStatus.feed_path(user)) ++
'?max_id=' ++ to_charlist(last_activity.id),
type: 'application/atom+xml'
], []}
]
else else
[] []
end end ++ entries
++ entries }
}] ]
end end
end end

View file

@ -6,7 +6,8 @@ defmodule Pleroma.Web.OStatus.FollowHandler do
def handle(entry, doc) do def handle(entry, doc) do
with {:ok, actor} <- OStatus.find_make_or_update_user(doc), with {:ok, actor} <- OStatus.find_make_or_update_user(doc),
id when not is_nil(id) <- XML.string_from_xpath("/entry/id", entry), 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, followed} <- OStatus.find_or_make_user(followed_uri),
{:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do {:ok, activity} <- ActivityPub.follow(actor, followed, id, false) do
User.follow(actor, followed) User.follow(actor, followed)

View file

@ -13,49 +13,56 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
3. A newly generated context id. 3. A newly generated context id.
""" """
def get_context(entry, inReplyTo) do def get_context(entry, inReplyTo) do
context = ( context =
XML.string_from_xpath("//ostatus:conversation[1]", entry) (XML.string_from_xpath("//ostatus:conversation[1]", entry) ||
|| XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) XML.string_from_xpath("//ostatus:conversation[1]/@ref", entry) || "")
|| "") |> String.trim |> String.trim()
with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(inReplyTo) do with %{data: %{"context" => context}} <- Object.get_cached_by_ap_id(inReplyTo) do
context context
else _e -> else
_e ->
if String.length(context) > 0 do if String.length(context) > 0 do
context context
else else
Utils.generate_context_id Utils.generate_context_id()
end end
end end
end end
def get_people_mentions(entry) do def get_people_mentions(entry) do
:xmerl_xpath.string('//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/person"]', entry) :xmerl_xpath.string(
|> Enum.map(fn(person) -> XML.string_from_xpath("@href", person) end) '//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 end
def get_collection_mentions(entry) do def get_collection_mentions(entry) do
transmogrify = fn transmogrify = fn
("http://activityschema.org/collection/public") -> "http://activityschema.org/collection/public" ->
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
(group) ->
group ->
group group
end end
:xmerl_xpath.string('//link[@rel="mentioned" and @ostatus:object-type="http://activitystrea.ms/schema/1.0/collection"]', entry) :xmerl_xpath.string(
|> Enum.map(fn(collection) -> XML.string_from_xpath("@href", collection) |> transmogrify.() end) '//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 end
def get_mentions(entry) do def get_mentions(entry) do
(get_people_mentions(entry) (get_people_mentions(entry) ++ get_collection_mentions(entry))
++ get_collection_mentions(entry)) |> Enum.filter(& &1)
|> Enum.filter(&(&1))
end end
def get_emoji(entry) do def get_emoji(entry) do
try do try do
:xmerl_xpath.string('//link[@rel="emoji"]', entry) :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)) Map.put(acc, XML.string_from_xpath("@name", emoji), XML.string_from_xpath("@href", emoji))
end) end)
rescue rescue
@ -79,7 +86,8 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
activity activity
else else
_e -> _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 {:ok, [activity | _]} <- OStatus.fetch_activity_from_url(inReplyToHref) do
activity activity
else else
@ -107,16 +115,40 @@ defmodule Pleroma.Web.OStatus.NoteHandler do
date <- XML.string_from_xpath("//published", entry), date <- XML.string_from_xpath("//published", entry),
unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted", unlisted <- XML.string_from_xpath("//mastodon:scope", entry) == "unlisted",
cc <- if(unlisted, do: ["https://www.w3.org/ns/activitystreams#Public"], else: []), cc <- if(unlisted, do: ["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("id", id) |> Map.put("tag", tags),
note <- note |> Map.put("published", date), note <- note |> Map.put("published", date),
note <- note |> Map.put("emoji", get_emoji(entry)), note <- note |> Map.put("emoji", get_emoji(entry)),
note <- add_external_url(note, entry), note <- add_external_url(note, entry),
note <- note |> Map.put("cc", cc), note <- note |> Map.put("cc", cc),
# TODO: Handle this case in make_note_data # TODO: Handle this case in make_note_data
note <- (if inReplyTo && !inReplyToActivity, do: note |> Map.put("inReplyTo", inReplyTo), else: note) note <-
do if(
res = ActivityPub.create(%{to: to, actor: actor, context: context, object: note, published: date, local: false, additional: %{"cc" => cc}}) 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) User.increase_note_count(actor)
res res
else else

View file

@ -16,7 +16,7 @@ defmodule Pleroma.Web.OStatus do
end end
def pubsub_path(user) do def pubsub_path(user) do
"#{Web.base_url}/push/hub/#{user.nickname}" "#{Web.base_url()}/push/hub/#{user.nickname}"
end end
def salmon_path(user) do def salmon_path(user) do
@ -24,15 +24,18 @@ defmodule Pleroma.Web.OStatus do
end end
def remote_follow_path do def remote_follow_path do
"#{Web.base_url}/ostatus_subscribe?acct={uri}" "#{Web.base_url()}/ostatus_subscribe?acct={uri}"
end end
def handle_incoming(xml_string) do def handle_incoming(xml_string) do
with doc when doc != :error <- parse_document(xml_string) do with doc when doc != :error <- parse_document(xml_string) do
entries = :xmerl_xpath.string('//entry', doc) entries = :xmerl_xpath.string('//entry', doc)
activities = Enum.map(entries, fn (entry) -> activities =
{:xmlObj, :string, object_type} = :xmerl_xpath.string('string(/entry/activity:object-type[1])', entry) 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) {:xmlObj, :string, verb} = :xmerl_xpath.string('string(/entry/activity:verb[1])', entry)
Logger.debug("Handling #{verb}") Logger.debug("Handling #{verb}")
@ -40,18 +43,26 @@ defmodule Pleroma.Web.OStatus do
case verb do case verb do
'http://activitystrea.ms/schema/1.0/delete' -> 'http://activitystrea.ms/schema/1.0/delete' ->
with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity with {:ok, activity} <- DeleteHandler.handle_delete(entry, doc), do: activity
'http://activitystrea.ms/schema/1.0/follow' -> 'http://activitystrea.ms/schema/1.0/follow' ->
with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity with {:ok, activity} <- FollowHandler.handle(entry, doc), do: activity
'http://activitystrea.ms/schema/1.0/share' -> 'http://activitystrea.ms/schema/1.0/share' ->
with {:ok, activity, retweeted_activity} <- handle_share(entry, doc), do: [activity, retweeted_activity] with {:ok, activity, retweeted_activity} <- handle_share(entry, doc),
do: [activity, retweeted_activity]
'http://activitystrea.ms/schema/1.0/favorite' -> 'http://activitystrea.ms/schema/1.0/favorite' ->
with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc), do: [activity, favorited_activity] with {:ok, activity, favorited_activity} <- handle_favorite(entry, doc),
do: [activity, favorited_activity]
_ -> _ ->
case object_type do case object_type do
'http://activitystrea.ms/schema/1.0/note' -> 'http://activitystrea.ms/schema/1.0/note' ->
with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity
'http://activitystrea.ms/schema/1.0/comment' -> 'http://activitystrea.ms/schema/1.0/comment' ->
with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity with {:ok, activity} <- NoteHandler.handle_note(entry, doc), do: activity
_ -> _ ->
Logger.error("Couldn't parse incoming document") Logger.error("Couldn't parse incoming document")
nil nil
@ -65,7 +76,7 @@ defmodule Pleroma.Web.OStatus do
nil nil
end end
end) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
{:ok, activities} {:ok, activities}
else else
@ -113,15 +124,20 @@ defmodule Pleroma.Web.OStatus do
def get_or_try_fetching(entry) do def get_or_try_fetching(entry) do
Logger.debug("Trying to get entry from db") Logger.debug("Trying to get entry from db")
with id when not is_nil(id) <- string_from_xpath("//activity:object[1]/id", entry), 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 %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
{:ok, activity} {:ok, activity}
else _ -> else
_ ->
Logger.debug("Couldn't get, will try to fetch") 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]} <- fetch_activity_from_url(href) do
{:ok, favorited_activity} {: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 end
end end
@ -137,20 +153,22 @@ defmodule Pleroma.Web.OStatus do
def get_attachments(entry) do def get_attachments(entry) do
:xmerl_xpath.string('/entry/link[@rel="enclosure"]', entry) :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), 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 when not is_nil(type) <- string_from_xpath("/link/@type", enclosure) do
%{ %{
"type" => "Attachment", "type" => "Attachment",
"url" => [%{ "url" => [
%{
"type" => "Link", "type" => "Link",
"mediaType" => type, "mediaType" => type,
"href" => href "href" => href
}] }
]
} }
end end
end) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
end end
@doc """ @doc """
@ -166,14 +184,15 @@ defmodule Pleroma.Web.OStatus do
def get_cw(entry) do def get_cw(entry) do
with cw when not is_nil(cw) <- string_from_xpath("/*/summary", entry) do with cw when not is_nil(cw) <- string_from_xpath("/*/summary", entry) do
cw cw
else _e -> nil else
_e -> nil
end end
end end
def get_tags(entry) do def get_tags(entry) do
:xmerl_xpath.string('//category', entry) :xmerl_xpath.string('//category', entry)
|> Enum.map(fn (category) -> string_from_xpath("/category/@term", category) end) |> Enum.map(fn category -> string_from_xpath("/category/@term", category) end)
|> Enum.filter(&(&1)) |> Enum.filter(& &1)
|> Enum.map(&String.downcase/1) |> Enum.map(&String.downcase/1)
end end
@ -184,6 +203,7 @@ defmodule Pleroma.Web.OStatus do
maybe_update_ostatus(doc, user) maybe_update_ostatus(doc, user)
end end
end end
def maybe_update_ostatus(doc, user) do def maybe_update_ostatus(doc, user) do
old_data = %{ old_data = %{
avatar: user.avatar, avatar: user.avatar,
@ -196,26 +216,33 @@ defmodule Pleroma.Web.OStatus do
avatar <- make_avatar_object(doc), avatar <- make_avatar_object(doc),
bio <- string_from_xpath("//author[1]/summary", doc), bio <- string_from_xpath("//author[1]/summary", doc),
name <- string_from_xpath("//author[1]/poco:displayName", doc), name <- string_from_xpath("//author[1]/poco:displayName", doc),
info <- Map.put(user.info, "banner", make_avatar_object(doc, "header") || user.info["banner"]), info <-
new_data <- %{avatar: avatar || old_data.avatar, name: name || old_data.name, bio: bio || old_data.bio, info: info || old_data.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 false <- new_data == old_data do
change = Ecto.Changeset.change(user, new_data) change = Ecto.Changeset.change(user, new_data)
Repo.update(change) Repo.update(change)
else _ -> else
_ ->
{:ok, user} {:ok, user}
end end
end end
def find_make_or_update_user(doc) do def find_make_or_update_user(doc) do
uri = string_from_xpath("//author/uri[1]", doc) uri = string_from_xpath("//author/uri[1]", doc)
with {:ok, user} <- find_or_make_user(uri) do with {:ok, user} <- find_or_make_user(uri) do
maybe_update(doc, user) maybe_update(doc, user)
end end
end end
def find_or_make_user(uri) do def find_or_make_user(uri) do
query = from user in User, query = from(user in User, where: user.ap_id == ^uri)
where: user.ap_id == ^uri
user = Repo.one(query) user = Repo.one(query)
@ -236,10 +263,12 @@ defmodule Pleroma.Web.OStatus do
avatar: info["avatar"], avatar: info["avatar"],
bio: info["bio"] bio: info["bio"]
} }
with false <- update, with false <- update,
%User{} = user <- User.get_by_ap_id(data.ap_id) do %User{} = user <- User.get_by_ap_id(data.ap_id) do
{:ok, user} {:ok, user}
else _e -> User.insert_or_update_user(data) else
_e -> User.insert_or_update_user(data)
end end
end end
end end
@ -252,12 +281,13 @@ defmodule Pleroma.Web.OStatus do
if href do if href do
%{ %{
"type" => "Image", "type" => "Image",
"url" => "url" => [
[%{ %{
"type" => "Link", "type" => "Link",
"mediaType" => type, "mediaType" => type,
"href" => href "href" => href
}] }
]
} }
else else
nil nil
@ -268,7 +298,8 @@ defmodule Pleroma.Web.OStatus do
with {:ok, webfinger_data} <- WebFinger.finger(username), with {:ok, webfinger_data} <- WebFinger.finger(username),
{:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do {:ok, feed_data} <- Websub.gather_feed_data(webfinger_data["topic"]) do
{:ok, Map.merge(webfinger_data, feed_data) |> Map.put("fqn", username)} {:ok, Map.merge(webfinger_data, feed_data) |> Map.put("fqn", username)}
else e -> else
e ->
Logger.debug(fn -> "Couldn't gather info for #{username}" end) Logger.debug(fn -> "Couldn't gather info for #{username}" end)
{:error, e} {:error, e}
end end
@ -284,12 +315,15 @@ defmodule Pleroma.Web.OStatus do
Regex.match?(@mastodon_regex, body) -> Regex.match?(@mastodon_regex, body) ->
[[_, match]] = Regex.scan(@mastodon_regex, body) [[_, match]] = Regex.scan(@mastodon_regex, body)
{:ok, match} {:ok, match}
Regex.match?(@gs_regex, body) -> Regex.match?(@gs_regex, body) ->
[[_, match]] = Regex.scan(@gs_regex, body) [[_, match]] = Regex.scan(@gs_regex, body)
{:ok, match} {:ok, match}
Regex.match?(@gs_classic_regex, body) -> Regex.match?(@gs_classic_regex, body) ->
[[_, match]] = Regex.scan(@gs_classic_regex, body) [[_, match]] = Regex.scan(@gs_classic_regex, body)
{:ok, match} {:ok, match}
true -> true ->
Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end) Logger.debug(fn -> "Couldn't find Atom link in #{inspect(body)}" end)
{:error, "Couldn't find the Atom link"} {:error, "Couldn't find the Atom link"}
@ -298,7 +332,14 @@ defmodule Pleroma.Web.OStatus do
def fetch_activity_from_atom_url(url) do def fetch_activity_from_atom_url(url) do
with true <- String.starts_with?(url, "http"), 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...") Logger.debug("Got document from #{url}, handling...")
handle_incoming(body) handle_incoming(body)
else else
@ -310,8 +351,10 @@ defmodule Pleroma.Web.OStatus do
def fetch_activity_from_html_url(url) do def fetch_activity_from_html_url(url) do
Logger.debug("Trying to fetch #{url}") Logger.debug("Trying to fetch #{url}")
with true <- String.starts_with?(url, "http"), 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 {:ok, atom_url} <- get_atom_url(body) do
fetch_activity_from_atom_url(atom_url) fetch_activity_from_atom_url(atom_url)
else else
@ -326,7 +369,8 @@ defmodule Pleroma.Web.OStatus do
with {:ok, activities} when length(activities) > 0 <- fetch_activity_from_atom_url(url) do with {:ok, activities} when length(activities) > 0 <- fetch_activity_from_atom_url(url) do
{:ok, activities} {:ok, activities}
else else
_e -> with {:ok, activities} <- fetch_activity_from_html_url(url) do _e ->
with {:ok, activities} <- fetch_activity_from_html_url(url) do
{:ok, activities} {:ok, activities}
end end
end end

View file

@ -16,20 +16,23 @@ defmodule Pleroma.Web.OStatus.OStatusController do
case get_format(conn) do case get_format(conn) do
"html" -> Fallback.RedirectController.redirector(conn, nil) "html" -> Fallback.RedirectController.redirector(conn, nil)
"activity+json" -> ActivityPubController.user(conn, params) "activity+json" -> ActivityPubController.user(conn, params)
_ -> redirect conn, external: OStatus.feed_path(user) _ -> redirect(conn, external: OStatus.feed_path(user))
end end
end end
def feed(conn, %{"nickname" => nickname} = params) do def feed(conn, %{"nickname" => nickname} = params) do
user = User.get_cached_by_nickname(nickname) user = User.get_cached_by_nickname(nickname)
query_params = Map.take(params, ["max_id"]) query_params =
Map.take(params, ["max_id"])
|> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id})
activities = ActivityPub.fetch_public_activities(query_params) activities =
|> Enum.reverse ActivityPub.fetch_public_activities(query_params)
|> Enum.reverse()
response = user response =
user
|> FeedRepresenter.to_simple_form(activities, [user]) |> FeedRepresenter.to_simple_form(activities, [user])
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string
@ -103,15 +106,18 @@ defmodule Pleroma.Web.OStatus.OStatusController do
conn conn
|> put_resp_content_type("text/html") |> put_resp_content_type("text/html")
|> send_file(200, "priv/static/index.html") |> send_file(200, "priv/static/index.html")
_ -> represent_activity(conn, activity, user)
_ ->
represent_activity(conn, activity, user)
end end
end end
end end
defp represent_activity(conn, activity, user) do defp represent_activity(conn, activity, user) do
response = activity response =
activity
|> ActivityRepresenter.to_simple_form(user, true) |> ActivityRepresenter.to_simple_form(user, true)
|> ActivityRepresenter.wrap_with_entry |> ActivityRepresenter.wrap_with_entry()
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string

View file

@ -1,18 +1,22 @@
defmodule Pleroma.Web.OStatus.UserRepresenter do defmodule Pleroma.Web.OStatus.UserRepresenter do
alias Pleroma.User alias Pleroma.User
def to_simple_form(user) do def to_simple_form(user) do
ap_id = to_charlist(user.ap_id) ap_id = to_charlist(user.ap_id)
nickname = to_charlist(user.nickname) nickname = to_charlist(user.nickname)
name = to_charlist(user.name) name = to_charlist(user.name)
bio = to_charlist(user.bio) bio = to_charlist(user.bio)
avatar_url = to_charlist(User.avatar_url(user)) avatar_url = to_charlist(User.avatar_url(user))
banner = if banner_url = User.banner_url(user) do
banner =
if banner_url = User.banner_url(user) do
[{:link, [rel: 'header', href: banner_url], []}] [{:link, [rel: 'header', href: banner_url], []}]
else else
[] []
end end
ap_enabled = if user.local do ap_enabled =
if user.local do
[{:ap_enabled, ['true']}] [{:ap_enabled, ['true']}]
else else
[] []

View file

@ -11,294 +11,305 @@ defmodule Pleroma.Web.Router do
end end
pipeline :api do pipeline :api do
plug :accepts, ["json"] plug(:accepts, ["json"])
plug :fetch_session plug(:fetch_session)
plug Pleroma.Plugs.OAuthPlug plug(Pleroma.Plugs.OAuthPlug)
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true})
end end
pipeline :authenticated_api do pipeline :authenticated_api do
plug :accepts, ["json"] plug(:accepts, ["json"])
plug :fetch_session plug(:fetch_session)
plug Pleroma.Plugs.OAuthPlug plug(Pleroma.Plugs.OAuthPlug)
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1} plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1})
end end
pipeline :mastodon_html do pipeline :mastodon_html do
plug :accepts, ["html"] plug(:accepts, ["html"])
plug :fetch_session plug(:fetch_session)
plug Pleroma.Plugs.OAuthPlug plug(Pleroma.Plugs.OAuthPlug)
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true})
end end
pipeline :pleroma_html do pipeline :pleroma_html do
plug :accepts, ["html"] plug(:accepts, ["html"])
plug :fetch_session plug(:fetch_session)
plug Pleroma.Plugs.OAuthPlug plug(Pleroma.Plugs.OAuthPlug)
plug Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true} plug(Pleroma.Plugs.AuthenticationPlug, %{fetcher: &Router.user_fetcher/1, optional: true})
end end
pipeline :well_known do pipeline :well_known do
plug :accepts, ["xml", "xrd+xml", "json", "jrd+json"] plug(:accepts, ["xml", "xrd+xml", "json", "jrd+json"])
end end
pipeline :config do pipeline :config do
plug :accepts, ["json", "xml"] plug(:accepts, ["json", "xml"])
end end
pipeline :oauth do pipeline :oauth do
plug :accepts, ["html", "json"] plug(:accepts, ["html", "json"])
end end
pipeline :pleroma_api do pipeline :pleroma_api do
plug :accepts, ["html", "json"] plug(:accepts, ["html", "json"])
end end
scope "/api/pleroma", Pleroma.Web.TwitterAPI do scope "/api/pleroma", Pleroma.Web.TwitterAPI do
pipe_through :pleroma_api pipe_through(:pleroma_api)
get "/password_reset/:token", UtilController, :show_password_reset get("/password_reset/:token", UtilController, :show_password_reset)
post "/password_reset", UtilController, :password_reset post("/password_reset", UtilController, :password_reset)
get "/emoji", UtilController, :emoji get("/emoji", UtilController, :emoji)
end end
scope "/", Pleroma.Web.TwitterAPI do scope "/", Pleroma.Web.TwitterAPI do
pipe_through :pleroma_html pipe_through(:pleroma_html)
get "/ostatus_subscribe", UtilController, :remote_follow get("/ostatus_subscribe", UtilController, :remote_follow)
post "/ostatus_subscribe", UtilController, :do_remote_follow post("/ostatus_subscribe", UtilController, :do_remote_follow)
post "/main/ostatus", UtilController, :remote_subscribe post("/main/ostatus", UtilController, :remote_subscribe)
end end
scope "/api/pleroma", Pleroma.Web.TwitterAPI do scope "/api/pleroma", Pleroma.Web.TwitterAPI do
pipe_through :authenticated_api pipe_through(:authenticated_api)
post "/follow_import", UtilController, :follow_import post("/follow_import", UtilController, :follow_import)
end end
scope "/oauth", Pleroma.Web.OAuth do scope "/oauth", Pleroma.Web.OAuth do
get "/authorize", OAuthController, :authorize get("/authorize", OAuthController, :authorize)
post "/authorize", OAuthController, :create_authorization post("/authorize", OAuthController, :create_authorization)
post "/token", OAuthController, :token_exchange post("/token", OAuthController, :token_exchange)
end end
scope "/api/v1", Pleroma.Web.MastodonAPI do scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through :authenticated_api pipe_through(:authenticated_api)
patch "/accounts/update_credentials", MastodonAPIController, :update_credentials patch("/accounts/update_credentials", MastodonAPIController, :update_credentials)
get "/accounts/verify_credentials", MastodonAPIController, :verify_credentials get("/accounts/verify_credentials", MastodonAPIController, :verify_credentials)
get "/accounts/relationships", MastodonAPIController, :relationships get("/accounts/relationships", MastodonAPIController, :relationships)
get "/accounts/search", MastodonAPIController, :account_search get("/accounts/search", MastodonAPIController, :account_search)
post "/accounts/:id/follow", MastodonAPIController, :follow post("/accounts/:id/follow", MastodonAPIController, :follow)
post "/accounts/:id/unfollow", MastodonAPIController, :unfollow post("/accounts/:id/unfollow", MastodonAPIController, :unfollow)
post "/accounts/:id/block", MastodonAPIController, :block post("/accounts/:id/block", MastodonAPIController, :block)
post "/accounts/:id/unblock", MastodonAPIController, :unblock post("/accounts/:id/unblock", MastodonAPIController, :unblock)
post "/accounts/:id/mute", MastodonAPIController, :relationship_noop post("/accounts/:id/mute", MastodonAPIController, :relationship_noop)
post "/accounts/:id/unmute", 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("/domain_blocks", MastodonAPIController, :empty_array)
get "/follow_requests", MastodonAPIController, :empty_array get("/follow_requests", MastodonAPIController, :empty_array)
get "/mutes", 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 post("/statuses", MastodonAPIController, :post_status)
delete "/statuses/:id", MastodonAPIController, :delete_status delete("/statuses/:id", MastodonAPIController, :delete_status)
post "/statuses/:id/reblog", MastodonAPIController, :reblog_status post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
post "/statuses/:id/favourite", MastodonAPIController, :fav_status post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
post "/statuses/:id/unfavourite", MastodonAPIController, :unfav_status post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
post "/notifications/clear", MastodonAPIController, :clear_notifications post("/notifications/clear", MastodonAPIController, :clear_notifications)
post "/notifications/dismiss", MastodonAPIController, :dismiss_notification post("/notifications/dismiss", MastodonAPIController, :dismiss_notification)
get "/notifications", MastodonAPIController, :notifications get("/notifications", MastodonAPIController, :notifications)
get "/notifications/:id", MastodonAPIController, :get_notification get("/notifications/:id", MastodonAPIController, :get_notification)
post "/media", MastodonAPIController, :upload post("/media", MastodonAPIController, :upload)
end end
scope "/api/v1", Pleroma.Web.MastodonAPI do scope "/api/v1", Pleroma.Web.MastodonAPI do
pipe_through :api pipe_through(:api)
get "/instance", MastodonAPIController, :masto_instance get("/instance", MastodonAPIController, :masto_instance)
get "/instance/peers", MastodonAPIController, :peers get("/instance/peers", MastodonAPIController, :peers)
post "/apps", MastodonAPIController, :create_app post("/apps", MastodonAPIController, :create_app)
get "/custom_emojis", MastodonAPIController, :custom_emojis get("/custom_emojis", MastodonAPIController, :custom_emojis)
get "/timelines/public", MastodonAPIController, :public_timeline get("/timelines/public", MastodonAPIController, :public_timeline)
get "/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline get("/timelines/tag/:tag", MastodonAPIController, :hashtag_timeline)
get "/statuses/:id", MastodonAPIController, :get_status get("/statuses/:id", MastodonAPIController, :get_status)
get "/statuses/:id/context", MastodonAPIController, :get_context get("/statuses/:id/context", MastodonAPIController, :get_context)
get "/statuses/:id/card", MastodonAPIController, :empty_object get("/statuses/:id/card", MastodonAPIController, :empty_object)
get "/statuses/:id/favourited_by", MastodonAPIController, :favourited_by get("/statuses/:id/favourited_by", MastodonAPIController, :favourited_by)
get "/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by get("/statuses/:id/reblogged_by", MastodonAPIController, :reblogged_by)
get "/accounts/:id/statuses", MastodonAPIController, :user_statuses get("/accounts/:id/statuses", MastodonAPIController, :user_statuses)
get "/accounts/:id/followers", MastodonAPIController, :followers get("/accounts/:id/followers", MastodonAPIController, :followers)
get "/accounts/:id/following", MastodonAPIController, :following get("/accounts/:id/following", MastodonAPIController, :following)
get "/accounts/:id", MastodonAPIController, :user get("/accounts/:id", MastodonAPIController, :user)
get "/search", MastodonAPIController, :search get("/search", MastodonAPIController, :search)
end end
scope "/api", Pleroma.Web do scope "/api", Pleroma.Web do
pipe_through :config pipe_through(:config)
get "/help/test", TwitterAPI.UtilController, :help_test get("/help/test", TwitterAPI.UtilController, :help_test)
post "/help/test", TwitterAPI.UtilController, :help_test post("/help/test", TwitterAPI.UtilController, :help_test)
get "/statusnet/config", TwitterAPI.UtilController, :config get("/statusnet/config", TwitterAPI.UtilController, :config)
get "/statusnet/version", TwitterAPI.UtilController, :version get("/statusnet/version", TwitterAPI.UtilController, :version)
end end
@instance Application.get_env(:pleroma, :instance) @instance Application.get_env(:pleroma, :instance)
@registrations_open Keyword.get(@instance, :registrations_open) @registrations_open Keyword.get(@instance, :registrations_open)
scope "/api", Pleroma.Web do scope "/api", Pleroma.Web do
pipe_through :api pipe_through(:api)
get "/statuses/public_timeline", TwitterAPI.Controller, :public_timeline 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/followers", TwitterAPI.Controller, :followers get(
get "/statuses/friends", TwitterAPI.Controller, :friends "/statuses/public_and_external_timeline",
get "/statuses/show/:id", TwitterAPI.Controller, :fetch_status TwitterAPI.Controller,
get "/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation :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 if @registrations_open do
post "/account/register", TwitterAPI.Controller, :register post("/account/register", TwitterAPI.Controller, :register)
end end
get "/search", TwitterAPI.Controller, :search get("/search", TwitterAPI.Controller, :search)
get "/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline)
end end
scope "/api", Pleroma.Web do scope "/api", Pleroma.Web do
pipe_through :authenticated_api pipe_through(:authenticated_api)
get "/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/verify_credentials", TwitterAPI.Controller, :verify_credentials)
post "/account/update_profile", TwitterAPI.Controller, :update_profile post("/account/update_profile", TwitterAPI.Controller, :update_profile)
post "/account/update_profile_banner", TwitterAPI.Controller, :update_banner post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner)
post "/qvitter/update_background_image", TwitterAPI.Controller, :update_background 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/home_timeline", TwitterAPI.Controller, :friends_timeline)
get "/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline)
get "/statuses/mentions", TwitterAPI.Controller, :mentions_timeline get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline)
get "/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline)
post "/statuses/update", TwitterAPI.Controller, :status_update post("/statuses/update", TwitterAPI.Controller, :status_update)
post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet)
post "/statuses/destroy/:id", TwitterAPI.Controller, :delete_post post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post)
post "/friendships/create", TwitterAPI.Controller, :follow post("/friendships/create", TwitterAPI.Controller, :follow)
post "/friendships/destroy", TwitterAPI.Controller, :unfollow post("/friendships/destroy", TwitterAPI.Controller, :unfollow)
post "/blocks/create", TwitterAPI.Controller, :block post("/blocks/create", TwitterAPI.Controller, :block)
post "/blocks/destroy", TwitterAPI.Controller, :unblock post("/blocks/destroy", TwitterAPI.Controller, :unblock)
post "/statusnet/media/upload", TwitterAPI.Controller, :upload post("/statusnet/media/upload", TwitterAPI.Controller, :upload)
post "/media/upload", TwitterAPI.Controller, :upload_json post("/media/upload", TwitterAPI.Controller, :upload_json)
post "/favorites/create/:id", TwitterAPI.Controller, :favorite post("/favorites/create/:id", TwitterAPI.Controller, :favorite)
post "/favorites/create", TwitterAPI.Controller, :favorite post("/favorites/create", TwitterAPI.Controller, :favorite)
post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite 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("/friends/ids", TwitterAPI.Controller, :friends_ids)
get "/friendships/no_retweets/ids", TwitterAPI.Controller, :empty_array 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 end
pipeline :ostatus do pipeline :ostatus do
plug :accepts, ["xml", "atom", "html", "activity+json"] plug(:accepts, ["xml", "atom", "html", "activity+json"])
end end
scope "/", Pleroma.Web do scope "/", Pleroma.Web do
pipe_through :ostatus pipe_through(:ostatus)
get "/objects/:uuid", OStatus.OStatusController, :object get("/objects/:uuid", OStatus.OStatusController, :object)
get "/activities/:uuid", OStatus.OStatusController, :activity get("/activities/:uuid", OStatus.OStatusController, :activity)
get "/notice/:id", OStatus.OStatusController, :notice get("/notice/:id", OStatus.OStatusController, :notice)
get "/users/:nickname/feed", OStatus.OStatusController, :feed get("/users/:nickname/feed", OStatus.OStatusController, :feed)
get "/users/:nickname", OStatus.OStatusController, :feed_redirect get("/users/:nickname", OStatus.OStatusController, :feed_redirect)
if @federating do if @federating do
post "/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming post("/users/:nickname/salmon", OStatus.OStatusController, :salmon_incoming)
post "/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request post("/push/hub/:nickname", Websub.WebsubController, :websub_subscription_request)
get "/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation get("/push/subscriptions/:id", Websub.WebsubController, :websub_subscription_confirmation)
post "/push/subscriptions/:id", Websub.WebsubController, :websub_incoming post("/push/subscriptions/:id", Websub.WebsubController, :websub_incoming)
end end
end end
pipeline :activitypub do pipeline :activitypub do
plug :accepts, ["activity+json"] plug(:accepts, ["activity+json"])
plug Pleroma.Web.Plugs.HTTPSignaturePlug plug(Pleroma.Web.Plugs.HTTPSignaturePlug)
end end
scope "/", Pleroma.Web.ActivityPub do scope "/", Pleroma.Web.ActivityPub do
# XXX: not really ostatus # XXX: not really ostatus
pipe_through :ostatus pipe_through(:ostatus)
get "/users/:nickname/followers", ActivityPubController, :followers get("/users/:nickname/followers", ActivityPubController, :followers)
get "/users/:nickname/following", ActivityPubController, :following get("/users/:nickname/following", ActivityPubController, :following)
get "/users/:nickname/outbox", ActivityPubController, :outbox get("/users/:nickname/outbox", ActivityPubController, :outbox)
end end
if @federating do if @federating do
scope "/", Pleroma.Web.ActivityPub do scope "/", Pleroma.Web.ActivityPub do
pipe_through :activitypub pipe_through(:activitypub)
post "/users/:nickname/inbox", ActivityPubController, :inbox post("/users/:nickname/inbox", ActivityPubController, :inbox)
post "/inbox", ActivityPubController, :inbox post("/inbox", ActivityPubController, :inbox)
end end
scope "/.well-known", Pleroma.Web do scope "/.well-known", Pleroma.Web do
pipe_through :well_known pipe_through(:well_known)
get "/host-meta", WebFinger.WebFingerController, :host_meta get("/host-meta", WebFinger.WebFingerController, :host_meta)
get "/webfinger", WebFinger.WebFingerController, :webfinger get("/webfinger", WebFinger.WebFingerController, :webfinger)
end end
end end
scope "/", Pleroma.Web.MastodonAPI do scope "/", Pleroma.Web.MastodonAPI do
pipe_through :mastodon_html pipe_through(:mastodon_html)
get "/web/login", MastodonAPIController, :login get("/web/login", MastodonAPIController, :login)
post "/web/login", MastodonAPIController, :login_post post("/web/login", MastodonAPIController, :login_post)
get "/web/*path", MastodonAPIController, :index get("/web/*path", MastodonAPIController, :index)
delete "/auth/sign_out", MastodonAPIController, :logout delete("/auth/sign_out", MastodonAPIController, :logout)
end end
pipeline :remote_media do pipeline :remote_media do
plug :accepts, ["html"] plug(:accepts, ["html"])
end end
scope "/proxy/", Pleroma.Web.MediaProxy do scope "/proxy/", Pleroma.Web.MediaProxy do
pipe_through :remote_media pipe_through(:remote_media)
get "/:sig/:url", MediaProxyController, :remote get("/:sig/:url", MediaProxyController, :remote)
end end
scope "/", Fallback do scope "/", Fallback do
get "/*path", RedirectController, :redirector get("/*path", RedirectController, :redirector)
end end
end end
defmodule Fallback.RedirectController do defmodule Fallback.RedirectController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
def redirector(conn, _params) do def redirector(conn, _params) do
if Mix.env != :test do if Mix.env() != :test do
conn conn
|> put_resp_content_type("text/html") |> put_resp_content_type("text/html")
|> send_file(200, "priv/static/index.html") |> send_file(200, "priv/static/index.html")

View file

@ -38,7 +38,8 @@ defmodule Pleroma.Web.Salmon do
def decode_and_validate(magickey, salmon) do def decode_and_validate(magickey, salmon) do
[data, type, encoding, alg, sig] = decode(salmon) [data, type, encoding, alg, sig] = decode(salmon)
signed_text = [data, type, encoding, alg] signed_text =
[data, type, encoding, alg]
|> Enum.map(&Base.url_encode64/1) |> Enum.map(&Base.url_encode64/1)
|> Enum.join(".") |> Enum.join(".")
@ -54,22 +55,23 @@ defmodule Pleroma.Web.Salmon do
end end
def decode_key("RSA." <> magickey) do def decode_key("RSA." <> magickey) do
make_integer = fn(bin) -> make_integer = fn bin ->
list = :erlang.binary_to_list(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 end
[modulus, exponent] = magickey [modulus, exponent] =
magickey
|> String.split(".") |> String.split(".")
|> Enum.map(fn (n) -> Base.url_decode64!(n, padding: false) end) |> Enum.map(fn n -> Base.url_decode64!(n, padding: false) end)
|> Enum.map(make_integer) |> Enum.map(make_integer)
{:RSAPublicKey, modulus, exponent} {:RSAPublicKey, modulus, exponent}
end end
def encode_key({:RSAPublicKey, modulus, exponent}) do def encode_key({:RSAPublicKey, modulus, exponent}) do
modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64 modulus_enc = :binary.encode_unsigned(modulus) |> Base.url_encode64()
exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64 exponent_enc = :binary.encode_unsigned(exponent) |> Base.url_encode64()
"RSA.#{modulus_enc}.#{exponent_enc}" "RSA.#{modulus_enc}.#{exponent_enc}"
end end
@ -78,20 +80,25 @@ defmodule Pleroma.Web.Salmon do
# We try at compile time to generate natively an RSA key otherwise we fallback on the old way. # We try at compile time to generate natively an RSA key otherwise we fallback on the old way.
try do try do
_ = :public_key.generate_key({:rsa, 2048, 65537}) _ = :public_key.generate_key({:rsa, 2048, 65537})
def generate_rsa_pem do def generate_rsa_pem do
key = :public_key.generate_key({:rsa, 2048, 65537}) key = :public_key.generate_key({:rsa, 2048, 65537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) 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} {:ok, pem}
end end
rescue rescue
_ -> _ ->
def generate_rsa_pem do def generate_rsa_pem do
port = Port.open({:spawn, "openssl genrsa"}, [:binary]) port = Port.open({:spawn, "openssl genrsa"}, [:binary])
{:ok, pem} = receive do
{:ok, pem} =
receive do
{^port, {:data, pem}} -> {:ok, pem} {^port, {:data, pem}} -> {:ok, pem}
end end
Port.close(port) Port.close(port)
if Regex.match?(~r/RSA PRIVATE KEY/, pem) do if Regex.match?(~r/RSA PRIVATE KEY/, pem) do
{:ok, pem} {:ok, pem}
else else
@ -113,17 +120,20 @@ defmodule Pleroma.Web.Salmon do
encoding = "base64url" encoding = "base64url"
alg = "RSA-SHA256" alg = "RSA-SHA256"
signed_text = [doc, type, encoding, alg] signed_text =
[doc, type, encoding, alg]
|> Enum.map(&Base.url_encode64/1) |> Enum.map(&Base.url_encode64/1)
|> Enum.join(".") |> Enum.join(".")
signature = signed_text signature =
signed_text
|> :public_key.sign(:sha256, private_key) |> :public_key.sign(:sha256, private_key)
|> to_string |> to_string
|> Base.url_encode64 |> Base.url_encode64()
doc_base64 = doc doc_base64 =
|> Base.url_encode64 doc
|> Base.url_encode64()
# Don't need proper xml building, these strings are safe to leave unescaped # Don't need proper xml building, these strings are safe to leave unescaped
salmon = """ salmon = """
@ -141,13 +151,22 @@ defmodule Pleroma.Web.Salmon do
def remote_users(%{data: %{"to" => to} = data}) do def remote_users(%{data: %{"to" => to} = data}) do
to = to ++ (data["cc"] || []) to = to ++ (data["cc"] || [])
to to
|> Enum.map(fn(id) -> User.get_cached_by_ap_id(id) end) |> Enum.map(fn id -> User.get_cached_by_ap_id(id) end)
|> Enum.filter(fn(user) -> user && !user.local end) |> Enum.filter(fn user -> user && !user.local end)
end end
defp send_to_user(%{info: %{"salmon" => salmon}}, feed, poster) do 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) Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
else else
e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end) e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end)
@ -165,9 +184,12 @@ defmodule Pleroma.Web.Salmon do
"Delete" "Delete"
] ]
def publish(user, activity, poster \\ &@httpoison.post/4) 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) def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
|> ActivityRepresenter.wrap_with_entry when type in @supported_activities do
feed =
ActivityRepresenter.to_simple_form(activity, user, true)
|> ActivityRepresenter.wrap_with_entry()
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string
@ -176,7 +198,7 @@ defmodule Pleroma.Web.Salmon do
{:ok, feed} = encode(private, feed) {:ok, feed} = encode(private, feed)
remote_users(activity) remote_users(activity)
|> Enum.each(fn(remote_user) -> |> Enum.each(fn remote_user ->
Task.start(fn -> Task.start(fn ->
Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end) Logger.debug(fn -> "Sending Salmon to #{remote_user.ap_id}" end)
send_to_user(remote_user, feed, poster) send_to_user(remote_user, feed, poster)

View file

@ -5,9 +5,11 @@ defmodule Pleroma.Web.Streamer do
def start_link do def start_link do
spawn(fn -> spawn(fn ->
Process.sleep(1000 * 30) # 30 seconds # 30 seconds
Process.sleep(1000 * 30)
GenServer.cast(__MODULE__, %{action: :ping}) GenServer.cast(__MODULE__, %{action: :ping})
end) end)
GenServer.start_link(__MODULE__, %{}, name: __MODULE__) GenServer.start_link(__MODULE__, %{}, name: __MODULE__)
end end
@ -25,39 +27,54 @@ defmodule Pleroma.Web.Streamer do
def handle_cast(%{action: :ping}, topics) do def handle_cast(%{action: :ping}, topics) do
Map.values(topics) Map.values(topics)
|> List.flatten |> List.flatten()
|> Enum.each(fn (socket) -> |> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping") Logger.debug("Sending keepalive ping")
send socket.transport_pid, {:text, ""} send(socket.transport_pid, {:text, ""})
end) end)
spawn(fn -> spawn(fn ->
Process.sleep(1000 * 30) # 30 seconds # 30 seconds
Process.sleep(1000 * 30)
GenServer.cast(__MODULE__, %{action: :ping}) GenServer.cast(__MODULE__, %{action: :ping})
end) end)
{:noreply, topics} {:noreply, topics}
end end
def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do def handle_cast(%{action: :stream, topic: "user", item: %Notification{} = item}, topics) do
topic = "user:#{item.user_id}" 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) end)
{:noreply, topics} {:noreply, topics}
end end
def handle_cast(%{action: :stream, topic: "user", item: item}, topics) do def handle_cast(%{action: :stream, topic: "user", item: item}, topics) do
Logger.debug("Trying to push to users") 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) push_to_socket(topics, topic, item)
end) end)
{:noreply, topics} {:noreply, topics}
end end
@ -92,13 +109,21 @@ defmodule Pleroma.Web.Streamer do
end end
def push_to_socket(topics, topic, item) do def push_to_socket(topics, topic, item) do
Enum.each(topics[topic] || [], fn (socket) -> Enum.each(topics[topic] || [], fn socket ->
json = %{ json =
%{
event: "update", event: "update",
payload: Pleroma.Web.MastodonAPI.StatusView.render("status.json", activity: item, for: socket.assigns[:user]) |> Jason.encode! payload:
} |> Jason.encode! 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)
end end

View file

@ -12,20 +12,20 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def show_password_reset(conn, %{"token" => token}) do def show_password_reset(conn, %{"token" => token}) do
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}), with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
%User{} = user <- Repo.get(User, token.user_id) do %User{} = user <- Repo.get(User, token.user_id) do
render conn, "password_reset.html", %{ render(conn, "password_reset.html", %{
token: token, token: token,
user: user user: user
} })
else else
_e -> render conn, "invalid_token.html" _e -> render(conn, "invalid_token.html")
end end
end end
def password_reset(conn, %{"data" => data}) do def password_reset(conn, %{"data" => data}) do
with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do with {:ok, _} <- PasswordResetToken.reset_password(data["token"], data) do
render conn, "password_reset_success.html" render(conn, "password_reset_success.html")
else else
_e -> render conn, "password_reset_failed.html" _e -> render(conn, "password_reset_failed.html")
end end
end end
@ -34,14 +34,19 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
end end
def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do def remote_subscribe(conn, %{"nickname" => nick, "profile" => _}) do
with %User{} = user <- User.get_cached_by_nickname(nick), with %User{} = user <- User.get_cached_by_nickname(nick), avatar = User.avatar_url(user) do
avatar = User.avatar_url(user) do
conn conn
|> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false}) |> render("subscribe.html", %{nickname: nick, avatar: avatar, error: false})
else 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
end end
def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do def remote_subscribe(conn, %{"user" => %{"nickname" => nick, "profile" => profile}}) do
with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile), with {:ok, %{"subscribe_address" => template}} <- WebFinger.finger(profile),
%User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do %User{ap_id: ap_id} <- User.get_cached_by_nickname(nick) do
@ -49,7 +54,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id)) |> Phoenix.Controller.redirect(external: String.replace(template, "{uri}", ap_id))
else else
_e -> _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
end end
@ -64,14 +73,23 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id}) |> render("follow.html", %{error: err, acct: acct, avatar: avatar, name: name, id: id})
else else
conn 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
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) followee = Repo.get(User, id)
avatar = User.avatar_url(followee) avatar = User.avatar_url(followee)
name = followee.nickname name = followee.nickname
with %User{} = user <- User.get_cached_by_nickname(username), with %User{} = user <- User.get_cached_by_nickname(username),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
%User{} = followed <- Repo.get(User, id), %User{} = followed <- Repo.get(User, id),
@ -82,9 +100,15 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
else else
_e -> _e ->
conn 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
end end
def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do def do_remote_follow(%{assigns: %{user: user}} = conn, %{"user" => %{"id" => id}}) do
with %User{} = followee <- Repo.get(User, id), with %User{} = followee <- Repo.get(User, id),
{:ok, follower} <- User.follow(user, followee), {:ok, follower} <- User.follow(user, followee),
@ -93,7 +117,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|> render("followed.html", %{error: false}) |> render("followed.html", %{error: false})
else else
e -> e ->
Logger.debug("Remote follow failed with error #{inspect e}") Logger.debug("Remote follow failed with error #{inspect(e)}")
conn conn
|> render("followed.html", %{error: inspect(e)}) |> render("followed.html", %{error: inspect(e)})
end end
@ -107,20 +132,22 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
<config> <config>
<site> <site>
<name>#{Keyword.get(@instance, :name)}</name> <name>#{Keyword.get(@instance, :name)}</name>
<site>#{Web.base_url}</site> <site>#{Web.base_url()}</site>
<textlimit>#{Keyword.get(@instance, :limit)}</textlimit> <textlimit>#{Keyword.get(@instance, :limit)}</textlimit>
<closed>#{!Keyword.get(@instance, :registrations_open)}</closed> <closed>#{!Keyword.get(@instance, :registrations_open)}</closed>
</site> </site>
</config> </config>
""" """
conn conn
|> put_resp_content_type("application/xml") |> put_resp_content_type("application/xml")
|> send_resp(200, response) |> send_resp(200, response)
_ -> _ ->
json(conn, %{ json(conn, %{
site: %{ site: %{
name: Keyword.get(@instance, :name), name: Keyword.get(@instance, :name),
server: Web.base_url, server: Web.base_url(),
textlimit: to_string(Keyword.get(@instance, :limit)), textlimit: to_string(Keyword.get(@instance, :limit)),
closed: if(Keyword.get(@instance, :registrations_open), do: "0", else: "1") closed: if(Keyword.get(@instance, :registrations_open), do: "0", else: "1")
} }
@ -130,23 +157,28 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
def version(conn, _params) do def version(conn, _params) do
version = Keyword.get(@instance, :version) version = Keyword.get(@instance, :version)
case get_format(conn) do case get_format(conn) do
"xml" -> "xml" ->
response = "<version>#{version}</version>" response = "<version>#{version}</version>"
conn conn
|> put_resp_content_type("application/xml") |> put_resp_content_type("application/xml")
|> send_resp(200, response) |> send_resp(200, response)
_ -> json(conn, version)
_ ->
json(conn, version)
end end
end end
def emoji(conn, _params) do def emoji(conn, _params) do
json conn, Enum.into(Formatter.get_custom_emoji(), %{}) json(conn, Enum.into(Formatter.get_custom_emoji(), %{}))
end end
def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do def follow_import(conn, %{"list" => %Plug.Upload{} = listfile}) do
follow_import(conn, %{"list" => File.read!(listfile.path)}) follow_import(conn, %{"list" => File.read!(listfile.path)})
end end
def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do
Task.start(fn -> Task.start(fn ->
String.split(list) String.split(list)
@ -156,11 +188,11 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
{:ok, follower} <- User.follow(follower, followed) do {:ok, follower} <- User.follow(follower, followed) do
ActivityPub.follow(follower, followed) ActivityPub.follow(follower, followed)
else else
_e -> Logger.debug "follow_import: following #{nick} failed" _e -> Logger.debug("follow_import: following #{nick} failed")
end end
end) end)
end) end)
json conn, "job started" json(conn, "job started")
end end
end end

View file

@ -7,18 +7,22 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
alias Pleroma.Formatter alias Pleroma.Formatter
defp user_by_ap_id(user_list, ap_id) do 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 end
def to_map(%Activity{data: %{"type" => "Announce", "actor" => actor, "published" => created_at}} = activity, def to_map(
%{users: users, announced_activity: announced_activity} = opts) do %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) 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." text = "#{user.nickname} retweeted a status."
announced_user = user_by_ap_id(users, announced_activity.data["actor"]) announced_user = user_by_ap_id(users, announced_activity.data["actor"])
retweeted_status = to_map(announced_activity, Map.merge(%{user: announced_user}, opts)) retweeted_status = to_map(announced_activity, Map.merge(%{user: announced_user}, opts))
%{ %{
"id" => activity.id, "id" => activity.id,
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
@ -35,9 +39,11 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
} }
end end
def to_map(%Activity{data: %{"type" => "Like", "published" => created_at}} = activity, def to_map(
%{user: user, liked_activity: liked_activity} = opts) do %Activity{data: %{"type" => "Like", "published" => created_at}} = activity,
created_at = created_at |> Utils.date_to_asctime %{user: user, liked_activity: liked_activity} = opts
) do
created_at = created_at |> Utils.date_to_asctime()
text = "#{user.nickname} favorited a status." text = "#{user.nickname} favorited a status."
@ -56,12 +62,16 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
} }
end end
def to_map(%Activity{data: %{"type" => "Follow", "object" => followed_id}} = activity, %{user: user} = opts) do def to_map(
created_at = activity.data["published"] || (DateTime.to_iso8601(activity.inserted_at)) %Activity{data: %{"type" => "Follow", "object" => followed_id}} = activity,
created_at = created_at |> Utils.date_to_asctime %{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) followed = User.get_cached_by_ap_id(followed_id)
text = "#{user.nickname} started following #{followed.nickname}" text = "#{user.nickname} started following #{followed.nickname}"
%{ %{
"id" => activity.id, "id" => activity.id,
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
@ -79,10 +89,16 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
# TODO: # TODO:
# Make this more proper. Just a placeholder to not break the frontend. # 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 def to_map(
created_at = created_at |> Utils.date_to_asctime %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}" text = "#{user.nickname} undid the action at #{undid_activity}"
%{ %{
"id" => activity.id, "id" => activity.id,
"user" => UserView.render("show.json", %{user: user, for: opts[:for]}), "user" => UserView.render("show.json", %{user: user, for: opts[:for]}),
@ -98,8 +114,12 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
} }
end end
def to_map(%Activity{data: %{"type" => "Delete", "published" => created_at, "object" => _ }} = activity, %{user: user} = opts) do def to_map(
created_at = created_at |> Utils.date_to_asctime %Activity{data: %{"type" => "Delete", "published" => created_at, "object" => _}} =
activity,
%{user: user} = opts
) do
created_at = created_at |> Utils.date_to_asctime()
%{ %{
"id" => activity.id, "id" => activity.id,
@ -117,8 +137,11 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
} }
end end
def to_map(%Activity{data: %{"object" => %{"content" => content} = object}} = activity, %{user: user} = opts) do def to_map(
created_at = object["published"] |> Utils.date_to_asctime %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 like_count = object["like_count"] || 0
announcement_count = object["announcement_count"] || 0 announcement_count = object["announcement_count"] || 0
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
@ -126,10 +149,11 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
mentions = opts[:mentioned] || [] mentions = opts[:mentioned] || []
attentions = activity.recipients attentions =
|> Enum.map(fn (ap_id) -> Enum.find(mentions, fn(user) -> ap_id == user.ap_id end) end) activity.recipients
|> Enum.filter(&(&1)) |> Enum.map(fn ap_id -> Enum.find(mentions, fn user -> ap_id == user.ap_id end) end)
|> Enum.map(fn (user) -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) |> Enum.filter(& &1)
|> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end)
conversation_id = conversation_id(activity) conversation_id = conversation_id(activity)
@ -139,13 +163,16 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags tags = if possibly_sensitive, do: Enum.uniq(["nsfw" | tags]), else: tags
summary = activity.data["object"]["summary"] summary = activity.data["object"]["summary"]
content = if !!summary and summary != "" do
content =
if !!summary and summary != "" do
"<span>#{activity.data["object"]["summary"]}</span><br />#{content}</span>" "<span>#{activity.data["object"]["summary"]}</span><br />#{content}</span>"
else else
content content
end end
html = HtmlSanitizeEx.basic_html(content) html =
HtmlSanitizeEx.basic_html(content)
|> Formatter.emojify(object["emoji"]) |> Formatter.emojify(object["emoji"])
%{ %{
@ -175,7 +202,8 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
def conversation_id(activity) do def conversation_id(activity) do
with context when not is_nil(context) <- activity.data["context"] do with context when not is_nil(context) <- activity.data["context"] do
TwitterAPI.context_to_conversation_id(context) TwitterAPI.context_to_conversation_id(context)
else _e -> nil else
_e -> nil
end end
end end

View file

@ -1,15 +1,18 @@
defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do
defmacro __using__(_opts) do defmacro __using__(_opts) do
quote 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 def to_json(object, options) do
object object
|> to_map(options) |> to_map(options)
|> Jason.encode! |> Jason.encode!()
end end
def enum_to_list(enum, options) do 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) Enum.map(enum, mapping)
end end
@ -17,11 +20,14 @@ defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do
to_map(object, %{}) to_map(object, %{})
end 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 def enum_to_json(enum, options) do
enum enum
|> enum_to_list(options) |> enum_to_list(options)
|> Jason.encode! |> Jason.encode!()
end end
end end
end end

View file

@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do
def to_map(%Object{data: %{"url" => [url | _]}} = object, _opts) do def to_map(%Object{data: %{"url" => [url | _]}} = object, _opts) do
data = object.data data = object.data
%{ %{
url: url["href"] |> Pleroma.Web.MediaProxy.url(), url: url["href"] |> Pleroma.Web.MediaProxy.url(),
mimetype: url["mediaType"], mimetype: url["mediaType"],

View file

@ -13,7 +13,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end end
def fetch_friend_statuses(user, opts \\ %{}) do def fetch_friend_statuses(user, opts \\ %{}) do
opts = opts opts =
opts
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("user", user) |> Map.put("user", user)
|> Map.put("type", ["Create", "Announce", "Follow", "Like"]) |> Map.put("type", ["Create", "Announce", "Follow", "Like"])
@ -23,7 +24,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end end
def fetch_public_statuses(user, opts \\ %{}) do def fetch_public_statuses(user, opts \\ %{}) do
opts = opts opts =
opts
|> Map.put("local_only", true) |> Map.put("local_only", true)
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("type", ["Create", "Announce", "Follow"]) |> Map.put("type", ["Create", "Announce", "Follow"])
@ -33,7 +35,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end end
def fetch_public_and_external_statuses(user, opts \\ %{}) do def fetch_public_and_external_statuses(user, opts \\ %{}) do
opts = opts opts =
opts
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
|> Map.put("type", ["Create", "Announce", "Follow"]) |> Map.put("type", ["Create", "Announce", "Follow"])
@ -42,8 +45,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end end
def fetch_user_statuses(user, opts \\ %{}) do def fetch_user_statuses(user, opts \\ %{}) do
opts = opts opts =
opts
|> Map.put("type", ["Create"]) |> Map.put("type", ["Create"])
ActivityPub.fetch_public_activities(opts) ActivityPub.fetch_public_activities(opts)
|> activities_to_statuses(%{for: user}) |> activities_to_statuses(%{for: user})
end end
@ -55,11 +60,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def fetch_conversation(user, id) do def fetch_conversation(user, id) do
with context when is_binary(context) <- conversation_id_to_context(id), with context when is_binary(context) <- conversation_id_to_context(id),
activities <- ActivityPub.fetch_activities_for_context(context, %{"blocking_user" => user, "user" => user}), activities <-
statuses <- activities |> activities_to_statuses(%{for: user}) ActivityPub.fetch_activities_for_context(context, %{
do "blocking_user" => user,
"user" => user
}),
statuses <- activities |> activities_to_statuses(%{for: user}) do
statuses statuses
else _e -> else
_e ->
[] []
end end
end end
@ -74,8 +83,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def follow(%User{} = follower, params) do def follow(%User{} = follower, params) do
with {:ok, %User{} = followed} <- get_user(params), with {:ok, %User{} = followed} <- get_user(params),
{:ok, follower} <- User.follow(follower, followed), {:ok, follower} <- User.follow(follower, followed),
{:ok, activity} <- ActivityPub.follow(follower, followed) {:ok, activity} <- ActivityPub.follow(follower, followed) do
do
{:ok, follower, followed, activity} {:ok, follower, followed, activity}
else else
err -> err err -> err
@ -85,13 +93,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def unfollow(%User{} = follower, params) do def unfollow(%User{} = follower, params) do
with {:ok, %User{} = unfollowed} <- get_user(params), with {:ok, %User{} = unfollowed} <- get_user(params),
{:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed), {:ok, follower, follow_activity} <- User.unfollow(follower, unfollowed),
{ :ok, _activity } <- ActivityPub.insert(%{ {:ok, _activity} <-
ActivityPub.insert(%{
"type" => "Undo", "type" => "Undo",
"actor" => follower.ap_id, "actor" => follower.ap_id,
"object" => follow_activity.data["id"], # get latest Follow for these users # get latest Follow for these users
"object" => follow_activity.data["id"],
"published" => make_date() "published" => make_date()
}) }) do
do
{:ok, follower, unfollowed} {:ok, follower, unfollowed}
else else
err -> err err -> err
@ -100,8 +109,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def block(%User{} = blocker, params) do def block(%User{} = blocker, params) do
with {:ok, %User{} = blocked} <- get_user(params), with {:ok, %User{} = blocked} <- get_user(params),
{:ok, blocker} <- User.block(blocker, blocked) {:ok, blocker} <- User.block(blocker, blocked) do
do
{:ok, blocker, blocked} {:ok, blocker, blocked}
else else
err -> err err -> err
@ -110,8 +118,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def unblock(%User{} = blocker, params) do def unblock(%User{} = blocker, params) do
with {:ok, %User{} = blocked} <- get_user(params), with {:ok, %User{} = blocked} <- get_user(params),
{:ok, blocker} <- User.unblock(blocker, blocked) {:ok, blocker} <- User.unblock(blocker, blocked) do
do
{:ok, blocker, blocked} {:ok, blocker, blocked}
else else
err -> err err -> err
@ -163,13 +170,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
<atom:link rel="enclosure" href="#{href}" type="#{type}"></atom:link> <atom:link rel="enclosure" href="#{href}" type="#{type}"></atom:link>
</rsp> </rsp>
""" """
"json" -> "json" ->
%{ %{
media_id: object.id, media_id: object.id,
media_id_string: "#{object.id}}", media_id_string: "#{object.id}}",
media_url: href, media_url: href,
size: 0 size: 0
} |> Jason.encode! }
|> Jason.encode!()
end end
end end
@ -189,8 +198,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
{:ok, user} {:ok, user}
else else
{:error, changeset} -> {:error, changeset} ->
errors = Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end) errors =
|> Jason.encode! Ecto.Changeset.traverse_errors(changeset, fn {msg, _opts} -> msg end)
|> Jason.encode!()
{:error, %{error: errors}} {:error, %{error: errors}}
end end
end end
@ -209,16 +220,20 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
case target = get_by_id_or_nickname(user_id) do case target = get_by_id_or_nickname(user_id) do
nil -> nil ->
{:error, "No user with such user_id"} {:error, "No user with such user_id"}
_ -> _ ->
{:ok, target} {:ok, target}
end end
%{"screen_name" => nickname} -> %{"screen_name" => nickname} ->
case target = Repo.get_by(User, nickname: nickname) do case target = Repo.get_by(User, nickname: nickname) do
nil -> nil ->
{:error, "No user with such screen_name"} {:error, "No user with such screen_name"}
_ -> _ ->
{:ok, target} {:ok, target}
end end
_ -> _ ->
if user do if user do
{:ok, user} {:ok, user}
@ -229,6 +244,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
end end
defp parse_int(string, default) defp parse_int(string, default)
defp parse_int(string, default) when is_binary(string) do defp parse_int(string, default) when is_binary(string) do
with {n, _} <- Integer.parse(string) do with {n, _} <- Integer.parse(string) do
n n
@ -236,6 +252,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
_e -> default _e -> default
end end
end end
defp parse_int(_, default), do: default defp parse_int(_, default), do: default
def search(user, %{"q" => query} = params) do def search(user, %{"q" => query} = params) do
@ -243,19 +260,28 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
page = parse_int(params["page"], 1) page = parse_int(params["page"], 1)
offset = (page - 1) * limit offset = (page - 1) * limit
q = from a in Activity, q =
from(
a in Activity,
where: fragment("?->>'type' = 'Create'", a.data), where: fragment("?->>'type' = 'Create'", a.data),
where: fragment("to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)", a.data, ^query), where:
fragment(
"to_tsvector('english', ?->'object'->>'content') @@ plainto_tsquery('english', ?)",
a.data,
^query
),
limit: ^limit, limit: ^limit,
offset: ^offset, offset: ^offset,
order_by: [desc: :inserted_at] # this one isn't indexed so psql won't take the wrong index. # this one isn't indexed so psql won't take the wrong index.
order_by: [desc: :inserted_at]
)
activities = Repo.all(q) activities = Repo.all(q)
activities_to_statuses(activities, %{for: user}) activities_to_statuses(activities, %{for: user})
end end
defp activities_to_statuses(activities, opts) do defp activities_to_statuses(activities, opts) do
Enum.map(activities, fn(activity) -> Enum.map(activities, fn activity ->
activity_to_status(activity, opts) activity_to_status(activity, opts)
end) end)
end end
@ -266,7 +292,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
user = User.get_cached_by_ap_id(actor) user = User.get_cached_by_ap_id(actor)
[liked_activity] = Activity.all_by_object_ap_id(activity.data["object"]) [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 end
# For announces, fetch the announced activity and the user. # For announces, fetch the announced activity and the user.
@ -276,7 +305,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
[announced_activity] = Activity.all_by_object_ap_id(activity.data["object"]) [announced_activity] = Activity.all_by_object_ap_id(activity.data["object"])
announced_actor = User.get_cached_by_ap_id(announced_activity.data["actor"]) 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 end
defp activity_to_status(%Activity{data: %{"type" => "Delete"}} = activity, opts) do defp activity_to_status(%Activity{data: %{"type" => "Delete"}} = activity, opts) do
@ -289,32 +321,41 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
actor = get_in(activity.data, ["actor"]) actor = get_in(activity.data, ["actor"])
user = User.get_cached_by_ap_id(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 = Repo.all(from user in User, where: user.ap_id in ^activity.data["to"])
mentioned_users = Enum.map(activity.recipients || [], fn (ap_id) -> mentioned_users =
Enum.map(activity.recipients || [], fn ap_id ->
if ap_id do if ap_id do
User.get_cached_by_ap_id(ap_id) User.get_cached_by_ap_id(ap_id)
else else
nil nil
end end
end) end)
|> Enum.filter(&(&1)) |> 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 end
defp make_date do defp make_date do
DateTime.utc_now() |> DateTime.to_iso8601 DateTime.utc_now() |> DateTime.to_iso8601()
end end
def context_to_conversation_id(context) do def context_to_conversation_id(context) do
with %Object{id: id} <- Object.get_cached_by_ap_id(context) do with %Object{id: id} <- Object.get_cached_by_ap_id(context) do
id id
else _e -> else
_e ->
changeset = Object.context_mapping(context) changeset = Object.context_mapping(context)
case Repo.insert(changeset) do case Repo.insert(changeset) do
{:ok, %{id: id}} -> id {:ok, %{id: id}} ->
id
# This should be solved by an upsert, but it seems ecto # This should be solved by an upsert, but it seems ecto
# has problems accessing the constraint inside the jsonb. # 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 end
end end
@ -322,7 +363,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
def conversation_id_to_context(id) do def conversation_id_to_context(id) do
with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do with %Object{data: %{"id" => context}} <- Repo.get(Object, id) do
context context
else _e -> else
_e ->
{:error, "No such conversation"} {:error, "No such conversation"}
end end
end end
@ -331,12 +373,15 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
with %User{} = user <- User.get_or_fetch(uri) do with %User{} = user <- User.get_or_fetch(uri) do
spawn(fn -> spawn(fn ->
with url <- user.info["topic"], 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) OStatus.handle_incoming(body)
end end
end) end)
{:ok, UserView.render("show.json", %{user: user, for: for_user})} {:ok, UserView.render("show.json", %{user: user, for: for_user})}
else _e -> else
_e ->
{:error, "Couldn't find user"} {:error, "Couldn't find user"}
end end
end end

View file

@ -16,7 +16,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do def status_update(%{assigns: %{user: user}} = conn, %{"status" => _} = status_data) do
with media_ids <- extract_media_ids(status_data), 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 conn
|> json(ActivityRepresenter.to_map(activity, %{user: user})) |> json(ActivityRepresenter.to_map(activity, %{user: user}))
else else
@ -35,10 +36,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
defp extract_media_ids(status_data) do defp extract_media_ids(status_data) do
with media_ids when not is_nil(media_ids) <- status_data["media_ids"], with media_ids when not is_nil(media_ids) <- status_data["media_ids"],
split_ids <- String.split(media_ids, ","), split_ids <- String.split(media_ids, ","),
clean_ids <- Enum.reject(split_ids, fn (id) -> String.length(id) == 0 end) clean_ids <- Enum.reject(split_ids, fn id -> String.length(id) == 0 end) do
do
clean_ids clean_ids
else _e -> [] else
_e -> []
end end
end end
@ -69,9 +70,9 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def show_user(conn, params) do def show_user(conn, params) do
with {:ok, shown} <- TwitterAPI.get_user(params) do with {:ok, shown} <- TwitterAPI.get_user(params) do
if user = conn.assigns.user 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 else
render conn, UserView, "show.json", %{user: shown} render(conn, UserView, "show.json", %{user: shown})
end end
else else
{:error, msg} -> {:error, msg} ->
@ -84,8 +85,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
{:ok, target_user} -> {:ok, target_user} ->
params = Map.merge(params, %{"actor_id" => target_user.ap_id, "whole_db" => true}) 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 conn
|> json_reply(200, statuses |> Jason.encode!) |> json_reply(200, statuses |> Jason.encode!())
{:error, msg} -> {:error, msg} ->
bad_request_reply(conn, msg) bad_request_reply(conn, msg)
end end
@ -103,29 +106,36 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
case TwitterAPI.follow(user, params) do case TwitterAPI.follow(user, params) do
{:ok, user, followed, _activity} -> {:ok, user, followed, _activity} ->
render(conn, UserView, "show.json", %{user: followed, for: user}) 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
end end
def block(%{assigns: %{user: user}} = conn, params) do def block(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.block(user, params) do case TwitterAPI.block(user, params) do
{:ok, user, blocked} -> {:ok, user, blocked} ->
render conn, UserView, "show.json", %{user: blocked, for: user} render(conn, UserView, "show.json", %{user: blocked, for: user})
{:error, msg} -> forbidden_json_reply(conn, msg)
{:error, msg} ->
forbidden_json_reply(conn, msg)
end end
end end
def unblock(%{assigns: %{user: user}} = conn, params) do def unblock(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.unblock(user, params) do case TwitterAPI.unblock(user, params) do
{:ok, user, blocked} -> {:ok, user, blocked} ->
render conn, UserView, "show.json", %{user: blocked, for: user} render(conn, UserView, "show.json", %{user: blocked, for: user})
{:error, msg} -> forbidden_json_reply(conn, msg)
{:error, msg} ->
forbidden_json_reply(conn, msg)
end end
end end
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with {:ok, delete} <- CommonAPI.delete(id, user) do with {:ok, delete} <- CommonAPI.delete(id, user) do
json = ActivityRepresenter.to_json(delete, %{user: user, for: user}) json = ActivityRepresenter.to_json(delete, %{user: user, for: user})
conn conn
|> json_reply(200, json) |> json_reply(200, json)
end end
@ -135,14 +145,16 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
case TwitterAPI.unfollow(user, params) do case TwitterAPI.unfollow(user, params) do
{:ok, user, unfollowed} -> {:ok, user, unfollowed} ->
render(conn, UserView, "show.json", %{user: unfollowed, for: user}) 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
end end
def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do def fetch_status(%{assigns: %{user: user}} = conn, %{"id" => id}) do
with %Activity{} = activity <- Repo.get(Activity, id), with %Activity{} = activity <- Repo.get(Activity, id),
true <- ActivityPub.visible_for_user?(activity, user) do 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
end end
@ -156,6 +168,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def upload(conn, %{"media" => media}) do def upload(conn, %{"media" => media}) do
response = TwitterAPI.upload(media) response = TwitterAPI.upload(media)
conn conn
|> put_resp_content_type("application/atom+xml") |> put_resp_content_type("application/atom+xml")
|> send_resp(200, response) |> send_resp(200, response)
@ -163,12 +176,14 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def upload_json(conn, %{"media" => media}) do def upload_json(conn, %{"media" => media}) do
response = TwitterAPI.upload(media, "json") response = TwitterAPI.upload(media, "json")
conn conn
|> json_reply(200, response) |> json_reply(200, response)
end end
def get_by_id_or_ap_id(id) do def get_by_id_or_ap_id(id) do
activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id) activity = Repo.get(Activity, id) || Activity.get_create_activity_by_object_ap_id(id)
if activity.data["type"] == "Create" do if activity.data["type"] == "Create" do
activity activity
else else
@ -220,7 +235,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
{:ok, user} <- User.update_and_set_cache(change) do {:ok, user} <- User.update_and_set_cache(change) do
CommonAPI.update(user) CommonAPI.update(user)
%{"url" => [%{"href" => href} | _]} = object.data %{"url" => [%{"href" => href} | _]} = object.data
response = %{ url: href } |> Jason.encode! response = %{url: href} |> Jason.encode!()
conn conn
|> json_reply(200, response) |> json_reply(200, response)
end end
@ -232,7 +248,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
change <- User.info_changeset(user, %{info: new_info}), change <- User.info_changeset(user, %{info: new_info}),
{:ok, _user} <- User.update_and_set_cache(change) do {:ok, _user} <- User.update_and_set_cache(change) do
%{"url" => [%{"href" => href} | _]} = object.data %{"url" => [%{"href" => href} | _]} = object.data
response = %{ url: href } |> Jason.encode! response = %{url: href} |> Jason.encode!()
conn conn
|> json_reply(200, response) |> json_reply(200, response)
end end
@ -285,9 +302,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def friends_ids(%{assigns: %{user: user}} = conn, _params) do def friends_ids(%{assigns: %{user: user}} = conn, _params) do
with {:ok, friends} <- User.get_friends(user) do with {:ok, friends} <- User.get_friends(user) do
ids = friends ids =
friends
|> Enum.map(fn x -> x.id end) |> Enum.map(fn x -> x.id end)
|> Jason.encode! |> Jason.encode!()
json(conn, ids) json(conn, ids)
else else
@ -300,7 +318,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end end
def update_profile(%{assigns: %{user: user}} = conn, params) do def update_profile(%{assigns: %{user: user}} = conn, params) do
params = if bio = params["description"] do params =
if bio = params["description"] do
Map.put(params, "bio", bio) Map.put(params, "bio", bio)
else else
params params
@ -339,6 +358,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
end end
defp error_json(conn, error_message) do 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
end end

View file

@ -10,7 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do def render("activity.json", %{activity: %{data: %{"type" => "Announce"}} = activity} = opts) do
user = User.get_by_ap_id(activity.data["actor"]) 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"]) announced_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"])
text = "#{user.nickname} retweeted a status." text = "#{user.nickname} retweeted a status."
@ -37,8 +37,10 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do def render("activity.json", %{activity: %{data: %{"type" => "Like"}} = activity} = opts) do
user = User.get_cached_by_ap_id(activity.data["actor"]) user = User.get_cached_by_ap_id(activity.data["actor"])
liked_activity = Activity.get_create_activity_by_object_ap_id(activity.data["object"]) 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." text = "#{user.nickname} favorited a status."
@ -57,20 +59,24 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
} }
end 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"]) actor = get_in(activity.data, ["actor"])
user = User.get_cached_by_ap_id(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 like_count = object["like_count"] || 0
announcement_count = object["announcement_count"] || 0 announcement_count = object["announcement_count"] || 0
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
attentions = activity.recipients attentions =
|> Enum.map(fn (ap_id) -> User.get_cached_by_ap_id(ap_id) end) activity.recipients
|> Enum.filter(&(&1)) |> Enum.map(fn ap_id -> User.get_cached_by_ap_id(ap_id) end)
|> Enum.map(fn (user) -> UserView.render("show.json", %{user: user, for: opts[:for]}) end) |> Enum.filter(& &1)
|> Enum.map(fn user -> UserView.render("show.json", %{user: user, for: opts[:for]}) end)
conversation_id = conversation_id(activity) conversation_id = conversation_id(activity)
@ -81,13 +87,16 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
summary = activity.data["object"]["summary"] summary = activity.data["object"]["summary"]
content = object["content"] content = object["content"]
content = if !!summary and summary != "" do
content =
if !!summary and summary != "" do
"<span>#{activity.data["object"]["summary"]}</span><br />#{content}</span>" "<span>#{activity.data["object"]["summary"]}</span><br />#{content}</span>"
else else
content content
end end
html = HtmlSanitizeEx.basic_html(content) html =
HtmlSanitizeEx.basic_html(content)
|> Formatter.emojify(object["emoji"]) |> Formatter.emojify(object["emoji"])
%{ %{
@ -117,7 +126,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
defp conversation_id(activity) do defp conversation_id(activity) do
with context when not is_nil(context) <- activity.data["context"] do with context when not is_nil(context) <- activity.data["context"] do
TwitterAPI.context_to_conversation_id(context) TwitterAPI.context_to_conversation_id(context)
else _e -> nil else
_e -> nil
end end
end end
end end

View file

@ -14,7 +14,9 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
def render("user.json", %{user: user = %User{}} = assigns) do def render("user.json", %{user: user = %User{}} = assigns) do
image = User.avatar_url(user) |> MediaProxy.url() image = User.avatar_url(user) |> MediaProxy.url()
{following, follows_you, statusnet_blocking} = if assigns[:for] do
{following, follows_you, statusnet_blocking} =
if assigns[:for] do
{ {
User.following?(assigns[:for], user), User.following?(assigns[:for], user),
User.following?(user, assigns[:for]), User.following?(user, assigns[:for]),
@ -27,7 +29,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
user_info = User.get_cached_user_info(user) user_info = User.get_cached_user_info(user)
data = %{ 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), "description" => HtmlSanitizeEx.strip_tags(user.bio),
"favourites_count" => 0, "favourites_count" => 0,
"followers_count" => user_info[:follower_count], "followers_count" => user_info[:follower_count],
@ -59,9 +61,14 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
end end
end end
def render("short.json", %{user: %User{ def render("short.json", %{
nickname: nickname, id: id, ap_id: ap_id, name: name user: %User{
}}) do nickname: nickname,
id: id,
ap_id: ap_id,
name: name
}
}) do
%{ %{
"fullname" => name, "fullname" => name,
"id" => id, "id" => id,

View file

@ -12,6 +12,6 @@ defmodule Pleroma.Web.ErrorView do
# In case no render clause matches or no # In case no render clause matches or no
# template is found, let's render it as 500 # template is found, let's render it as 500
def template_not_found(_template, assigns) do def template_not_found(_template, assigns) do
render "500.json", assigns render("500.json", assigns)
end end
end end

View file

@ -26,7 +26,8 @@ defmodule Pleroma.Web do
def view do def view do
quote do quote do
use Phoenix.View, root: "lib/pleroma/web/templates", use Phoenix.View,
root: "lib/pleroma/web/templates",
namespace: Pleroma.Web namespace: Pleroma.Web
# Import convenience functions from controllers # Import convenience functions from controllers
@ -59,6 +60,6 @@ defmodule Pleroma.Web do
end end
def base_url do def base_url do
Pleroma.Web.Endpoint.url Pleroma.Web.Endpoint.url()
end end
end end

View file

@ -8,41 +8,54 @@ defmodule Pleroma.Web.WebFinger do
require Logger require Logger
def host_meta do 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 end
def webfinger(resource, "JSON") do def webfinger(resource, "JSON") do
host = Pleroma.Web.Endpoint.host host = Pleroma.Web.Endpoint.host()
regex = ~r/(acct:)?(?<username>\w+)@#{host}/ regex = ~r/(acct:)?(?<username>\w+)@#{host}/
with %{"username" => username} <- Regex.named_captures(regex, resource) do with %{"username" => username} <- Regex.named_captures(regex, resource) do
user = User.get_by_nickname(username) user = User.get_by_nickname(username)
{:ok, represent_user(user, "JSON")} {:ok, represent_user(user, "JSON")}
else _e -> else
_e ->
with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do
{:ok, represent_user(user, "JSON")} {:ok, represent_user(user, "JSON")}
else _e -> else
_e ->
{:error, "Couldn't find user"} {:error, "Couldn't find user"}
end end
end end
end end
def webfinger(resource, "XML") do def webfinger(resource, "XML") do
host = Pleroma.Web.Endpoint.host host = Pleroma.Web.Endpoint.host()
regex = ~r/(acct:)?(?<username>\w+)@#{host}/ regex = ~r/(acct:)?(?<username>\w+)@#{host}/
with %{"username" => username} <- Regex.named_captures(regex, resource) do with %{"username" => username} <- Regex.named_captures(regex, resource) do
user = User.get_by_nickname(username) user = User.get_by_nickname(username)
{:ok, represent_user(user, "XML")} {:ok, represent_user(user, "XML")}
else _e -> else
_e ->
with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do with user when not is_nil(user) <- User.get_cached_by_ap_id(resource) do
{:ok, represent_user(user, "XML")} {:ok, represent_user(user, "XML")}
else _e -> else
_e ->
{:error, "Couldn't find user"} {:error, "Couldn't find user"}
end end
end end
@ -52,16 +65,28 @@ defmodule Pleroma.Web.WebFinger do
{:ok, user} = ensure_keys_present(user) {:ok, user} = ensure_keys_present(user)
{:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"])
magic_key = Salmon.encode_key(public) 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], "aliases" => [user.ap_id],
"links" => [ "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" => "salmon", "href" => OStatus.salmon_path(user)},
%{"rel" => "magic-public-key", "href" => "data:application/magic-public-key,#{magic_key}"}, %{"rel" => "magic-public-key", "href" => "data:application/magic-public-key,#{magic_key}"},
%{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id}, %{"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 end
@ -70,30 +95,42 @@ defmodule Pleroma.Web.WebFinger do
{:ok, user} = ensure_keys_present(user) {:ok, user} = ensure_keys_present(user)
{:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"])
magic_key = Salmon.encode_key(public) 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}, {:Alias, user.ap_id},
{:Link, %{rel: "http://schemas.google.com/g/2010#updates-from", type: "application/atom+xml", href: OStatus.feed_path(user)}}, {:Link,
{:Link, %{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)
}},
{: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: "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: "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 end
# This seems a better fit in Salmon # This seems a better fit in Salmon
def ensure_keys_present(user) do def ensure_keys_present(user) do
info = user.info || %{} info = user.info || %{}
if info["keys"] do if info["keys"] do
{:ok, user} {:ok, user}
else else
{:ok, pem} = Salmon.generate_rsa_pem {:ok, pem} = Salmon.generate_rsa_pem()
info = Map.put(info, "keys", pem) info = Map.put(info, "keys", pem)
Ecto.Changeset.change(user, info: info) Ecto.Changeset.change(user, info: info)
|> User.update_and_set_cache() |> User.update_and_set_cache()
end end
@ -102,11 +139,28 @@ defmodule Pleroma.Web.WebFinger do
defp webfinger_from_xml(doc) do defp webfinger_from_xml(doc) do
magic_key = XML.string_from_xpath(~s{//Link[@rel="magic-public-key"]/@href}, doc) magic_key = XML.string_from_xpath(~s{//Link[@rel="magic-public-key"]/@href}, doc)
"data:application/magic-public-key," <> magic_key = magic_key "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) subject = XML.string_from_xpath("//Subject", doc)
salmon = XML.string_from_xpath(~s{//Link[@rel="salmon"]/@href}, 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 = %{ data = %{
"magic_key" => magic_key, "magic_key" => magic_key,
"topic" => topic, "topic" => topic,
@ -115,33 +169,42 @@ defmodule Pleroma.Web.WebFinger do
"subscribe_address" => subscribe_address, "subscribe_address" => subscribe_address,
"ap_id" => ap_id "ap_id" => ap_id
} }
{:ok, data} {:ok, data}
end end
defp webfinger_from_json(doc) do defp webfinger_from_json(doc) do
data = Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn (link, data) -> data =
Enum.reduce(doc["links"], %{"subject" => doc["subject"]}, fn link, data ->
case {link["type"], link["rel"]} do case {link["type"], link["rel"]} do
{"application/activity+json", "self"} -> {"application/activity+json", "self"} ->
Map.put(data, "ap_id", link["href"]) Map.put(data, "ap_id", link["href"])
{_, "magic-public-key"} -> {_, "magic-public-key"} ->
"data:application/magic-public-key," <> magic_key = link["href"] "data:application/magic-public-key," <> magic_key = link["href"]
Map.put(data, "magic_key", magic_key) Map.put(data, "magic_key", magic_key)
{"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} -> {"application/atom+xml", "http://schemas.google.com/g/2010#updates-from"} ->
Map.put(data, "topic", link["href"]) Map.put(data, "topic", link["href"])
{_, "salmon"} -> {_, "salmon"} ->
Map.put(data, "salmon", link["href"]) Map.put(data, "salmon", link["href"])
{_, "http://ostatus.org/schema/1.0/subscribe"} -> {_, "http://ostatus.org/schema/1.0/subscribe"} ->
Map.put(data, "subscribe_address", link["template"]) Map.put(data, "subscribe_address", link["template"])
_ -> _ ->
Logger.debug("Unhandled type: #{inspect(link["type"])}") Logger.debug("Unhandled type: #{inspect(link["type"])}")
data data
end end
end) end)
{:ok, data} {:ok, data}
end end
def get_template_from_xml(body) do def get_template_from_xml(body) do
xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template" xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template"
with doc when doc != :error <- XML.parse_document(body), 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} {:ok, template}
@ -149,7 +212,8 @@ defmodule Pleroma.Web.WebFinger do
end end
def find_lrdd_template(domain) do 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) get_template_from_xml(body)
else else
_ -> _ ->
@ -163,22 +227,32 @@ defmodule Pleroma.Web.WebFinger do
def finger(account) do def finger(account) do
account = String.trim_leading(account, "@") account = String.trim_leading(account, "@")
domain = with [_name, domain] <- String.split(account, "@") do
domain =
with [_name, domain] <- String.split(account, "@") do
domain domain
else _e -> else
_e ->
URI.parse(account).host URI.parse(account).host
end end
case find_lrdd_template(domain) do case find_lrdd_template(domain) do
{:ok, template} -> {:ok, template} ->
address = String.replace(template, "{uri}", URI.encode(account)) address = String.replace(template, "{uri}", URI.encode(account))
_ -> _ ->
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}" address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
end 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 {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response do
doc = XML.parse_document(body) doc = XML.parse_document(body)
if doc != :error do if doc != :error do
webfinger_from_xml(doc) webfinger_from_xml(doc)
else else

View file

@ -4,7 +4,7 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
def host_meta(conn, _params) do def host_meta(conn, _params) do
xml = WebFinger.host_meta xml = WebFinger.host_meta()
conn conn
|> put_resp_content_type("application/xrd+xml") |> put_resp_content_type("application/xrd+xml")
@ -21,12 +21,14 @@ defmodule Pleroma.Web.WebFinger.WebFingerController do
else else
_e -> send_resp(conn, 404, "Couldn't find user") _e -> send_resp(conn, 404, "Couldn't find user")
end end
n when n in ["json", "jrd+json"] -> n when n in ["json", "jrd+json"] ->
with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do with {:ok, response} <- WebFinger.webfinger(resource, "JSON") do
json(conn, response) json(conn, response)
else else
_e -> send_resp(conn, 404, "Couldn't find user") _e -> send_resp(conn, 404, "Couldn't find user")
end end
_ -> _ ->
send_resp(conn, 404, "Unsupported format") send_resp(conn, 404, "Unsupported format")
end end

View file

@ -26,12 +26,13 @@ defmodule Pleroma.Web.Websub do
url = hd(String.split(subscription.callback, "?")) url = hd(String.split(subscription.callback, "?"))
query = URI.parse(subscription.callback).query || "" query = URI.parse(subscription.callback).query || ""
params = Map.merge(params, URI.decode_query(query)) params = Map.merge(params, URI.decode_query(query))
with {:ok, response} <- getter.(url, [], [params: params]),
^challenge <- response.body with {:ok, response} <- getter.(url, [], params: params),
do ^challenge <- response.body do
changeset = Changeset.change(subscription, %{state: "active"}) changeset = Changeset.change(subscription, %{state: "active"})
Repo.update(changeset) Repo.update(changeset)
else e -> else
e ->
Logger.debug("Couldn't verify subscription") Logger.debug("Couldn't verify subscription")
Logger.debug(inspect(e)) Logger.debug(inspect(e))
{:error, subscription} {:error, subscription}
@ -46,14 +47,21 @@ defmodule Pleroma.Web.Websub do
"Undo", "Undo",
"Delete" "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. # TODO: Only send to still valid subscriptions.
query = from sub in WebsubServerSubscription, query =
from(
sub in WebsubServerSubscription,
where: sub.topic == ^topic and sub.state == "active", where: sub.topic == ^topic and sub.state == "active",
where: fragment("? > NOW()", sub.valid_until) where: fragment("? > NOW()", sub.valid_until)
)
subscriptions = Repo.all(query) subscriptions = Repo.all(query)
Enum.each(subscriptions, fn(sub) ->
response = user Enum.each(subscriptions, fn sub ->
response =
user
|> FeedRepresenter.to_simple_form([activity], [user]) |> FeedRepresenter.to_simple_form([activity], [user])
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string
@ -64,22 +72,24 @@ defmodule Pleroma.Web.Websub do
callback: sub.callback, callback: sub.callback,
secret: sub.secret secret: sub.secret
} }
Pleroma.Web.Federator.enqueue(:publish_single_websub, data) Pleroma.Web.Federator.enqueue(:publish_single_websub, data)
end) end)
end end
def publish(_, _, _), do: "" def publish(_, _, _), do: ""
def sign(secret, doc) 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 end
def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) do
with {:ok, topic} <- valid_topic(params, user), with {:ok, topic} <- valid_topic(params, user),
{:ok, lease_time} <- lease_time(params), {:ok, lease_time} <- lease_time(params),
secret <- params["hub.secret"], secret <- params["hub.secret"],
callback <- params["hub.callback"] callback <- params["hub.callback"] do
do
subscription = get_subscription(topic, callback) subscription = get_subscription(topic, callback)
data = %{ data = %{
state: subscription.state || "requested", state: subscription.state || "requested",
topic: topic, topic: topic,
@ -90,14 +100,16 @@ defmodule Pleroma.Web.Websub do
change = Changeset.change(subscription, data) change = Changeset.change(subscription, data)
websub = Repo.insert_or_update!(change) websub = Repo.insert_or_update!(change)
change = Changeset.change(websub, %{valid_until: change =
NaiveDateTime.add(websub.updated_at, lease_time)}) Changeset.change(websub, %{valid_until: NaiveDateTime.add(websub.updated_at, lease_time)})
websub = Repo.update!(change) websub = Repo.update!(change)
Pleroma.Web.Federator.enqueue(:verify_websub, websub) Pleroma.Web.Federator.enqueue(:verify_websub, websub)
{:ok, websub} {:ok, websub}
else {:error, reason} -> else
{:error, reason} ->
Logger.debug("Couldn't create subscription") Logger.debug("Couldn't create subscription")
Logger.debug(inspect(reason)) Logger.debug(inspect(reason))
@ -112,7 +124,8 @@ defmodule Pleroma.Web.Websub do
# Temp hack for mastodon. # Temp hack for mastodon.
defp lease_time(%{"hub.lease_seconds" => ""}) do defp lease_time(%{"hub.lease_seconds" => ""}) do
{:ok, 60 * 60 * 24 * 3} # three days # three days
{:ok, 60 * 60 * 24 * 3}
end end
defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do defp lease_time(%{"hub.lease_seconds" => lease_seconds}) do
@ -120,7 +133,8 @@ defmodule Pleroma.Web.Websub do
end end
defp lease_time(_) do defp lease_time(_) do
{:ok, 60 * 60 * 24 * 3} # three days # three days
{:ok, 60 * 60 * 24 * 3}
end end
defp valid_topic(%{"hub.topic" => topic}, user) do defp valid_topic(%{"hub.topic" => topic}, user) do
@ -134,21 +148,26 @@ defmodule Pleroma.Web.Websub do
def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do
topic = subscribed.info["topic"] topic = subscribed.info["topic"]
# FIXME: Race condition, use transactions # FIXME: Race condition, use transactions
{:ok, subscription} = with subscription when not is_nil(subscription) <- Repo.get_by(WebsubClientSubscription, topic: topic) do {:ok, subscription} =
subscribers = [subscriber.ap_id | subscription.subscribers] |> Enum.uniq 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}) change = Ecto.Changeset.change(subscription, %{subscribers: subscribers})
Repo.update(change) Repo.update(change)
else _e -> else
_e ->
subscription = %WebsubClientSubscription{ subscription = %WebsubClientSubscription{
topic: topic, topic: topic,
hub: subscribed.info["hub"], hub: subscribed.info["hub"],
subscribers: [subscriber.ap_id], subscribers: [subscriber.ap_id],
state: "requested", state: "requested",
secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64, secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(),
user: subscribed user: subscribed
} }
Repo.insert(subscription) Repo.insert(subscription)
end end
requester.(subscription) requester.(subscription)
end end
@ -159,14 +178,14 @@ defmodule Pleroma.Web.Websub do
doc <- XML.parse_document(body), doc <- XML.parse_document(body),
uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc), 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 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) name = XML.string_from_xpath("/feed/author[1]/name", doc)
preferredUsername = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc) preferredUsername = XML.string_from_xpath("/feed/author[1]/poco:preferredUsername", doc)
displayName = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc) displayName = XML.string_from_xpath("/feed/author[1]/poco:displayName", doc)
avatar = OStatus.make_avatar_object(doc) avatar = OStatus.make_avatar_object(doc)
bio = XML.string_from_xpath("/feed/author[1]/summary", doc) bio = XML.string_from_xpath("/feed/author[1]/summary", doc)
{:ok, %{ {:ok,
%{
"uri" => uri, "uri" => uri,
"hub" => hub, "hub" => hub,
"nickname" => preferredUsername || name, "nickname" => preferredUsername || name,
@ -175,7 +194,8 @@ defmodule Pleroma.Web.Websub do
"avatar" => avatar, "avatar" => avatar,
"bio" => bio "bio" => bio
}} }}
else e -> else
e ->
{:error, e} {:error, e}
end end
end end
@ -190,20 +210,23 @@ defmodule Pleroma.Web.Websub do
# This checks once a second if we are confirmed yet # This checks once a second if we are confirmed yet
websub_checker = fn -> websub_checker = fn ->
helper = fn (helper) -> helper = fn helper ->
:timer.sleep(1000) :timer.sleep(1000)
websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted") websub = Repo.get_by(WebsubClientSubscription, id: websub.id, state: "accepted")
if websub, do: websub, else: helper.(helper) if websub, do: websub, else: helper.(helper)
end end
helper.(helper) helper.(helper)
end end
task = Task.async(websub_checker) 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} <- Task.yield(task, timeout) do
{:ok, websub} {:ok, websub}
else e -> else
e ->
Task.shutdown(task) Task.shutdown(task)
change = Ecto.Changeset.change(websub, %{state: "rejected"}) change = Ecto.Changeset.change(websub, %{state: "rejected"})
@ -219,14 +242,13 @@ defmodule Pleroma.Web.Websub do
def refresh_subscriptions(delta \\ 60 * 60 * 24) do def refresh_subscriptions(delta \\ 60 * 60 * 24) do
Logger.debug("Refreshing subscriptions") 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, query = from(sub in WebsubClientSubscription, where: sub.valid_until < ^cut_off)
where: sub.valid_until < ^cut_off
subs = Repo.all(query) subs = Repo.all(query)
Enum.each(subs, fn (sub) -> Enum.each(subs, fn sub ->
Pleroma.Web.Federator.enqueue(:request_subscription, sub) Pleroma.Web.Federator.enqueue(:request_subscription, sub)
end) end)
end end

View file

@ -3,13 +3,13 @@ defmodule Pleroma.Web.Websub.WebsubClientSubscription do
alias Pleroma.User alias Pleroma.User
schema "websub_client_subscriptions" do schema "websub_client_subscriptions" do
field :topic, :string field(:topic, :string)
field :secret, :string field(:secret, :string)
field :valid_until, :naive_datetime field(:valid_until, :naive_datetime)
field :state, :string field(:state, :string)
field :subscribers, {:array, :string}, default: [] field(:subscribers, {:array, :string}, default: [])
field :hub, :string field(:hub, :string)
belongs_to :user, User belongs_to(:user, User)
timestamps() timestamps()
end end

View file

@ -8,34 +8,47 @@ defmodule Pleroma.Web.Websub.WebsubController do
def websub_subscription_request(conn, %{"nickname" => nickname} = params) do def websub_subscription_request(conn, %{"nickname" => nickname} = params) do
user = User.get_cached_by_nickname(nickname) user = User.get_cached_by_nickname(nickname)
with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) with {:ok, _websub} <- Websub.incoming_subscription_request(user, params) do
do
conn conn
|> send_resp(202, "Accepted") |> send_resp(202, "Accepted")
else {:error, reason} -> else
{:error, reason} ->
conn conn
|> send_resp(500, reason) |> send_resp(500, reason)
end end
end end
# TODO: Extract this into the Websub module # 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("Got WebSub confirmation")
Logger.debug(inspect(params)) Logger.debug(inspect(params))
lease_seconds = if params["hub.lease_seconds"] do
lease_seconds =
if params["hub.lease_seconds"] do
String.to_integer(params["hub.lease_seconds"]) String.to_integer(params["hub.lease_seconds"])
else else
# Guess 3 days # Guess 3 days
60 * 60 * 24 * 3 60 * 60 * 24 * 3
end end
with %WebsubClientSubscription{} = websub <- Repo.get_by(WebsubClientSubscription, id: id, topic: topic) do with %WebsubClientSubscription{} = websub <-
valid_until = NaiveDateTime.add(NaiveDateTime.utc_now, lease_seconds) 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}) change = Ecto.Changeset.change(websub, %{state: "accepted", valid_until: valid_until})
{:ok, _websub} = Repo.update(change) {:ok, _websub} = Repo.update(change)
conn conn
|> send_resp(200, challenge) |> send_resp(200, challenge)
else _e -> else
_e ->
conn conn
|> send_resp(500, "Error") |> send_resp(500, "Error")
end end
@ -48,10 +61,13 @@ defmodule Pleroma.Web.Websub.WebsubController do
{:ok, body, _conn} = read_body(conn), {:ok, body, _conn} = read_body(conn),
^signature <- Websub.sign(websub.secret, body) do ^signature <- Websub.sign(websub.secret, body) do
Federator.enqueue(:incoming_doc, body) Federator.enqueue(:incoming_doc, body)
conn conn
|> send_resp(200, "OK") |> send_resp(200, "OK")
else _e -> else
_e ->
Logger.debug("Can't handle incoming subscription post") Logger.debug("Can't handle incoming subscription post")
conn conn
|> send_resp(500, "Error") |> send_resp(500, "Error")
end end

View file

@ -2,11 +2,11 @@ defmodule Pleroma.Web.Websub.WebsubServerSubscription do
use Ecto.Schema use Ecto.Schema
schema "websub_server_subscriptions" do schema "websub_server_subscriptions" do
field :topic, :string field(:topic, :string)
field :callback, :string field(:callback, :string)
field :secret, :string field(:secret, :string)
field :valid_until, :naive_datetime field(:valid_until, :naive_datetime)
field :state, :string field(:state, :string)
timestamps() timestamps()
end end

View file

@ -2,21 +2,24 @@ defmodule Pleroma.Web.XML do
require Logger require Logger
def string_from_xpath(_, :error), do: nil def string_from_xpath(_, :error), do: nil
def string_from_xpath(xpath, doc) do def string_from_xpath(xpath, doc) do
{:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc) {:xmlObj, :string, res} = :xmerl_xpath.string('string(#{xpath})', doc)
res = res res =
res
|> to_string |> to_string
|> String.trim |> String.trim()
if res == "", do: nil, else: res if res == "", do: nil, else: res
end end
def parse_document(text) do def parse_document(text) do
try do try do
{doc, _rest} = text {doc, _rest} =
|> :binary.bin_to_list text
|> :xmerl_scan.string |> :binary.bin_to_list()
|> :xmerl_scan.string()
doc doc
catch catch

View file

@ -1,8 +1,10 @@
defmodule Phoenix.Transports.WebSocket.Raw do defmodule Phoenix.Transports.WebSocket.Raw do
import Plug.Conn, only: [ import Plug.Conn,
only: [
fetch_query_params: 1, fetch_query_params: 1,
send_resp: 3 send_resp: 3
] ]
alias Phoenix.Socket.Transport alias Phoenix.Socket.Transport
def default_config do def default_config do
@ -16,7 +18,8 @@ defmodule Phoenix.Transports.WebSocket.Raw do
def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do def init(%Plug.Conn{method: "GET"} = conn, {endpoint, handler, transport}) do
{_, opts} = handler.__transport__(transport) {_, opts} = handler.__transport__(transport)
conn = conn conn =
conn
|> fetch_query_params |> fetch_query_params
|> Transport.transport_log(opts[:transport_log]) |> Transport.transport_log(opts[:transport_log])
|> Transport.force_ssl(handler, endpoint, opts) |> Transport.force_ssl(handler, endpoint, opts)
@ -27,10 +30,12 @@ defmodule Phoenix.Transports.WebSocket.Raw do
case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do case Transport.connect(endpoint, handler, transport, __MODULE__, nil, conn.params) do
{:ok, socket} -> {:ok, socket} ->
{:ok, conn, {__MODULE__, {socket, opts}}} {:ok, conn, {__MODULE__, {socket, opts}}}
:error -> :error ->
send_resp(conn, :forbidden, "") send_resp(conn, :forbidden, "")
{:error, conn} {:error, conn}
end end
_ -> _ ->
{:error, conn} {:error, conn}
end end
@ -52,10 +57,13 @@ defmodule Phoenix.Transports.WebSocket.Raw do
|> case do |> case do
{op, data} -> {op, data} ->
{:reply, {op, data}, state} {:reply, {op, data}, state}
{op, data, state} -> {op, data, state} ->
{:reply, {op, data}, state} {:reply, {op, data}, state}
%{} = state -> %{} = state ->
{:ok, state} {:ok, state}
_ -> _ ->
{:ok, state} {:ok, state}
end end

View file

@ -23,7 +23,7 @@ defmodule Pleroma.XmlBuilder do
for element <- content do for element <- content do
to_xml(element) to_xml(element)
end end
|> Enum.join |> Enum.join()
end end
def to_xml(%NaiveDateTime{} = time) do def to_xml(%NaiveDateTime{} = time) do
@ -33,10 +33,12 @@ defmodule Pleroma.XmlBuilder do
def to_doc(content), do: ~s(<?xml version="1.0" encoding="UTF-8"?>) <> to_xml(content) def to_doc(content), do: ~s(<?xml version="1.0" encoding="UTF-8"?>) <> to_xml(content)
defp make_open_tag(tag, attributes) do defp make_open_tag(tag, attributes) do
attributes_string = for {attribute, value} <- attributes do attributes_string =
for {attribute, value} <- attributes do
"#{attribute}=\"#{value}\"" "#{attribute}=\"#{value}\""
end |> Enum.join(" ") end
|> Enum.join(" ")
[tag, attributes_string] |> Enum.join(" ") |> String.trim [tag, attributes_string] |> Enum.join(" ") |> String.trim()
end end
end end

27
mix.exs
View file

@ -2,22 +2,23 @@ defmodule Pleroma.Mixfile do
use Mix.Project use Mix.Project
def project do def project do
[app: :pleroma, [
app: :pleroma,
version: "0.9.0", version: "0.9.0",
elixir: "~> 1.4", elixir: "~> 1.4",
elixirc_paths: elixirc_paths(Mix.env), elixirc_paths: elixirc_paths(Mix.env()),
compilers: [:phoenix, :gettext] ++ Mix.compilers, compilers: [:phoenix, :gettext] ++ Mix.compilers(),
start_permanent: Mix.env == :prod, start_permanent: Mix.env() == :prod,
aliases: aliases(), aliases: aliases(),
deps: deps()] deps: deps()
]
end end
# Configuration for the OTP application. # Configuration for the OTP application.
# #
# Type `mix help compile.app` for more information. # Type `mix help compile.app` for more information.
def application do def application do
[mod: {Pleroma.Application, []}, [mod: {Pleroma.Application, []}, extra_applications: [:logger, :runtime_tools, :comeonin]]
extra_applications: [:logger, :runtime_tools, :comeonin]]
end end
# Specifies which paths to compile per environment. # Specifies which paths to compile per environment.
@ -28,7 +29,8 @@ defmodule Pleroma.Mixfile do
# #
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[{:phoenix, "~> 1.3.0"}, [
{:phoenix, "~> 1.3.0"},
{:phoenix_pubsub, "~> 1.0"}, {:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"}, {:phoenix_ecto, "~> 3.2"},
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.0.0"},
@ -43,7 +45,8 @@ defmodule Pleroma.Mixfile do
{:httpoison, "~> 0.11.2"}, {:httpoison, "~> 0.11.2"},
{:jason, "~> 1.0"}, {:jason, "~> 1.0"},
{:ex_machina, "~> 2.0", only: :test}, {:ex_machina, "~> 2.0", only: :test},
{:credo, "~> 0.7", only: [:dev, :test]}] {:credo, "~> 0.7", only: [:dev, :test]}
]
end end
# Aliases are shortcuts or tasks specific to the current project. # Aliases are shortcuts or tasks specific to the current project.
@ -53,8 +56,10 @@ defmodule Pleroma.Mixfile do
# #
# See the documentation for `Mix` for more info on aliases. # See the documentation for `Mix` for more info on aliases.
defp aliases do defp aliases do
["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"], [
"ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
"ecto.reset": ["ecto.drop", "ecto.setup"], "ecto.reset": ["ecto.drop", "ecto.setup"],
"test": ["ecto.create --quiet", "ecto.migrate", "test"]] test: ["ecto.create --quiet", "ecto.migrate", "test"]
]
end end
end end

View file

@ -18,7 +18,9 @@ defmodule Pleroma.ActivityTest do
test "returns the activity that created an object" do test "returns the activity that created an object" do
activity = insert(:note_activity) 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 assert activity == found_activity
end end

View file

@ -7,44 +7,56 @@ defmodule Pleroma.FormatterTest do
describe ".add_hashtag_links" do describe ".add_hashtag_links" do
test "turns hashtags into links" do test "turns hashtags into links" do
text = "I love #cofe and #2hu" text = "I love #cofe and #2hu"
expected_text = "I love <a href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
expected_text =
"I love <a href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
tags = Formatter.parse_tags(text) 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
end end
describe ".add_links" do describe ".add_links" do
test "turning urls into 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." text = "Hey, check out https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla."
expected = "Hey, check out <a href='https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla'>https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla</a>."
assert Formatter.add_links({[], text}) |> Formatter.finalize == expected expected =
"Hey, check out <a href='https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla'>https://www.youtube.com/watch?v=8Zg1-TufF%20zY?x=1&y=2#blabla</a>."
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
text = "https://mastodon.social/@lambadalambda" text = "https://mastodon.social/@lambadalambda"
expected = "<a href='https://mastodon.social/@lambadalambda'>https://mastodon.social/@lambadalambda</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize == expected expected =
"<a href='https://mastodon.social/@lambadalambda'>https://mastodon.social/@lambadalambda</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
text = "@lambadalambda" text = "@lambadalambda"
expected = "@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/" text = "http://www.cs.vu.nl/~ast/intel/"
expected = "<a href='http://www.cs.vu.nl/~ast/intel/'>http://www.cs.vu.nl/~ast/intel/</a>" expected = "<a href='http://www.cs.vu.nl/~ast/intel/'>http://www.cs.vu.nl/~ast/intel/</a>"
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" text = "https://forum.zdoom.org/viewtopic.php?f=44&t=57087"
expected = "<a href='https://forum.zdoom.org/viewtopic.php?f=44&t=57087'>https://forum.zdoom.org/viewtopic.php?f=44&t=57087</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize == expected expected =
"<a href='https://forum.zdoom.org/viewtopic.php?f=44&t=57087'>https://forum.zdoom.org/viewtopic.php?f=44&t=57087</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul" text = "https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul"
expected = "<a href='https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul'>https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize == expected expected =
"<a href='https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul'>https://en.wikipedia.org/wiki/Sophia_(Gnosticism)#Mythos_of_the_soul</a>"
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
end end
end end
@ -60,9 +72,14 @@ defmodule Pleroma.FormatterTest do
{subs, text} = Formatter.add_user_links({[], text}, mentions) {subs, text} = Formatter.add_user_links({[], text}, mentions)
assert length(subs) == 3 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 = "<span><a href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a href='#{archaeme.ap_id}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a href='#{archaeme_remote.ap_id}'>@<span>archaeme</span></a></span>" expected_text =
"<span><a href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a href='#{
archaeme.ap_id
}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a href='#{
archaeme_remote.ap_id
}'>@<span>archaeme</span></a></span>"
assert expected_text == Formatter.finalize({subs, text}) assert expected_text == Formatter.finalize({subs, text})
end end
@ -71,6 +88,7 @@ defmodule Pleroma.FormatterTest do
describe ".parse_tags" do describe ".parse_tags" do
test "parses tags in the text" do test "parses tags in the text" do
text = "Here's a #Test. Maybe these are #working or not. What about #漢字? And #は。" text = "Here's a #Test. Maybe these are #working or not. What about #漢字? And #は。"
expected = [ expected = [
{"#Test", "test"}, {"#Test", "test"},
{"#working", "working"}, {"#working", "working"},
@ -92,7 +110,7 @@ defmodule Pleroma.FormatterTest do
expected_result = [ expected_result = [
{"@gsimg", gsimg}, {"@gsimg", gsimg},
{"@archaeme", archaeme}, {"@archaeme", archaeme},
{"@archaeme@archae.me", archaeme_remote}, {"@archaeme@archae.me", archaeme_remote}
] ]
assert Formatter.parse_mentions(text) == expected_result assert Formatter.parse_mentions(text) == expected_result
@ -101,7 +119,8 @@ defmodule Pleroma.FormatterTest do
test "it adds cool emoji" do test "it adds cool emoji" do
text = "I love :moominmamma:" text = "I love :moominmamma:"
expected_result = "I love <img height='32px' width='32px' alt='moominmamma' title='moominmamma' src='/finmoji/128px/moominmamma-128.png' />" expected_result =
"I love <img height='32px' width='32px' alt='moominmamma' title='moominmamma' src='/finmoji/128px/moominmamma-128.png' />"
assert Formatter.emojify(text) == expected_result assert Formatter.emojify(text) == expected_result
end end

View file

@ -10,7 +10,10 @@ defmodule Pleroma.NotificationTest do
other_user = insert(:user) other_user = insert(:user)
third_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) {:ok, [notification, other_notification]} = Notification.create_notifications(activity)
@ -37,7 +40,9 @@ defmodule Pleroma.NotificationTest do
user = insert(:user) user = insert(:user)
other_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.create_notifications(activity)
{:ok, notification} = Notification.get(other_user, notification.id) {:ok, notification} = Notification.get(other_user, notification.id)
@ -48,7 +53,9 @@ defmodule Pleroma.NotificationTest do
user = insert(:user) user = insert(:user)
other_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.create_notifications(activity)
{:error, _notification} = Notification.get(user, notification.id) {:error, _notification} = Notification.get(user, notification.id)
end end
@ -59,7 +66,9 @@ defmodule Pleroma.NotificationTest do
user = insert(:user) user = insert(:user)
other_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.create_notifications(activity)
{:ok, notification} = Notification.dismiss(other_user, notification.id) {:ok, notification} = Notification.dismiss(other_user, notification.id)
@ -70,7 +79,9 @@ defmodule Pleroma.NotificationTest do
user = insert(:user) user = insert(:user)
other_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.create_notifications(activity)
{:error, _notification} = Notification.dismiss(user, notification.id) {:error, _notification} = Notification.dismiss(user, notification.id)
end end
@ -82,9 +93,18 @@ defmodule Pleroma.NotificationTest do
other_user = insert(:user) other_user = insert(:user)
third_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, _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) {:ok, _notifs} = Notification.create_notifications(activity)
Notification.clear(other_user) Notification.clear(other_user)

View file

@ -37,7 +37,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
describe "without an authorization header" do describe "without an authorization header" do
test "it halts the application" do test "it halts the application" do
conn = build_conn() conn =
build_conn()
|> Plug.Session.call(Plug.Session.init(@session_opts)) |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session |> fetch_session
|> AuthenticationPlug.call(%{}) |> AuthenticationPlug.call(%{})
@ -47,7 +48,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
end end
test "it assigns a nil user if the 'optional' option is used" do test "it assigns a nil user if the 'optional' option is used" do
conn = build_conn() conn =
build_conn()
|> Plug.Session.call(Plug.Session.init(@session_opts)) |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session |> fetch_session
|> AuthenticationPlug.call(%{optional: true}) |> AuthenticationPlug.call(%{optional: true})
@ -126,7 +128,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
header = basic_auth_enc("dude", "guy") header = basic_auth_enc("dude", "guy")
conn = conn conn =
conn
|> Plug.Session.call(Plug.Session.init(@session_opts)) |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session |> fetch_session
|> put_req_header("authorization", header) |> put_req_header("authorization", header)
@ -147,7 +150,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
header = basic_auth_enc("dude", "guy") header = basic_auth_enc("dude", "guy")
conn = conn conn =
conn
|> Plug.Session.call(Plug.Session.init(@session_opts)) |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session |> fetch_session
|> put_req_header("authorization", header) |> put_req_header("authorization", header)
@ -167,7 +171,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
header = basic_auth_enc("dude", "THIS IS WRONG") header = basic_auth_enc("dude", "THIS IS WRONG")
conn = conn conn =
conn
|> Plug.Session.call(Plug.Session.init(@session_opts)) |> Plug.Session.call(Plug.Session.init(@session_opts))
|> fetch_session |> fetch_session
|> put_session(:user_id, @user.id) |> put_session(:user_id, @user.id)
@ -182,7 +187,8 @@ defmodule Pleroma.Plugs.AuthenticationPlugTest do
describe "with an assigned user" do describe "with an assigned user" do
test "it does nothing, returning the incoming conn", %{conn: conn} do test "it does nothing, returning the incoming conn", %{conn: conn} do
conn = conn conn =
conn
|> assign(:user, @user) |> assign(:user, @user)
conn_result = AuthenticationPlug.call(conn, %{}) conn_result = AuthenticationPlug.call(conn, %{})

View file

@ -4,17 +4,19 @@ defmodule Pleroma.Builders.ActivityBuilder do
def build(data \\ %{}, opts \\ %{}) do def build(data \\ %{}, opts \\ %{}) do
user = opts[:user] || Pleroma.Factory.insert(:user) user = opts[:user] || Pleroma.Factory.insert(:user)
activity = %{ activity = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id, "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"actor" => user.ap_id, "actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"type" => "Create", "type" => "Create",
"object" => %{ "object" => %{
"type" => "Note", "type" => "Note",
"content" => "test", "content" => "test",
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"]
} }
} }
Map.merge(activity, data) Map.merge(activity, data)
end end
@ -24,7 +26,7 @@ defmodule Pleroma.Builders.ActivityBuilder do
end end
def insert_list(times, data \\ %{}, opts \\ %{}) do def insert_list(times, data \\ %{}, opts \\ %{}) do
Enum.map(1..times, fn (n) -> Enum.map(1..times, fn n ->
{:ok, activity} = insert(data, opts) {:ok, activity} = insert(data, opts)
activity activity
end) end)

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Builders.UserBuilder do
bio: "A tester.", bio: "A tester.",
ap_id: "some id" ap_id: "some id"
} }
Map.merge(user, data) Map.merge(user, data)
end end

View file

@ -25,13 +25,13 @@ defmodule Pleroma.Web.ChannelCase do
end end
end end
setup tags do setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
unless tags[:async] do unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()})
end end
:ok :ok
end end
end end

View file

@ -26,14 +26,14 @@ defmodule Pleroma.Web.ConnCase do
end end
end end
setup tags do setup tags do
Cachex.clear(:user_cache) Cachex.clear(:user_cache)
:ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo) :ok = Ecto.Adapters.SQL.Sandbox.checkout(Pleroma.Repo)
unless tags[:async] do unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()}) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, {:shared, self()})
end end
{:ok, conn: Phoenix.ConnTest.build_conn()} {:ok, conn: Phoenix.ConnTest.build_conn()}
end end
end end

View file

@ -9,20 +9,27 @@ defmodule Pleroma.Factory do
password_hash: Comeonin.Pbkdf2.hashpwsalt("test"), password_hash: Comeonin.Pbkdf2.hashpwsalt("test"),
bio: sequence(:bio, &"Tester Number #{&1}") 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 end
def note_factory do def note_factory do
text = sequence(:text, &"This is :moominmamma: note #{&1}") text = sequence(:text, &"This is :moominmamma: note #{&1}")
user = insert(:user) user = insert(:user)
data = %{ data = %{
"type" => "Note", "type" => "Note",
"content" => text, "content" => text,
"id" => Pleroma.Web.ActivityPub.Utils.generate_object_id, "id" => Pleroma.Web.ActivityPub.Utils.generate_object_id(),
"actor" => user.ap_id, "actor" => user.ap_id,
"to" => ["https://www.w3.org/ns/activitystreams#Public"], "to" => ["https://www.w3.org/ns/activitystreams#Public"],
"published" => DateTime.utc_now() |> DateTime.to_iso8601, "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"likes" => [], "likes" => [],
"like_count" => 0, "like_count" => 0,
"context" => "2hu", "context" => "2hu",
@ -40,13 +47,14 @@ defmodule Pleroma.Factory do
def note_activity_factory do def note_activity_factory do
note = insert(:note) note = insert(:note)
data = %{ data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Create", "type" => "Create",
"actor" => note.data["actor"], "actor" => note.data["actor"],
"to" => note.data["to"], "to" => note.data["to"],
"object" => note.data, "object" => note.data,
"published" => DateTime.utc_now() |> DateTime.to_iso8601, "published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => note.data["context"] "context" => note.data["context"]
} }
@ -62,11 +70,11 @@ defmodule Pleroma.Factory do
user = insert(:user) user = insert(:user)
data = %{ data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => user.ap_id, "actor" => user.ap_id,
"type" => "Like", "type" => "Like",
"object" => note_activity.data["object"]["id"], "object" => note_activity.data["object"]["id"],
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601 "published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
} }
%Pleroma.Activity{ %Pleroma.Activity{
@ -79,11 +87,11 @@ defmodule Pleroma.Factory do
followed = insert(:user) followed = insert(:user)
data = %{ data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id, "id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"actor" => follower.ap_id, "actor" => follower.ap_id,
"type" => "Follow", "type" => "Follow",
"object" => followed.ap_id, "object" => followed.ap_id,
"published_at" => DateTime.utc_now() |> DateTime.to_iso8601 "published_at" => DateTime.utc_now() |> DateTime.to_iso8601()
} }
%Pleroma.Activity{ %Pleroma.Activity{
@ -96,7 +104,7 @@ defmodule Pleroma.Factory do
topic: "http://example.org", topic: "http://example.org",
callback: "http://example/org/callback", callback: "http://example/org/callback",
secret: "here's a secret", secret: "here's a secret",
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, 100), valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), 100),
state: "requested" state: "requested"
} }
end end

View file

@ -3,461 +3,712 @@ defmodule HTTPoisonMock do
def get(url, body \\ [], headers \\ []) 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json") body: File.read!("test/fixtures/httpoison_mock/framasoft@framatube.org.json")
}} }}
end 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{ 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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json") body: File.read!("test/fixtures/httpoison_mock/winterdienst_webfinger.json")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 500,
body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml") body: File.read!("test/fixtures/httpoison_mock/nonexistant@social.heldscal.la.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml") body: File.read!("test/fixtures/httpoison_mock/shp@social.heldscal.la.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml") body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_23211.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml") body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_user_29191.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
)
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.xml"
)
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml") body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_user_1.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml") body: File.read!("test/fixtures/httpoison_mock/spc_5381_xrd.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml") body:
File.read!(
"test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
)
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml") body:
File.read!(
"test/fixtures/httpoison_mock/http___gs.example.org_4040_index.php_user_1.xml"
)
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml") body: File.read!("test/fixtures/httpoison_mock/atarifrosch_webfinger.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml") body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain.xml")
}} }}
end end
def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _body, _headers) do def get("https://social.heldscal.la/api/statuses/user_timeline/29191.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_29191.atom.xml"
)
}} }}
end end
def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _body, _headers) do def get("https://shitposter.club/api/statuses/user_timeline/5381.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom") body: File.read!("test/fixtures/httpoison_mock/spc_5381.atom")
}} }}
end end
def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _body, _headers) do def get("https://social.heldscal.la/api/statuses/user_timeline/23211.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___social.heldscal.la_api_statuses_user_timeline_23211.atom.xml"
)
}} }}
end end
def get("https://mastodon.social/users/lambadalambda.atom", _body, _headers) do def get("https://mastodon.social/users/lambadalambda.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom") body:
File.read!(
"test/fixtures/httpoison_mock/https___mastodon.social_users_lambadalambda.atom"
)
}} }}
end end
def get("https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom", _body, _headers) do def get(
{:ok, %Response{ "https://social.stopwatchingus-heidelberg.de/api/statuses/user_timeline/18330.atom",
_body,
_headers
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml") body: File.read!("test/fixtures/httpoison_mock/atarifrosch_feed.xml")
}} }}
end end
def get("https://pleroma.soykaf.com/users/lain/feed.atom", _body, _headers) do def get("https://pleroma.soykaf.com/users/lain/feed.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___pleroma.soykaf.com_users_lain_feed.atom.xml"
)
}} }}
end end
def get("https://social.sakamoto.gq/users/eal/feed.atom", _body, _headers) do def get("https://social.sakamoto.gq/users/eal/feed.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom") body: File.read!("test/fixtures/httpoison_mock/sakamoto_eal_feed.atom")
}} }}
end end
def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _body, _headers) do def get("http://gs.example.org/index.php/api/statuses/user_timeline/1.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/http__gs.example.org_index.php_api_statuses_user_timeline_1.atom.xml"
)
}} }}
end end
def get("https://shitposter.club/notice/2827873", _body, _headers) do def get("https://shitposter.club/notice/2827873", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html") body:
File.read!("test/fixtures/httpoison_mock/https___shitposter.club_notice_2827873.html")
}} }}
end end
def get("https://shitposter.club/api/statuses/show/2827873.atom", _body, _headers) do def get("https://shitposter.club/api/statuses/show/2827873.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_show_2827873.atom.xml"
)
}} }}
end end
def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _body, _headers) do def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml") body:
File.read!(
"test/fixtures/httpoison_mock/https___shitposter.club_api_statuses_user_timeline_1.atom.xml"
)
}} }}
end end
def post("https://social.heldscal.la/main/push/hub", {:form, data}, ["Content-type": "application/x-www-form-urlencoded"]) do def post(
{:ok, %Response{ "https://social.heldscal.la/main/push/hub",
{:form, data},
"Content-type": "application/x-www-form-urlencoded"
) do
{:ok,
%Response{
status_code: 202 status_code: 202
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml") body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.xml")
}} }}
end end
def get("https://pawoo.net/users/pekorino.atom", _, _) do def get("https://pawoo.net/users/pekorino.atom", _, _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom") body: File.read!("test/fixtures/httpoison_mock/https___pawoo.net_users_pekorino.atom")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom") body: File.read!("test/fixtures/httpoison_mock/skruyb@mamot.fr.atom")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml") body: File.read!("test/fixtures/httpoison_mock/eal_sakamoto.xml")
}} }}
end 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 def get(
{:ok, %Response{ "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, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner") body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.webfigner")
}} }}
end end
def get("https://squeet.me/xrd/?uri=lain@squeet.me", [Accept: "application/xrd+xml,application/jrd+json"], [follow_redirect: true]) do def get(
{:ok, %Response{ "https://squeet.me/xrd/?uri=lain@squeet.me",
[Accept: "application/xrd+xml,application/jrd+json"],
follow_redirect: true
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml") body: File.read!("test/fixtures/httpoison_mock/lain_squeet.me_webfinger.xml")
}} }}
end end
def get("https://mamot.fr/users/Skruyb.atom", _, _) do def get("https://mamot.fr/users/Skruyb.atom", _, _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom") body: File.read!("test/fixtures/httpoison_mock/https___mamot.fr_users_Skruyb.atom")
}} }}
end end
def get("https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056", [Accept: "application/atom+xml"], _) do def get(
{:ok, %Response{ "https://social.sakamoto.gq/objects/0ccc1a2c-66b0-4305-b23a-7f7f2b040056",
[Accept: "application/atom+xml"],
_
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom") body: File.read!("test/fixtures/httpoison_mock/sakamoto.atom")
}} }}
end end
def get("https://pleroma.soykaf.com/users/shp/feed.atom", _, _) do def get("https://pleroma.soykaf.com/users/shp/feed.atom", _, _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed") body: File.read!("test/fixtures/httpoison_mock/shp@pleroma.soykaf.com.feed")
}} }}
end end
def get("http://social.heldscal.la/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://social.heldscal.la/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta") body: File.read!("test/fixtures/httpoison_mock/social.heldscal.la_host_meta")
}} }}
end end
def get("http://macgirvin.com/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://macgirvin.com/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta") body: File.read!("test/fixtures/httpoison_mock/macgirvin.com_host_meta")
}} }}
end end
def get("http://mastodon.social/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://mastodon.social/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta") body: File.read!("test/fixtures/httpoison_mock/mastodon.social_host_meta")
}} }}
end end
def get("http://shitposter.club/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://shitposter.club/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta") body: File.read!("test/fixtures/httpoison_mock/shitposter.club_host_meta")
}} }}
end end
def get("http://pleroma.soykaf.com/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://pleroma.soykaf.com/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta") body: File.read!("test/fixtures/httpoison_mock/pleroma.soykaf.com_host_meta")
}} }}
end end
def get("http://social.sakamoto.gq/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://social.sakamoto.gq/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta") body: File.read!("test/fixtures/httpoison_mock/social.sakamoto.gq_host_meta")
}} }}
end end
def get("http://gs.example.org/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://gs.example.org/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta") body: File.read!("test/fixtures/httpoison_mock/gs.example.org_host_meta")
}} }}
end end
def get("http://pawoo.net/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://pawoo.net/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/pawoo.net_host_meta") body: File.read!("test/fixtures/httpoison_mock/pawoo.net_host_meta")
}} }}
end end
def get("http://mamot.fr/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://mamot.fr/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/mamot.fr_host_meta") body: File.read!("test/fixtures/httpoison_mock/mamot.fr_host_meta")
}} }}
end end
def get("http://mastodon.xyz/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://mastodon.xyz/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/mastodon.xyz_host_meta") body: File.read!("test/fixtures/httpoison_mock/mastodon.xyz_host_meta")
}} }}
end end
def get("http://social.wxcafe.net/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://social.wxcafe.net/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/social.wxcafe.net_host_meta") body: File.read!("test/fixtures/httpoison_mock/social.wxcafe.net_host_meta")
}} }}
end end
def get("http://squeet.me/.well-known/host-meta", [], [follow_redirect: true]) do def get("http://squeet.me/.well-known/host-meta", [], follow_redirect: true) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta") body: File.read!("test/fixtures/httpoison_mock/squeet.me_host_meta")
}} }}
end end
def get("http://social.stopwatchingus-heidelberg.de/.well-known/host-meta", [], [follow_redirect: true]) do def get(
{:ok, %Response{ "http://social.stopwatchingus-heidelberg.de/.well-known/host-meta",
[],
follow_redirect: true
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta") body:
File.read!("test/fixtures/httpoison_mock/social.stopwatchingus-heidelberg.de_host_meta")
}} }}
end end
def get("http://mastodon.example.org/users/admin", ["Accept": "application/activity+json"], _) do def get("http://mastodon.example.org/users/admin", [Accept: "application/activity+json"], _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json") body: File.read!("test/fixtures/httpoison_mock/admin@mastdon.example.org.json")
}} }}
end end
def get("https://masto.quad.moe/users/_HellPie", ["Accept": "application/activity+json"], _) do def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/hellpie.json") body: File.read!("test/fixtures/httpoison_mock/hellpie.json")
}} }}
end end
def get("https://niu.moe/users/rye", ["Accept": "application/activity+json"], _) do def get("https://niu.moe/users/rye", [Accept: "application/activity+json"], _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/rye.json") body: File.read!("test/fixtures/httpoison_mock/rye.json")
}} }}
end end
def get("https://mst3k.interlinked.me/users/luciferMysticus", ["Accept": "application/activity+json"], _) do def get(
{:ok, %Response{ "https://mst3k.interlinked.me/users/luciferMysticus",
[Accept: "application/activity+json"],
_
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json") body: File.read!("test/fixtures/httpoison_mock/lucifermysticus.json")
}} }}
end end
def get("https://mstdn.io/users/mayuutann", ["Accept": "application/activity+json"], _) do def get("https://mstdn.io/users/mayuutann", [Accept: "application/activity+json"], _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/mayumayu.json") body: File.read!("test/fixtures/httpoison_mock/mayumayu.json")
}} }}
end end
def get("http://mastodon.example.org/@admin/99541947525187367", ["Accept": "application/activity+json"], _) do def get(
{:ok, %Response{ "http://mastodon.example.org/@admin/99541947525187367",
[Accept: "application/activity+json"],
_
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/mastodon-note-object.json") body: File.read!("test/fixtures/mastodon-note-object.json")
}} }}
end end
def get("https://mstdn.io/users/mayuutann/statuses/99568293732299394", ["Accept": "application/activity+json"], _) do def get(
{:ok, %Response{ "https://mstdn.io/users/mayuutann/statuses/99568293732299394",
[Accept: "application/activity+json"],
_
) do
{:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json") body: File.read!("test/fixtures/httpoison_mock/mayumayupost.json")
}} }}
end end
def get("https://shitposter.club/notice/7369654", _, _) do def get("https://shitposter.club/notice/7369654", _, _) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/7369654.html") body: File.read!("test/fixtures/httpoison_mock/7369654.html")
}} }}
end end
def get("https://shitposter.club/api/statuses/show/7369654.atom", _body, _headers) do def get("https://shitposter.club/api/statuses/show/7369654.atom", _body, _headers) do
{:ok, %Response{ {:ok,
%Response{
status_code: 200, status_code: 200,
body: File.read!("test/fixtures/httpoison_mock/7369654.atom") body: File.read!("test/fixtures/httpoison_mock/7369654.atom")
}} }}
end end
def get(url, body, headers) do 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 end
def post(url, body, headers) do def post(url, body, headers) do

View file

@ -1,5 +1,6 @@
defmodule Pleroma.Web.OStatusMock do defmodule Pleroma.Web.OStatusMock do
import Pleroma.Factory import Pleroma.Factory
def handle_incoming(_doc) do def handle_incoming(_doc) do
insert(:note_activity) insert(:note_activity)
end end

View file

@ -2,4 +2,3 @@ ExUnit.start()
Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual) Ecto.Adapters.SQL.Sandbox.mode(Pleroma.Repo, :manual)
{:ok, _} = Application.ensure_all_started(:ex_machina) {:ok, _} = Application.ensure_all_started(:ex_machina)

View file

@ -4,20 +4,37 @@ defmodule Pleroma.UploadTest do
describe "Storing a file" do describe "Storing a file" do
test "copies the file to the configured folder" 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) data = Upload.store(file)
assert data["name"] == "an [image.jpg" 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 end
test "fixes an incorrect content type" do 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) data = Upload.store(file)
assert hd(data["url"])["mediaType"] == "image/jpeg" assert hd(data["url"])["mediaType"] == "image/jpeg"
end end
test "does not modify a valid content type" do 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) data = Upload.store(file)
assert hd(data["url"])["mediaType"] == "image/png" assert hd(data["url"])["mediaType"] == "image/png"
end end

View file

@ -10,15 +10,15 @@ defmodule Pleroma.UserTest do
import Ecto.Query import Ecto.Query
test "ap_id returns the activity pub id for the user" do 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) assert expected_ap_id == User.ap_id(user)
end end
test "ap_followers returns the followers collection for the user" do 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" expected_followers_collection = "#{User.ap_id(user)}/followers"
@ -83,7 +83,6 @@ defmodule Pleroma.UserTest do
assert user.following == [user.ap_id] assert user.following == [user.ap_id]
end end
test "test if a user is following another user" do test "test if a user is following another user" do
followed = insert(:user) followed = insert(:user)
user = insert(:user, %{following: [User.ap_followers(followed)]}) user = insert(:user, %{following: [User.ap_followers(followed)]})
@ -104,12 +103,12 @@ defmodule Pleroma.UserTest do
test "it requires an email, name, nickname and password, bio is optional" do test "it requires an email, name, nickname and password, bio is optional" do
@full_user_data @full_user_data
|> Map.keys |> Map.keys()
|> Enum.each(fn (key) -> |> Enum.each(fn key ->
params = Map.delete(@full_user_data, key) params = Map.delete(@full_user_data, key)
changeset = User.register_changeset(%User{}, params) 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)
end end
@ -120,7 +119,11 @@ defmodule Pleroma.UserTest do
assert is_binary(changeset.changes[:password_hash]) assert is_binary(changeset.changes[:password_hash])
assert changeset.changes[:ap_id] == User.ap_id(%User{nickname: @full_user_data.nickname}) 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" assert changeset.changes.follower_address == "#{changeset.changes.ap_id}/followers"
end end
end end
@ -158,12 +161,24 @@ defmodule Pleroma.UserTest do
test "returns an ap_id for a user" do test "returns an ap_id for a user" do
user = insert(:user) 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 end
test "returns an ap_followers link for a user" do test "returns an ap_followers link for a user" do
user = insert(:user) 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 end
describe "remote user creation changeset" do describe "remote user creation changeset" do
@ -184,7 +199,8 @@ defmodule Pleroma.UserTest do
test "it sets the follower_adress" do test "it sets the follower_adress" do
cs = User.remote_user_creation(@valid_remote) cs = User.remote_user_creation(@valid_remote)
# remote users get a fake local follower address # 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 end
test "it enforces the fqn format for nicknames" do test "it enforces the fqn format for nicknames" do
@ -196,7 +212,7 @@ defmodule Pleroma.UserTest do
test "it has required fields" do test "it has required fields" do
[:name, :nickname, :ap_id] [:name, :nickname, :ap_id]
|> Enum.each(fn (field) -> |> Enum.each(fn field ->
cs = User.remote_user_creation(Map.delete(@valid_remote, field)) cs = User.remote_user_creation(Map.delete(@valid_remote, field))
refute cs.valid? refute cs.valid?
end) end)
@ -204,7 +220,7 @@ defmodule Pleroma.UserTest do
test "it restricts some sizes" do test "it restricts some sizes" do
[bio: 5000, name: 100] [bio: 5000, name: 100]
|> Enum.each(fn ({field, size}) -> |> Enum.each(fn {field, size} ->
string = String.pad_leading(".", size) string = String.pad_leading(".", size)
cs = User.remote_user_creation(Map.put(@valid_remote, field, string)) cs = User.remote_user_creation(Map.put(@valid_remote, field, string))
assert cs.valid? assert cs.valid?
@ -323,7 +339,11 @@ defmodule Pleroma.UserTest do
user_two = insert(:user, local: false) user_two = insert(:user, local: false)
addressed = insert(:user, local: true) addressed = insert(:user, local: true)
addressed_remote = insert(:user, local: false) 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) assert [addressed] == User.get_recipients_from_activity(activity)

View file

@ -9,7 +9,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
test "it returns a json representation of the user", %{conn: conn} do test "it returns a json representation of the user", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/users/#{user.nickname}") |> get("/users/#{user.nickname}")
@ -22,9 +23,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
describe "/object/:uuid" do describe "/object/:uuid" do
test "it returns a json representation of the object", %{conn: conn} do test "it returns a json representation of the object", %{conn: conn} do
note = insert(:note) note = insert(:note)
uuid = String.split(note.data["id"], "/") |> List.last uuid = String.split(note.data["id"], "/") |> List.last()
conn = conn conn =
conn
|> put_req_header("accept", "application/activity+json") |> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}") |> get("/objects/#{uuid}")
@ -34,9 +36,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubControllerTest do
describe "/users/:nickname/inbox" do describe "/users/:nickname/inbox" do
test "it inserts an incoming activity into the database", %{conn: conn} 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 conn =
conn
|> assign(:valid_signature, true) |> assign(:valid_signature, true)
|> put_req_header("content-type", "application/activity+json") |> put_req_header("content-type", "application/activity+json")
|> post("/inbox", data) |> post("/inbox", data)

View file

@ -37,6 +37,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert is_binary(activity.data["id"]) assert is_binary(activity.data["id"])
given_id = "bla" given_id = "bla"
data = %{ data = %{
"ok" => true, "ok" => true,
"id" => given_id "id" => given_id
@ -63,7 +64,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
describe "create activities" do describe "create activities" do
test "removes doubled 'to' recipients" 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.data["to"] == ["user1", "user2"]
assert activity.actor == "1" assert activity.actor == "1"
assert activity.recipients == ["user1", "user2"] assert activity.recipients == ["user1", "user2"]
@ -124,11 +132,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
describe "public fetch activities" do describe "public fetch activities" do
test "retrieves public 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 length(activities) == 1
assert Enum.at(activities, 0) == public assert Enum.at(activities, 0) == public
end end
@ -137,7 +145,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
activities = ActivityBuilder.insert_list(30) activities = ActivityBuilder.insert_list(30)
last_expected = List.last(activities) last_expected = List.last(activities)
activities = ActivityPub.fetch_public_activities activities = ActivityPub.fetch_public_activities()
last = List.last(activities) last = List.last(activities)
assert length(activities) == 20 assert length(activities) == 20
@ -232,7 +240,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
{:ok, announce_activity, object} = ActivityPub.announce(user, object) {:ok, announce_activity, object} = ActivityPub.announce(user, object)
assert object.data["announcement_count"] == 1 assert object.data["announcement_count"] == 1
assert object.data["announcements"] == [user.ap_id] 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["object"] == object.data["id"]
assert announce_activity.data["actor"] == user.ap_id assert announce_activity.data["actor"] == user.ap_id
assert announce_activity.data["context"] == object.data["context"] assert announce_activity.data["context"] == object.data["context"]
@ -241,7 +254,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
describe "uploading files" do describe "uploading files" do
test "copies the file to the configured folder" 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) {:ok, %Object{} = object} = ActivityPub.upload(file)
assert object.data["name"] == "an_image.jpg" assert object.data["name"] == "an_image.jpg"
@ -268,11 +285,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
describe "fetching an object" do describe "fetching an object" do
test "it fetches 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 = Activity.get_create_activity_by_object_ap_id(object.data["id"])
assert activity.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 [attachment] = object.data["attachment"]
assert is_list(attachment["url"]) assert is_list(attachment["url"])
@ -285,7 +305,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"]) assert activity = Activity.get_create_activity_by_object_ap_id(object.data["id"])
assert activity.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 assert object == object_again
end end
@ -344,7 +365,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
user = insert(:user) user = insert(:user)
{:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user) {:ok, user} = Pleroma.Web.WebFinger.ensure_keys_present(user)
user_data = Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: 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["actor"] == user.ap_id
assert update.data["to"] == [user.follower_address] assert update.data["to"] == [user.follower_address]

View file

@ -16,8 +16,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
test "it ignores an incoming notice if we already have it" do test "it ignores an incoming notice if we already have it" do
activity = insert(:note_activity) activity = insert(:note_activity)
data = File.read!("test/fixtures/mastodon-post-activity.json") data =
|> Poison.decode! File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"]) |> Map.put("object", activity.data["object"])
{:ok, returned_activity} = Transmogrifier.handle_incoming(data) {:ok, returned_activity} = Transmogrifier.handle_incoming(data)
@ -26,51 +27,72 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end end
test "it fetches replied-to activities if we don't have them" do test "it fetches replied-to activities if we don't have them" do
data = File.read!("test/fixtures/mastodon-post-activity.json") data =
|> Poison.decode! File.read!("test/fixtures/mastodon-post-activity.json")
|> Poison.decode!()
object = data["object"] object =
data["object"]
|> Map.put("inReplyTo", "https://shitposter.club/notice/2827873") |> Map.put("inReplyTo", "https://shitposter.club/notice/2827873")
data = data data =
data
|> Map.put("object", object) |> Map.put("object", object)
{:ok, returned_activity} = Transmogrifier.handle_incoming(data) {: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 activity =
assert returned_activity.data["object"]["inReplyToAtomUri"] == "https://shitposter.club/notice/2827873" 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 assert returned_activity.data["object"]["inReplyToStatusId"] == activity.id
end end
test "it works for incoming notices" do 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) {: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["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert data["cc"] == [ assert data["cc"] == [
"http://mastodon.example.org/users/admin/followers", "http://mastodon.example.org/users/admin/followers",
"http://localtesting.pleroma.lol/users/lain" "http://localtesting.pleroma.lol/users/lain"
] ]
assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["actor"] == "http://mastodon.example.org/users/admin"
object = data["object"] object = data["object"]
assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822" assert object["id"] == "http://mastodon.example.org/users/admin/statuses/99512778738411822"
assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"] assert object["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
assert object["cc"] == [ assert object["cc"] == [
"http://mastodon.example.org/users/admin/followers", "http://mastodon.example.org/users/admin/followers",
"http://localtesting.pleroma.lol/users/lain" "http://localtesting.pleroma.lol/users/lain"
] ]
assert object["actor"] == "http://mastodon.example.org/users/admin" assert object["actor"] == "http://mastodon.example.org/users/admin"
assert object["attributedTo"] == "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 assert object["sensitive"] == true
end end
test "it works for incoming notices with hashtags" do 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) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert Enum.at(data["object"]["tag"], 2) == "moo" assert Enum.at(data["object"]["tag"], 2) == "moo"
@ -78,7 +100,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
test "it works for incoming follow requests" do test "it works for incoming follow requests" do
user = insert(:user) user = insert(:user)
data = File.read!("test/fixtures/mastodon-follow-activity.json") |> Poison.decode!
data =
File.read!("test/fixtures/mastodon-follow-activity.json") |> Poison.decode!()
|> Map.put("object", user.ap_id) |> Map.put("object", user.ap_id)
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -93,7 +117,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
user = insert(:user) user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
data = File.read!("test/fixtures/mastodon-like.json") |> Poison.decode! data =
File.read!("test/fixtures/mastodon-like.json") |> Poison.decode!()
|> Map.put("object", activity.data["object"]["id"]) |> Map.put("object", activity.data["object"]["id"])
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
@ -105,14 +130,18 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
end end
test "it works for incoming announces" do 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) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce" 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"]) assert Activity.get_create_activity_by_object_ap_id(data["object"])
end end
@ -121,30 +150,37 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
user = insert(:user) user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
data = File.read!("test/fixtures/mastodon-announce.json") data =
|> Poison.decode! File.read!("test/fixtures/mastodon-announce.json")
|> Poison.decode!()
|> Map.put("object", activity.data["object"]["id"]) |> Map.put("object", activity.data["object"]["id"])
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
assert data["actor"] == "http://mastodon.example.org/users/admin" assert data["actor"] == "http://mastodon.example.org/users/admin"
assert data["type"] == "Announce" 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 data["object"] == activity.data["object"]["id"]
assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id assert Activity.get_create_activity_by_object_ap_id(data["object"]).id == activity.id
end end
test "it works for incoming update activities" do 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) {:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode! update_data = File.read!("test/fixtures/mastodon-update.json") |> Poison.decode!()
object = update_data["object"]
object =
update_data["object"]
|> Map.put("actor", data["actor"]) |> Map.put("actor", data["actor"])
|> Map.put("id", data["actor"]) |> Map.put("id", data["actor"])
update_data = update_data update_data =
update_data
|> Map.put("actor", data["actor"]) |> Map.put("actor", data["actor"])
|> Map.put("object", object) |> Map.put("object", object)
@ -152,20 +188,37 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
user = User.get_cached_by_ap_id(data["actor"]) user = User.get_cached_by_ap_id(data["actor"])
assert user.name == "gargle" 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 == "<p>Some bio</p>" assert user.bio == "<p>Some bio</p>"
end end
test "it works for incoming deletes" do test "it works for incoming deletes" do
activity = insert(:note_activity) activity = insert(:note_activity)
data = File.read!("test/fixtures/mastodon-delete.json")
|> Poison.decode!
object = data["object"] data =
File.read!("test/fixtures/mastodon-delete.json")
|> Poison.decode!()
object =
data["object"]
|> Map.put("id", activity.data["object"]["id"]) |> Map.put("id", activity.data["object"]["id"])
data = data data =
data
|> Map.put("object", object) |> Map.put("object", object)
|> Map.put("actor", activity.data["actor"]) |> Map.put("actor", activity.data["actor"])
@ -180,7 +233,8 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
user = insert(:user) user = insert(:user)
other_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) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
object = modified["object"] object = modified["object"]
@ -192,7 +246,7 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
} }
expected_tag = %{ expected_tag = %{
"href" => Pleroma.Web.Endpoint.url <> "/tags/2hu", "href" => Pleroma.Web.Endpoint.url() <> "/tags/2hu",
"type" => "Hashtag", "type" => "Hashtag",
"name" => "#2hu" "name" => "#2hu"
} }
@ -247,7 +301,9 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
user = insert(:user) 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) {:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29" assert modified["object"]["inReplyTo"] == "http://gs.example.org:4040/index.php/notice/29"
@ -256,7 +312,14 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
describe "user upgrade" do describe "user upgrade" do
test "it upgrades a user to activitypub" 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]}) user_two = insert(:user, %{following: [user.follower_address]})
{:ok, activity} = CommonAPI.post(user, %{"status" => "test"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "test"})
@ -279,8 +342,25 @@ defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
activity = Repo.get(Activity, activity.id) activity = Repo.get(Activity, activity.id)
assert user.follower_address in activity.recipients 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 refute "..." in activity.recipients
unrelated_activity = Repo.get(Activity, unrelated_activity.id) unrelated_activity = Repo.get(Activity, unrelated_activity.id)

View file

@ -3,7 +3,8 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
use Pleroma.DataCase use Pleroma.DataCase
test "it adds attachment links to a given text and attachment set" do 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 = %{ attachment = %{
"url" => [%{"href" => name}] "url" => [%{"href" => name}]
@ -11,6 +12,7 @@ defmodule Pleroma.Web.CommonAPI.UtilsTest do
res = Utils.add_attachments("", [attachment]) res = Utils.add_attachments("", [attachment])
assert res == "<br><a href=\"#{name}\" class='attachment'>Sakura Mana Turned on by a Se…</a>" assert res ==
"<br><a href=\"#{name}\" class='attachment'>Sakura Mana Turned on by a Se…</a>"
end end
end end

View file

@ -5,7 +5,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountViewTest do
alias Pleroma.User alias Pleroma.User
test "Represent a user account" do 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 = %{ expected = %{
id: to_string(user.id), id: to_string(user.id),

View file

@ -14,7 +14,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"}) {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/timelines/home") |> get("/api/v1/timelines/home")
@ -22,7 +23,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, user} = User.follow(user, following) {:ok, user} = User.follow(user, following)
conn = build_conn() conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/timelines/home") |> get("/api/v1/timelines/home")
@ -32,42 +34,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "the public timeline", %{conn: conn} do test "the public timeline", %{conn: conn} do
following = insert(:user) following = insert(:user)
capture_log fn -> capture_log(fn ->
{:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"}) {:ok, _activity} = TwitterAPI.create_status(following, %{"status" => "test"})
{:ok, [_activity]} = OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
conn = conn {:ok, [_activity]} =
OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
conn =
conn
|> get("/api/v1/timelines/public", %{"local" => "False"}) |> get("/api/v1/timelines/public", %{"local" => "False"})
assert length(json_response(conn, 200)) == 2 assert length(json_response(conn, 200)) == 2
conn = build_conn() conn =
build_conn()
|> get("/api/v1/timelines/public", %{"local" => "True"}) |> get("/api/v1/timelines/public", %{"local" => "True"})
assert [%{"content" => "test"}] = json_response(conn, 200) assert [%{"content" => "test"}] = json_response(conn, 200)
conn = build_conn() conn =
build_conn()
|> get("/api/v1/timelines/public", %{"local" => "1"}) |> get("/api/v1/timelines/public", %{"local" => "1"})
assert [%{"content" => "test"}] = json_response(conn, 200) assert [%{"content" => "test"}] = json_response(conn, 200)
end end)
end end
test "posting a status", %{conn: conn} do test "posting a status", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses", %{"status" => "cofe", "spoiler_text" => "2hu", "sensitive" => "false"}) |> 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) assert Repo.get(Activity, id)
end end
test "posting a sensitive status", %{conn: conn} do test "posting a sensitive status", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true}) |> post("/api/v1/statuses", %{"status" => "cofe", "sensitive" => true})
@ -80,7 +95,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"}) {:ok, replied_to} = TwitterAPI.create_status(user, %{"status" => "cofe"})
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id}) |> post("/api/v1/statuses", %{"status" => "xD", "in_reply_to_id" => replied_to.id})
@ -95,7 +111,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "verify_credentials", %{conn: conn} do test "verify_credentials", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/accounts/verify_credentials") |> get("/api/v1/accounts/verify_credentials")
@ -106,7 +123,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "get a status", %{conn: conn} do test "get a status", %{conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)
conn = conn conn =
conn
|> get("/api/v1/statuses/#{activity.id}") |> get("/api/v1/statuses/#{activity.id}")
assert %{"id" => id} = json_response(conn, 200) assert %{"id" => id} = json_response(conn, 200)
@ -118,7 +136,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
activity = insert(:note_activity) activity = insert(:note_activity)
author = User.get_by_ap_id(activity.data["actor"]) author = User.get_by_ap_id(activity.data["actor"])
conn = conn conn =
conn
|> assign(:user, author) |> assign(:user, author)
|> delete("/api/v1/statuses/#{activity.id}") |> delete("/api/v1/statuses/#{activity.id}")
@ -131,7 +150,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
activity = insert(:note_activity) activity = insert(:note_activity)
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> delete("/api/v1/statuses/#{activity.id}") |> delete("/api/v1/statuses/#{activity.id}")
@ -146,14 +166,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_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) {:ok, [_notification]} = Notification.create_notifications(activity)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/notifications") |> get("/api/v1/notifications")
expected_response = "hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>" expected_response =
"hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
assert response == expected_response assert response == expected_response
end end
@ -162,14 +187,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_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) {:ok, [notification]} = Notification.create_notifications(activity)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/notifications/#{notification.id}") |> get("/api/v1/notifications/#{notification.id}")
expected_response = "hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>" expected_response =
"hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
assert %{"status" => %{"content" => response}} = json_response(conn, 200) assert %{"status" => %{"content" => response}} = json_response(conn, 200)
assert response == expected_response assert response == expected_response
end end
@ -178,10 +208,13 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_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) {:ok, [notification]} = Notification.create_notifications(activity)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/notifications/dismiss", %{"id" => notification.id}) |> post("/api/v1/notifications/dismiss", %{"id" => notification.id})
@ -192,16 +225,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_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) {:ok, [_notification]} = Notification.create_notifications(activity)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/notifications/clear") |> post("/api/v1/notifications/clear")
assert %{} = json_response(conn, 200) assert %{} = json_response(conn, 200)
conn = build_conn() conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/notifications") |> get("/api/v1/notifications")
@ -215,11 +252,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
activity = insert(:note_activity) activity = insert(:note_activity)
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/reblog") |> 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 assert to_string(activity.id) == id
end end
end end
@ -229,11 +269,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
activity = insert(:note_activity) activity = insert(:note_activity)
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/favourite") |> 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 assert to_string(activity.id) == id
end end
end end
@ -245,11 +288,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, _, _} = CommonAPI.favorite(activity.id, user) {:ok, _, _} = CommonAPI.favorite(activity.id, user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses/#{activity.id}/unfavourite") |> 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 assert to_string(activity.id) == id
end end
end end
@ -261,7 +307,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = User.get_by_ap_id(note_two.data["actor"]) user = User.get_by_ap_id(note_two.data["actor"])
conn = conn conn =
conn
|> get("/api/v1/accounts/#{user.id}/statuses") |> get("/api/v1/accounts/#{user.id}/statuses")
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
@ -273,19 +320,28 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
note = insert(:note_activity) note = insert(:note_activity)
user = User.get_by_ap_id(note.data["actor"]) 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"} file = %Plug.Upload{
media = TwitterAPI.upload(file, "json") content_type: "image/jpg",
|> Poison.decode! 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 {: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"}) |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "true"})
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
assert id == to_string(image_post.id) assert id == to_string(image_post.id)
conn = build_conn() conn =
build_conn()
|> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"}) |> get("/api/v1/accounts/#{user.id}/statuses", %{"only_media" => "1"})
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
@ -299,7 +355,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, user} = User.follow(user, other_user) {:ok, user} = User.follow(user, other_user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]}) |> get("/api/v1/accounts/relationships", %{"id" => [other_user.id]})
@ -312,24 +369,31 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "account fetching", %{conn: conn} do test "account fetching", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> get("/api/v1/accounts/#{user.id}") |> get("/api/v1/accounts/#{user.id}")
assert %{"id" => id} = json_response(conn, 200) assert %{"id" => id} = json_response(conn, 200)
assert id == to_string(user.id) assert id == to_string(user.id)
conn = build_conn() conn =
build_conn()
|> get("/api/v1/accounts/-1") |> get("/api/v1/accounts/-1")
assert %{"error" => "Can't find user"} = json_response(conn, 404) assert %{"error" => "Can't find user"} = json_response(conn, 404)
end end
test "media upload", %{conn: conn} do 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) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/media", %{"file" => file}) |> post("/api/v1/media", %{"file" => file})
@ -341,16 +405,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "hashtag timeline", %{conn: conn} do test "hashtag timeline", %{conn: conn} do
following = insert(:user) following = insert(:user)
capture_log fn -> capture_log(fn ->
{:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"}) {:ok, activity} = TwitterAPI.create_status(following, %{"status" => "test #2hu"})
{:ok, [_activity]} = OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
conn = conn {:ok, [_activity]} =
OStatus.fetch_activity_from_url("https://shitposter.club/notice/2827873")
conn =
conn
|> get("/api/v1/timelines/tag/2hu") |> get("/api/v1/timelines/tag/2hu")
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
assert id == to_string(activity.id) assert id == to_string(activity.id)
end end)
end end
test "getting followers", %{conn: conn} do test "getting followers", %{conn: conn} do
@ -358,7 +426,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, user} = User.follow(user, other_user) {:ok, user} = User.follow(user, other_user)
conn = conn conn =
conn
|> get("/api/v1/accounts/#{other_user.id}/followers") |> get("/api/v1/accounts/#{other_user.id}/followers")
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
@ -370,7 +439,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
other_user = insert(:user) other_user = insert(:user)
{:ok, user} = User.follow(user, other_user) {:ok, user} = User.follow(user, other_user)
conn = conn conn =
conn
|> get("/api/v1/accounts/#{user.id}/following") |> get("/api/v1/accounts/#{user.id}/following")
assert [%{"id" => id}] = json_response(conn, 200) assert [%{"id" => id}] = json_response(conn, 200)
@ -381,21 +451,26 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/accounts/#{other_user.id}/follow") |> post("/api/v1/accounts/#{other_user.id}/follow")
assert %{"id" => _id, "following" => true} = json_response(conn, 200) assert %{"id" => _id, "following" => true} = json_response(conn, 200)
user = Repo.get(User, user.id) user = Repo.get(User, user.id)
conn = build_conn()
conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/accounts/#{other_user.id}/unfollow") |> post("/api/v1/accounts/#{other_user.id}/unfollow")
assert %{"id" => _id, "following" => false} = json_response(conn, 200) assert %{"id" => _id, "following" => false} = json_response(conn, 200)
user = Repo.get(User, user.id) user = Repo.get(User, user.id)
conn = build_conn()
conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/follows", %{"uri" => other_user.nickname}) |> post("/api/v1/follows", %{"uri" => other_user.nickname})
@ -407,14 +482,17 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
other_user = insert(:user) other_user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/accounts/#{other_user.id}/block") |> post("/api/v1/accounts/#{other_user.id}/block")
assert %{"id" => _id, "blocking" => true} = json_response(conn, 200) assert %{"id" => _id, "blocking" => true} = json_response(conn, 200)
user = Repo.get(User, user.id) user = Repo.get(User, user.id)
conn = build_conn()
conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/accounts/#{other_user.id}/unblock") |> post("/api/v1/accounts/#{other_user.id}/unblock")
@ -427,7 +505,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, user} = User.block(user, other_user) {:ok, user} = User.block(user, other_user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/blocks") |> get("/api/v1/blocks")
@ -440,8 +519,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
other_user = insert(:user) other_user = insert(:user)
["mute", "unmute"] ["mute", "unmute"]
|> Enum.each(fn(endpoint) -> |> Enum.each(fn endpoint ->
conn = build_conn() conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/accounts/#{other_user.id}/#{endpoint}") |> post("/api/v1/accounts/#{other_user.id}/#{endpoint}")
@ -454,8 +534,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
user = insert(:user) user = insert(:user)
["blocks", "domain_blocks", "mutes", "follow_requests"] ["blocks", "domain_blocks", "mutes", "follow_requests"]
|> Enum.each(fn(endpoint) -> |> Enum.each(fn endpoint ->
conn = build_conn() conn =
build_conn()
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/#{endpoint}") |> get("/api/v1/#{endpoint}")
@ -468,7 +549,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
_user_two = insert(:user, %{nickname: "shp@shitposter.club"}) _user_two = insert(:user, %{nickname: "shp@shitposter.club"})
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"}) user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/accounts/search", %{"q" => "2hu"}) |> get("/api/v1/accounts/search", %{"q" => "2hu"})
@ -484,7 +566,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"}) {:ok, activity} = CommonAPI.post(user, %{"status" => "This is about 2hu"})
{:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"}) {:ok, _} = CommonAPI.post(user_two, %{"status" => "This isn't"})
conn = conn conn =
conn
|> get("/api/v1/search", %{"q" => "2hu"}) |> get("/api/v1/search", %{"q" => "2hu"})
assert results = json_response(conn, 200) assert results = json_response(conn, 200)
@ -499,18 +582,21 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
end end
test "search fetches remote statuses", %{conn: conn} do test "search fetches remote statuses", %{conn: conn} do
capture_log fn -> capture_log(fn ->
conn = conn conn =
conn
|> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"}) |> get("/api/v1/search", %{"q" => "https://shitposter.club/notice/2827873"})
assert results = json_response(conn, 200) assert results = json_response(conn, 200)
[status] = results["statuses"] [status] = results["statuses"]
assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment" assert status["uri"] == "tag:shitposter.club,2017-05-05:noticeId=2827873:objectType=comment"
end end)
end end
test "search fetches remote accounts", %{conn: conn} do test "search fetches remote accounts", %{conn: conn} do
conn = conn conn =
conn
|> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"}) |> get("/api/v1/search", %{"q" => "shp@social.heldscal.la", "resolve" => "true"})
assert results = json_response(conn, 200) assert results = json_response(conn, 200)
@ -527,7 +613,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
{:ok, _, _} = CommonAPI.favorite(activity.id, user) {:ok, _, _} = CommonAPI.favorite(activity.id, user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/favourites") |> get("/api/v1/favourites")
@ -539,7 +626,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "updates the user's bio", %{conn: conn} do test "updates the user's bio", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{"note" => "I drink #cofe"}) |> patch("/api/v1/accounts/update_credentials", %{"note" => "I drink #cofe"})
@ -550,7 +638,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "updates the user's name", %{conn: conn} do test "updates the user's name", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = conn conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"}) |> patch("/api/v1/accounts/update_credentials", %{"display_name" => "markorepairs"})
@ -561,9 +650,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "updates the user's avatar", %{conn: conn} do test "updates the user's avatar", %{conn: conn} do
user = insert(:user) 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 conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar}) |> patch("/api/v1/accounts/update_credentials", %{"avatar" => new_avatar})
@ -574,9 +668,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "updates the user's banner", %{conn: conn} do test "updates the user's banner", %{conn: conn} do
user = insert(:user) 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 conn =
conn
|> assign(:user, user) |> assign(:user, user)
|> patch("/api/v1/accounts/update_credentials", %{"header" => new_header}) |> patch("/api/v1/accounts/update_credentials", %{"header" => new_header})
@ -594,7 +693,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
Pleroma.Stats.update_stats() Pleroma.Stats.update_stats()
conn = conn conn =
conn
|> get("/api/v1/instance") |> get("/api/v1/instance")
assert result = json_response(conn, 200) assert result = json_response(conn, 200)

View file

@ -13,7 +13,8 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
status = StatusView.render("status.json", %{activity: note}) status = StatusView.render("status.json", %{activity: note})
created_at = (note.data["object"]["published"] || "") created_at =
(note.data["object"]["published"] || "")
|> String.replace(~r/\.\d+Z/, ".000Z") |> String.replace(~r/\.\d+Z/, ".000Z")
expected = %{ expected = %{
@ -57,7 +58,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
test "a reply" do test "a reply" do
note = insert(:note_activity) note = insert(:note_activity)
user = insert(:user) 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}) status = StatusView.render("status.json", %{activity: activity})

View file

@ -4,7 +4,15 @@ defmodule Pleroma.Web.OAuth.AuthorizationTest do
import Pleroma.Factory import Pleroma.Factory
test "create an authorization token for a valid app" do 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) user = insert(:user)
{:ok, auth} = Authorization.create_authorization(app, user) {:ok, auth} = Authorization.create_authorization(app, user)
@ -16,7 +24,15 @@ defmodule Pleroma.Web.OAuth.AuthorizationTest do
end end
test "use up a token" do 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) user = insert(:user)
{:ok, auth} = Authorization.create_authorization(app, user) {:ok, auth} = Authorization.create_authorization(app, user)
@ -30,7 +46,7 @@ defmodule Pleroma.Web.OAuth.AuthorizationTest do
expired_auth = %Authorization{ expired_auth = %Authorization{
user_id: user.id, user_id: user.id,
app_id: app.id, app_id: app.id,
valid_until: NaiveDateTime.add(NaiveDateTime.utc_now, -10), valid_until: NaiveDateTime.add(NaiveDateTime.utc_now(), -10),
token: "mytoken", token: "mytoken",
used: false used: false
} }

View file

@ -6,7 +6,15 @@ defmodule Pleroma.Web.OAuth.TokenTest do
import Pleroma.Factory import Pleroma.Factory
test "exchanges a auth token for an access token" do 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) user = insert(:user)
{:ok, auth} = Authorization.create_authorization(app, user) {:ok, auth} = Authorization.create_authorization(app, user)

View file

@ -16,9 +16,12 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
tuple = ActivityRepresenter.to_simple_form(activity, user) 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{<link type="text/html" href="https://mastodon.social/users/lambadalambda/updates/2314748" rel="alternate"/>}) assert String.contains?(
res,
~s{<link type="text/html" href="https://mastodon.social/users/lambadalambda/updates/2314748" rel="alternate"/>}
)
end end
test "a note activity" do test "a note activity" do
@ -46,7 +49,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
tuple = ActivityRepresenter.to_simple_form(note_activity, user) 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) assert clean(res) == clean(expected)
end end
@ -61,7 +64,10 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
answer = %{answer | data: data} answer = %{answer | data: data}
note_object = Object.get_by_ap_id(note.data["object"]["id"]) 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"]) user = User.get_cached_by_ap_id(answer.data["actor"])
@ -86,7 +92,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
tuple = ActivityRepresenter.to_simple_form(answer, user) 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) assert clean(res) == clean(expected)
end end
@ -102,7 +108,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
note_user = User.get_cached_by_ap_id(note.data["actor"]) note_user = User.get_cached_by_ap_id(note.data["actor"])
note = Repo.get(Activity, note.id) note = Repo.get(Activity, note.id)
note_xml = ActivityRepresenter.to_simple_form(note, note_user, true)
note_xml =
ActivityRepresenter.to_simple_form(note, note_user, true)
|> :xmerl.export_simple_content(:xmerl_xml) |> :xmerl.export_simple_content(:xmerl_xml)
|> to_string |> to_string
@ -120,11 +128,14 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
<activity:object> <activity:object>
#{note_xml} #{note_xml}
</activity:object> </activity:object>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{note.data["actor"]}"/> <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
note.data["actor"]
}"/>
<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"/>
""" """
announce_xml = ActivityRepresenter.to_simple_form(announce, user) announce_xml =
ActivityRepresenter.to_simple_form(announce, user)
|> :xmerl.export_simple_content(:xmerl_xml) |> :xmerl.export_simple_content(:xmerl_xml)
|> to_string |> to_string
@ -139,7 +150,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
tuple = ActivityRepresenter.to_simple_form(like, user) tuple = ActivityRepresenter.to_simple_form(like, user)
refute is_nil(tuple) 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 = """ expected = """
<activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb> <activity:verb>http://activitystrea.ms/schema/1.0/favorite</activity:verb>
@ -156,7 +167,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
<link ref="#{like.data["context"]}" rel="ostatus:conversation" /> <link ref="#{like.data["context"]}" rel="ostatus:conversation" />
<link rel="self" type="application/atom+xml" href="#{like.data["id"]}"/> <link rel="self" type="application/atom+xml" href="#{like.data["id"]}"/>
<thr:in-reply-to ref="#{note.data["id"]}" /> <thr:in-reply-to ref="#{note.data["id"]}" />
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{note.data["actor"]}"/> <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
note.data["actor"]
}"/>
<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"/>
""" """
@ -166,7 +179,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
test "a follow activity" do test "a follow activity" do
follower = insert(:user) follower = insert(:user)
followed = insert(:user) followed = insert(:user)
{:ok, activity} = ActivityPub.insert(%{
{:ok, activity} =
ActivityPub.insert(%{
"type" => "Follow", "type" => "Follow",
"actor" => follower.ap_id, "actor" => follower.ap_id,
"object" => followed.ap_id, "object" => followed.ap_id,
@ -177,7 +192,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
refute is_nil(tuple) 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 = """ expected = """
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
@ -193,7 +208,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
<uri>#{activity.data["object"]}</uri> <uri>#{activity.data["object"]}</uri>
</activity:object> </activity:object>
<link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/> <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{activity.data["object"]}"/> <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
activity.data["object"]
}"/>
""" """
assert clean(res) == clean(expected) assert clean(res) == clean(expected)
@ -209,7 +226,7 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
refute is_nil(tuple) 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 = """ expected = """
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>
@ -225,7 +242,9 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
<uri>#{followed.ap_id}</uri> <uri>#{followed.ap_id}</uri>
</activity:object> </activity:object>
<link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/> <link rel="self" type="application/atom+xml" href="#{activity.data["id"]}"/>
<link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{followed.ap_id}"/> <link rel="mentioned" ostatus:object-type="http://activitystrea.ms/schema/1.0/person" href="#{
followed.ap_id
}"/>
""" """
assert clean(res) == clean(expected) assert clean(res) == clean(expected)
@ -233,13 +252,22 @@ defmodule Pleroma.Web.OStatus.ActivityRepresenterTest do
test "a delete" do test "a delete" do
user = insert(:user) 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) tuple = ActivityRepresenter.to_simple_form(activity, nil)
refute is_nil(tuple) 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 = """ expected = """
<activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type> <activity:object-type>http://activitystrea.ms/schema/1.0/activity</activity:object-type>

View file

@ -11,14 +11,18 @@ defmodule Pleroma.Web.OStatus.FeedRepresenterTest do
tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user]) tuple = FeedRepresenter.to_simple_form(user, [note_activity], [user])
most_recent_update = note_activity.updated_at most_recent_update =
|> NaiveDateTime.to_iso8601 note_activity.updated_at
|> NaiveDateTime.to_iso8601()
res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string res = :xmerl.export_simple_content(tuple, :xmerl_xml) |> to_string
user_xml = UserRepresenter.to_simple_form(user)
user_xml =
UserRepresenter.to_simple_form(user)
|> :xmerl.export_simple_content(:xmerl_xml) |> :xmerl.export_simple_content(:xmerl_xml)
entry_xml = ActivityRepresenter.to_simple_form(note_activity, user) entry_xml =
ActivityRepresenter.to_simple_form(note_activity, user)
|> :xmerl.export_simple_content(:xmerl_xml) |> :xmerl.export_simple_content(:xmerl_xml)
expected = """ expected = """
@ -39,6 +43,7 @@ defmodule Pleroma.Web.OStatus.FeedRepresenterTest do
</entry> </entry>
</feed> </feed>
""" """
assert clean(res) == clean(expected) assert clean(res) == clean(expected)
end end

View file

@ -14,8 +14,13 @@ defmodule Pleroma.Web.OStatus.DeleteHandlingTest do
{:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object) {:ok, like, _object} = Pleroma.Web.ActivityPub.ActivityPub.like(user, object)
incoming = File.read!("test/fixtures/delete.xml") incoming =
|> String.replace("tag:mastodon.sdf.org,2017-06-10:objectId=310513:objectType=Status", note.data["object"]["id"]) 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) {:ok, [delete]} = OStatus.handle_incoming(incoming)
refute Repo.get(Activity, note.id) refute Repo.get(Activity, note.id)

View file

@ -7,7 +7,9 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
test "decodes a salmon", %{conn: conn} do test "decodes a salmon", %{conn: conn} do
user = insert(:user) user = insert(:user)
salmon = File.read!("test/fixtures/salmon.xml") salmon = File.read!("test/fixtures/salmon.xml")
conn = conn
conn =
conn
|> put_req_header("content-type", "application/atom+xml") |> put_req_header("content-type", "application/atom+xml")
|> post("/users/#{user.nickname}/salmon", salmon) |> post("/users/#{user.nickname}/salmon", salmon)
@ -17,7 +19,9 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
test "decodes a salmon with a changed magic key", %{conn: conn} do test "decodes a salmon with a changed magic key", %{conn: conn} do
user = insert(:user) user = insert(:user)
salmon = File.read!("test/fixtures/salmon.xml") salmon = File.read!("test/fixtures/salmon.xml")
conn = conn
conn =
conn
|> put_req_header("content-type", "application/atom+xml") |> put_req_header("content-type", "application/atom+xml")
|> post("/users/#{user.nickname}/salmon", salmon) |> post("/users/#{user.nickname}/salmon", salmon)
@ -25,11 +29,18 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
# Set a wrong magic-key for a user so it has to refetch # 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") salmon_user = User.get_by_ap_id("http://gs.example.org:4040/index.php/user/1")
info = salmon_user.info # Wrong key
|> Map.put("magic_key", "RSA.pu0s-halox4tu7wmES1FVSx6u-4wc0YrUFXcqWXZG4-27UmbCOpMQftRCldNRfyA-qLbz-eqiwrong1EwUvjsD4cYbAHNGHwTvDOyx5AKthQUP44ykPv7kjKGh3DWKySJvcs9tlUG87hlo7AvnMo9pwRS_Zz2CacQ-MKaXyDepk=.AQAB") # 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})) Repo.update(User.info_changeset(salmon_user, %{info: info}))
conn = build_conn() conn =
build_conn()
|> put_req_header("content-type", "application/atom+xml") |> put_req_header("content-type", "application/atom+xml")
|> post("/users/#{user.nickname}/salmon", salmon) |> post("/users/#{user.nickname}/salmon", salmon)
@ -40,7 +51,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"]) user = User.get_cached_by_ap_id(note_activity.data["actor"])
conn = conn conn =
conn
|> get("/users/#{user.nickname}/feed.atom") |> get("/users/#{user.nickname}/feed.atom")
assert response(conn, 200) =~ note_activity.data["object"]["content"] assert response(conn, 200) =~ note_activity.data["object"]["content"]
@ -49,14 +61,16 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
test "gets an object", %{conn: conn} do test "gets an object", %{conn: conn} do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
user = User.get_by_ap_id(note_activity.data["actor"]) 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}" url = "/objects/#{uuid}"
conn = conn conn =
conn
|> get(url) |> get(url)
expected = ActivityRepresenter.to_simple_form(note_activity, user, true) expected =
|> ActivityRepresenter.wrap_with_entry ActivityRepresenter.to_simple_form(note_activity, user, true)
|> ActivityRepresenter.wrap_with_entry()
|> :xmerl.export_simple(:xmerl_xml) |> :xmerl.export_simple(:xmerl_xml)
|> to_string |> to_string
@ -65,10 +79,11 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
test "gets an activity", %{conn: conn} do test "gets an activity", %{conn: conn} do
note_activity = insert(:note_activity) 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}" url = "/activities/#{uuid}"
conn = conn conn =
conn
|> get(url) |> get(url)
assert response(conn, 200) assert response(conn, 200)
@ -78,7 +93,8 @@ defmodule Pleroma.Web.OStatus.OStatusControllerTest do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
url = "/notice/#{note_activity.id}" url = "/notice/#{note_activity.id}"
conn = conn conn =
conn
|> get(url) |> get(url)
assert response(conn, 200) assert response(conn, 200)

View file

@ -20,10 +20,16 @@ defmodule Pleroma.Web.OStatusTest do
assert user.info["note_count"] == 1 assert user.info["note_count"] == 1
assert activity.data["type"] == "Create" assert activity.data["type"] == "Create"
assert activity.data["object"]["type"] == "Note" 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["published"] == "2017-04-23T14:51:03+00:00"
assert activity.data["object"]["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 "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 assert activity.local == false
@ -68,7 +74,9 @@ defmodule Pleroma.Web.OStatusTest do
data: %{ data: %{
"id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4", "id" => "https://pleroma.soykaf.com/objects/c237d966-ac75-4fe3-a87a-d89d71a3a7a4",
"context" => "2hu" "context" => "2hu"
}}) }
})
incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml") incoming = File.read!("test/fixtures/incoming_reply_mastodon.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming) {:ok, [activity]} = OStatus.handle_incoming(incoming)
@ -113,8 +121,13 @@ defmodule Pleroma.Web.OStatusTest do
assert activity.data["type"] == "Create" assert activity.data["type"] == "Create"
assert activity.data["object"]["type"] == "Note" assert activity.data["object"]["type"] == "Note"
assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211" assert activity.data["object"]["actor"] == "https://social.heldscal.la/user/23211"
assert activity.data["object"]["content"] == "@<a href=\"https://gs.archae.me/user/4687\" class=\"h-card u-url p-nickname mention\" title=\"shpbot\">shpbot</a> why not indeed."
assert activity.data["object"]["inReplyTo"] == "tag:gs.archae.me,2017-04-30:noticeId=778260:objectType=note" assert activity.data["object"]["content"] ==
"@<a href=\"https://gs.archae.me/user/4687\" class=\"h-card u-url p-nickname mention\" title=\"shpbot\">shpbot</a> 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"] assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end end
@ -141,7 +154,9 @@ defmodule Pleroma.Web.OStatusTest do
incoming = File.read!("test/fixtures/share-gs-local.xml") incoming = File.read!("test/fixtures/share-gs-local.xml")
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"]) user = User.get_cached_by_ap_id(note_activity.data["actor"])
incoming = incoming
incoming =
incoming
|> String.replace("LOCAL_ID", note_activity.data["object"]["id"]) |> String.replace("LOCAL_ID", note_activity.data["object"]["id"])
|> String.replace("LOCAL_USER", user.ap_id) |> String.replace("LOCAL_USER", user.ap_id)
@ -168,7 +183,9 @@ defmodule Pleroma.Web.OStatusTest do
assert activity.data["type"] == "Announce" assert activity.data["type"] == "Announce"
assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda" assert activity.data["actor"] == "https://mastodon.social/users/lambadalambda"
assert activity.data["object"] == retweeted_activity.data["object"]["id"] 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 refute activity.local
assert retweeted_activity.data["type"] == "Create" assert retweeted_activity.data["type"] == "Create"
@ -178,34 +195,41 @@ defmodule Pleroma.Web.OStatusTest do
end end
test "handle incoming favorites - GS, websub" do test "handle incoming favorites - GS, websub" do
capture_log fn -> capture_log(fn ->
incoming = File.read!("test/fixtures/favorite.xml") incoming = File.read!("test/fixtures/favorite.xml")
{:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming) {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
assert activity.data["type"] == "Like" assert activity.data["type"] == "Like"
assert activity.data["actor"] == "https://social.heldscal.la/user/23211" assert activity.data["actor"] == "https://social.heldscal.la/user/23211"
assert activity.data["object"] == favorited_activity.data["object"]["id"] 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 refute activity.local
assert favorited_activity.data["type"] == "Create" assert favorited_activity.data["type"] == "Create"
assert favorited_activity.data["actor"] == "https://shitposter.club/user/1" 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 refute favorited_activity.local
end end)
end end
test "handle conversation references" do test "handle conversation references" do
incoming = File.read!("test/fixtures/mastodon_conversation.xml") incoming = File.read!("test/fixtures/mastodon_conversation.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming) {: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 end
test "handle incoming favorites with locally available object - GS, websub" do test "handle incoming favorites with locally available object - GS, websub" do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
incoming = File.read!("test/fixtures/favorite_with_local_note.xml") incoming =
File.read!("test/fixtures/favorite_with_local_note.xml")
|> String.replace("localid", note_activity.data["object"]["id"]) |> String.replace("localid", note_activity.data["object"]["id"])
{:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming) {:ok, [[activity, favorited_activity]]} = OStatus.handle_incoming(incoming)
@ -224,9 +248,15 @@ defmodule Pleroma.Web.OStatusTest do
assert activity.data["type"] == "Create" assert activity.data["type"] == "Create"
assert activity.data["object"]["type"] == "Note" 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 "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"] assert "https://www.w3.org/ns/activitystreams#Public" in activity.data["to"]
end end
@ -234,7 +264,10 @@ defmodule Pleroma.Web.OStatusTest do
incoming = File.read!("test/fixtures/follow.xml") incoming = File.read!("test/fixtures/follow.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming) {:ok, [activity]} = OStatus.handle_incoming(incoming)
assert activity.data["type"] == "Follow" 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["actor"] == "https://social.heldscal.la/user/23211"
assert activity.data["object"] == "https://pawoo.net/users/pekorino" assert activity.data["object"] == "https://pawoo.net/users/pekorino"
refute activity.local refute activity.local
@ -304,7 +337,8 @@ defmodule Pleroma.Web.OStatusTest do
expected = %{ expected = %{
"hub" => "https://social.heldscal.la/main/push/hub", "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", "name" => "shp",
"nickname" => "shp", "nickname" => "shp",
"salmon" => "https://social.heldscal.la/main/salmon/user/29191", "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
@ -314,10 +348,20 @@ defmodule Pleroma.Web.OStatusTest do
"host" => "social.heldscal.la", "host" => "social.heldscal.la",
"fqn" => user, "fqn" => user,
"bio" => "cofe", "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}", "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
"ap_id" => nil "ap_id" => nil
} }
assert data == expected assert data == expected
end end
@ -329,7 +373,8 @@ defmodule Pleroma.Web.OStatusTest do
expected = %{ expected = %{
"hub" => "https://social.heldscal.la/main/push/hub", "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", "name" => "shp",
"nickname" => "shp", "nickname" => "shp",
"salmon" => "https://social.heldscal.la/main/salmon/user/29191", "salmon" => "https://social.heldscal.la/main/salmon/user/29191",
@ -339,23 +384,35 @@ defmodule Pleroma.Web.OStatusTest do
"host" => "social.heldscal.la", "host" => "social.heldscal.la",
"fqn" => user, "fqn" => user,
"bio" => "cofe", "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}", "subscribe_address" => "https://social.heldscal.la/main/ostatussub?profile={uri}",
"ap_id" => nil "ap_id" => nil
} }
assert data == expected assert data == expected
end end
end end
describe "fetching a status by it's HTML url" do describe "fetching a status by it's HTML url" do
test "it builds a missing status from an 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" 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["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 end
test "it works for atom notes, too" do test "it works for atom notes, too" do
@ -370,6 +427,9 @@ defmodule Pleroma.Web.OStatusTest do
incoming = File.read!("test/fixtures/nil_mention_entry.xml") incoming = File.read!("test/fixtures/nil_mention_entry.xml")
{:ok, [activity]} = OStatus.handle_incoming(incoming) {: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
end end

Some files were not shown because too many files have changed in this diff Show more