Merge branch 'develop' into kaniini/pleroma-bugfix/unlisted-statuses

This commit is contained in:
lain 2018-05-13 10:56:11 +02:00
commit 76722ea9c8
59 changed files with 472 additions and 194 deletions

View File

@ -26,3 +26,12 @@ config :comeonin, :pbkdf2_rounds, 1
config :pleroma, :websub, Pleroma.Web.WebsubMock
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
config :pleroma, :httpoison, HTTPoisonMock
try do
import_config "test.secret.exs"
rescue
_ ->
IO.puts(
"You may want to create test.secret.exs to declare custom database connection parameters."
)
end

View File

@ -59,6 +59,16 @@ server {
}
# stop removing lines here.
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
# Uncomment this only after you get HTTPS working.
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

View File

@ -13,4 +13,3 @@ Restart=on-failure
[Install]
WantedBy=multi-user.target
Alias=pleroma.service

View File

@ -39,15 +39,9 @@ sub vcl_recv {
return (hash);
}
# Hack to enable a Terms of Service page missing from Pleroma
if (req.url ~ "^/about/more$") {
set req.http.x-redir = "https://" + req.http.host + "/static/terms-of-service.html";
return (synth(750, ""));
}
# Strip headers that will affect caching from all other static content
# This also permits caching of individual toots and AP Activities
if ((req.url ~ "^/(media|notice|objects|static)/") ||
if ((req.url ~ "^/(media|static)/") ||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
{
unset req.http.Cookie;
@ -99,8 +93,7 @@ sub vcl_backend_response {
# Strip cache-restricting headers from Pleroma on static content that we want to cache
# Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch)
if ((bereq.url ~ "^/(notice|objects)/") ||
(bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$")
{
unset beresp.http.set-cookie;
unset beresp.http.Cache-Control;

View File

@ -1,6 +1,5 @@
defmodule Mix.Tasks.FixApUsers do
use Mix.Task
import Mix.Ecto
import Ecto.Query
alias Pleroma.{Repo, User}

View File

@ -1,7 +1,6 @@
defmodule Mix.Tasks.GeneratePasswordReset do
use Mix.Task
import Mix.Ecto
alias Pleroma.{Repo, User}
alias Pleroma.User
@shortdoc "Generate password reset link for user"
def run([nickname]) do

View File

@ -1,6 +1,5 @@
defmodule Mix.Tasks.RegisterUser do
use Mix.Task
import Mix.Ecto
alias Pleroma.{Repo, User}
@shortdoc "Register user"

View File

@ -1,7 +1,6 @@
defmodule Mix.Tasks.RmUser do
use Mix.Task
import Mix.Ecto
alias Pleroma.{User, Repo}
alias Pleroma.User
@shortdoc "Permanently delete a user"
def run([nickname]) do

View File

@ -23,6 +23,18 @@ defmodule Pleroma.Application do
limit: 2500
]
]),
worker(
Cachex,
[
:idempotency_cache,
[
default_ttl: :timer.seconds(6 * 60 * 60),
ttl_interval: :timer.seconds(60),
limit: 2500
]
],
id: :cachex_idem
),
worker(Pleroma.Web.Federator, []),
worker(Pleroma.Gopher.Server, []),
worker(Pleroma.Stats, [])

View File

@ -65,12 +65,6 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
"#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n"
end
def response("") do
info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
link("Public Timeline", "/main/public") <>
link("Federated Timeline", "/main/all") <> ".\r\n"
end
def render_activities(activities) do
activities
|> Enum.reverse()
@ -93,6 +87,12 @@ defmodule Pleroma.Gopher.Server.ProtocolHandler do
|> Enum.join("\r\n")
end
def response("") do
info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
link("Public Timeline", "/main/public") <>
link("Federated Timeline", "/main/all") <> ".\r\n"
end
def response("/main/public") do
posts =
ActivityPub.fetch_public_activities(%{"type" => ["Create"], "local_only" => true})

View File

@ -91,7 +91,8 @@ defmodule Pleroma.Notification do
# TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user) do
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) do
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
user.ap_id == activity.data["actor"] do
notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification)
Pleroma.Web.Streamer.stream("user", notification)

View File

@ -7,11 +7,11 @@ defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
options
end
def call(%{assigns: %{valid_signature: true}} = conn, opts) do
def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
conn
end
def call(conn, opts) do
def call(conn, _opts) do
user = conn.params["actor"]
Logger.debug("Checking sig for #{user}")
[signature | _] = get_req_header(conn, "signature")

View File

@ -1,6 +1,6 @@
defmodule Pleroma.Stats do
import Ecto.Query
alias Pleroma.{User, Repo, Activity}
alias Pleroma.{User, Repo}
def start_link do
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)

View File

@ -1,6 +1,6 @@
defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
alias Pleroma.Web.WebFinger
alias Pleroma.Web.Federator
alias Pleroma.Web.OStatus
@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@httpoison Application.get_env(:pleroma, :httpoison)
@instance Application.get_env(:pleroma, :instance)
@rewrite_policy Keyword.get(@instance, :rewrite_policy)
def get_recipients(data) do
(data["to"] || []) ++ (data["cc"] || [])
@ -20,8 +19,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def insert(map, local \\ true) when is_map(map) do
with nil <- Activity.get_by_ap_id(map["id"]),
map <- lazy_put_activity_defaults(map),
:ok <- insert_full_object(map),
{:ok, map} <- @rewrite_policy.filter(map) do
{:ok, map} <- MRF.filter(map),
:ok <- insert_full_object(map) do
{:ok, activity} =
Repo.insert(%Activity{
data: map,
@ -66,7 +65,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
),
{:ok, activity} <- insert(create_data, local),
:ok <- maybe_federate(activity),
{:ok, actor} <- User.increase_note_count(actor) do
{:ok, _actor} <- User.increase_note_count(actor) do
{:ok, activity}
end
end
@ -177,7 +176,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
{:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity),
{:ok, actor} <- User.decrease_note_count(user) do
{:ok, _actor} <- User.decrease_note_count(user) do
{:ok, activity}
end
end
@ -236,7 +235,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
@ -313,7 +312,9 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
blocks = info["blocks"] || []
from(activity in query,
from(
activity in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks)
)
@ -405,7 +406,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end
def make_user_from_ap_id(ap_id) do
if user = User.get_by_ap_id(ap_id) do
if _user = User.get_by_ap_id(ap_id) do
Transmogrifier.upgrade_user_from_ap_id(ap_id)
else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
@ -501,7 +502,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
object = %Object{} ->
{:ok, object}
e ->
_e ->
Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
case OStatus.fetch_activity_from_url(id) do

View File

@ -1,7 +1,7 @@
defmodule Pleroma.Web.ActivityPub.ActivityPubController do
use Pleroma.Web, :controller
alias Pleroma.{User, Repo, Object, Activity}
alias Pleroma.Web.ActivityPub.{ObjectView, UserView, Transmogrifier}
alias Pleroma.{User, Object}
alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Federator

View File

@ -0,0 +1,24 @@
defmodule Pleroma.Web.ActivityPub.MRF do
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
def filter(object) do
get_policies()
|> Enum.reduce({:ok, object}, fn
policy, {:ok, object} ->
policy.filter(object)
_, error ->
error
end)
end
def get_policies() do
Application.get_env(:pleroma, :instance, [])
|> Keyword.get(:rewrite_policy, [])
|> get_policies()
end
defp get_policies(policy) when is_atom(policy), do: [policy]
defp get_policies(policies) when is_list(policies), do: policies
defp get_policies(_), do: []
end

View File

@ -1,6 +1,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do
Logger.info("REJECTING #{inspect(object)}")
{:reject, object}

View File

@ -1,4 +1,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do
{:ok, object}
end

View File

@ -1,5 +1,6 @@
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
@ -17,9 +18,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
if actor_info.host in @media_removal do
child_object = Map.delete(object["object"], "attachment")
object = Map.put(object, "object", child_object)
{:ok, object}
else
{:ok, object}
end
{:ok, object}
end
@media_nsfw Keyword.get(@mrf_policy, :media_nsfw)
@ -32,9 +34,10 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
child_object = Map.put(child_object, "tags", tags)
child_object = Map.put(child_object, "sensitive", true)
object = Map.put(object, "object", child_object)
{:ok, object}
else
{:ok, object}
end
{:ok, object}
end
@ftl_removal Keyword.get(@mrf_policy, :federated_timeline_removal)
@ -43,24 +46,31 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
user = User.get_by_ap_id(object["actor"])
# flip to/cc relationship to make the post unlisted
if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
user.follower_address in object["cc"] do
to =
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
[user.follower_address]
object =
if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
user.follower_address in object["cc"] do
to =
List.delete(object["to"], "https://www.w3.org/ns/activitystreams#Public") ++
[user.follower_address]
cc =
List.delete(object["cc"], user.follower_address) ++
["https://www.w3.org/ns/activitystreams#Public"]
cc =
List.delete(object["cc"], user.follower_address) ++
["https://www.w3.org/ns/activitystreams#Public"]
object = Map.put(object, "to", to)
object = Map.put(object, "cc", cc)
end
object
|> Map.put("to", to)
|> Map.put("cc", cc)
else
object
end
{:ok, object}
else
{:ok, object}
end
{:ok, object}
end
@impl true
def filter(object) do
actor_info = URI.parse(object["actor"])
@ -70,7 +80,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
{:ok, object} <- check_ftl_removal(actor_info, object) do
{:ok, object}
else
e -> {:reject, nil}
_e -> {:reject, nil}
end
end
end

View File

@ -72,9 +72,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Enum.reduce(%{}, fn data, mapping ->
name = data["name"]
if String.starts_with?(name, ":") do
name = name |> String.slice(1..-2)
end
name =
if String.starts_with?(name, ":") do
name |> String.slice(1..-2)
else
name
end
mapping |> Map.put(name, data["icon"]["url"])
end)
@ -143,12 +146,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data
%{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = _data
) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do
{:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
{:ok, activity}
else
_e -> :error
@ -156,12 +159,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data
%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = _data
) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do
{:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do
{:ok, activity}
else
_e -> :error
@ -202,7 +205,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# TODO: Make secure.
def handle_incoming(
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = _data
) do
object_id =
case object_id do
@ -210,13 +213,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
id -> id
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, activity} <- ActivityPub.delete(object, false) do
{:ok, activity}
else
e -> :error
_e -> :error
end
end
@ -254,10 +257,10 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> set_reply_to_uri
end
@doc
"""
internal -> Mastodon
"""
# @doc
# """
# internal -> Mastodon
# """
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
object =
@ -272,7 +275,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
end
def prepare_outgoing(%{"type" => type} = data) do
def prepare_outgoing(%{"type" => _type} = data) do
data =
data
|> maybe_fix_object_url
@ -286,7 +289,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
case ActivityPub.fetch_object_from_id(data["object"]) do
{:ok, relative_object} ->
if relative_object.data["external_url"] do
data =
_data =
data
|> Map.put("object", relative_object.data["external_url"])
else

View File

@ -47,25 +47,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|> Map.merge(Utils.make_json_ld_header())
end
def collection(collection, iri, page, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => length(collection),
"orderedItems" => items
}
if offset < length(collection) do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
end
end
def render("following.json", %{user: user, page: page}) do
query = User.get_friends_query(user)
query = from(user in query, select: [:ap_id])
@ -123,9 +104,12 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"limit" => "10"
}
if max_qid != nil do
params = Map.put(params, "max_id", max_qid)
end
params =
if max_qid != nil do
Map.put(params, "max_id", max_qid)
else
params
end
activities = ActivityPub.fetch_public_activities(params)
min_id = Enum.at(activities, 0).id
@ -162,4 +146,23 @@ defmodule Pleroma.Web.ActivityPub.UserView do
page |> Map.merge(Utils.make_json_ld_header())
end
end
def collection(collection, iri, page, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => total,
"orderedItems" => items
}
if offset < total do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
end
end
end

View File

@ -1,7 +1,6 @@
defmodule Pleroma.Web.UserSocket do
use Phoenix.Socket
alias Pleroma.User
alias Comeonin.Pbkdf2
## Channels
# channel "room:*", Pleroma.Web.RoomChannel

View File

@ -1,5 +1,5 @@
defmodule Pleroma.Web.CommonAPI do
alias Pleroma.{Repo, Activity, Object, User}
alias Pleroma.{Repo, Activity, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Formatter

View File

@ -1,5 +1,5 @@
defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.{Repo, Object, Formatter, User, Activity}
alias Pleroma.{Repo, Object, Formatter, Activity}
alias Pleroma.Web.ActivityPub.Utils
alias Calendar.Strftime
@ -49,7 +49,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{[user.follower_address | to], cc}
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)
if inReplyTo do
@ -69,7 +69,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def make_context(%Activity{data: %{"context" => context}}), do: context
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
add_attachments(text, attachments)

View File

@ -14,6 +14,10 @@ defmodule Pleroma.Web.Federator do
@federating Keyword.get(@instance, :federating)
@max_jobs 20
def init(args) do
{:ok, args}
end
def start_link do
spawn(fn ->
# 1 minute
@ -89,12 +93,12 @@ defmodule Pleroma.Web.Federator do
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.get_by_ap_id(params["id"]),
{:ok, activity} <- Transmogrifier.handle_incoming(params) do
{:ok, _activity} <- Transmogrifier.handle_incoming(params) do
else
%Activity{} ->
Logger.info("Already had #{params["id"]}")
e ->
_e ->
# Just drop those for now
Logger.info("Unhandled activity")
Logger.info(Poison.encode!(params, pretty: 2))
@ -154,7 +158,7 @@ defmodule Pleroma.Web.Federator do
end
end
def handle_cast({:enqueue, type, payload, priority}, state)
def handle_cast({:enqueue, type, payload, _priority}, state)
when type in [:incoming_doc, :incoming_ap_doc] do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
i_queue = enqueue_sorted(i_queue, {type, payload}, 1)
@ -162,7 +166,7 @@ defmodule Pleroma.Web.Federator do
{:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}}
end
def handle_cast({:enqueue, type, payload, priority}, state) do
def handle_cast({:enqueue, type, payload, _priority}, state) do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
o_queue = enqueue_sorted(o_queue, {type, payload}, 1)
{o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue)

View File

@ -45,7 +45,7 @@ defmodule Pleroma.Web.HTTPSignatures do
end
end
else
e ->
_e ->
Logger.debug("Could not public key!")
false
end

View File

@ -112,7 +112,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})",
email: Keyword.get(@instance, :email),
urls: %{
streaming_api: String.replace(Web.base_url(), ["http", "https"], "wss")
streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
},
stats: Stats.get_stats(),
thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
@ -212,14 +212,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("actor_id", ap_id)
|> Map.put("whole_db", true)
if params["pinned"] == "true" do
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
activities = []
else
activities =
activities =
if params["pinned"] == "true" do
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
[]
else
ActivityPub.fetch_public_activities(params)
|> Enum.reverse()
end
end
conn
|> add_link_headers(:user_statuses, activities, params["id"])
@ -275,7 +275,19 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
|> Map.put("no_attachment_links", true)
{:ok, activity} = CommonAPI.post(user, params)
idempotency_key =
case get_req_header(conn, "idempotency-key") do
[key] -> key
_ -> Ecto.UUID.generate()
end
{:ok, activity} =
Cachex.get!(
:idempotency_cache,
idempotency_key,
fallback: fn _ -> CommonAPI.post(user, params) end
)
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end

View File

@ -82,19 +82,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
id = activity.data["object"]["inReplyTo"]
replied_to_activities[activity.data["object"]["inReplyTo"]]
end
def get_reply_to(%{data: %{"object" => object}}, _) do
if object["inReplyTo"] && object["inReplyTo"] != "" do
Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
else
nil
end
end
def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do
user = User.get_cached_by_ap_id(activity.data["actor"])
@ -164,19 +151,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
}
end
def get_visibility(object) do
public = "https://www.w3.org/ns/activitystreams#Public"
to = object["to"] || []
cc = object["cc"] || []
cond do
public in to -> "public"
public in cc -> "unlisted"
Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
true -> "direct"
end
end
def render("attachment.json", %{attachment: attachment}) do
[%{"mediaType" => media_type, "href" => href} | _] = attachment["url"]
@ -199,4 +173,30 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
type: type
}
end
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
_id = activity.data["object"]["inReplyTo"]
replied_to_activities[activity.data["object"]["inReplyTo"]]
end
def get_reply_to(%{data: %{"object" => object}}, _) do
if object["inReplyTo"] && object["inReplyTo"] != "" do
Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
else
nil
end
end
def get_visibility(object) do
public = "https://www.w3.org/ns/activitystreams#Public"
to = object["to"] || []
cc = object["cc"] || []
cond do
public in to -> "public"
public in cc -> "unlisted"
Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
true -> "direct"
end
end
end

View File

@ -0,0 +1 @@

View File

@ -0,0 +1,62 @@
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
use Pleroma.Web, :controller
alias Pleroma.Stats
alias Pleroma.Web
@instance Application.get_env(:pleroma, :instance)
def schemas(conn, _params) do
response = %{
links: [
%{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
href: Web.base_url() <> "/nodeinfo/2.0.json"
}
]
}
json(conn, response)
end
# Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json
def nodeinfo(conn, %{"version" => "2.0"}) do
stats = Stats.get_stats()
response = %{
version: "2.0",
software: %{
name: "pleroma",
version: Keyword.get(@instance, :version)
},
protocols: ["ostatus", "activitypub"],
services: %{
inbound: [],
outbound: []
},
openRegistrations: Keyword.get(@instance, :registrations_open),
usage: %{
users: %{
total: stats.user_count || 0
},
localPosts: stats.status_count || 0
},
metadata: %{
nodeName: Keyword.get(@instance, :name)
}
}
conn
|> put_resp_header(
"content-type",
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
)
|> json(response)
end
def nodeinfo(conn, _) do
conn
|> put_status(404)
|> json(%{error: "Nodeinfo schema version not handled"})
end
end

View File

@ -11,7 +11,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
field(:valid_until, :naive_datetime)
field(:used, :boolean, default: false)
belongs_to(:user, Pleroma.User)
belongs_to(:app, Pleroma.App)
belongs_to(:app, App)
timestamps()
end

View File

@ -9,7 +9,7 @@ defmodule Pleroma.Web.OAuth.Token do
field(:refresh_token, :string)
field(:valid_until, :naive_datetime)
belongs_to(:user, Pleroma.User)
belongs_to(:app, Pleroma.App)
belongs_to(:app, App)
timestamps()
end

View File

@ -1,7 +1,6 @@
defmodule Pleroma.Web.OStatus.ActivityRepresenter do
alias Pleroma.{Activity, User, Object}
alias Pleroma.Web.OStatus.UserRepresenter
alias Pleroma.Formatter
require Logger
defp get_href(id) do

View File

@ -1,7 +1,7 @@
defmodule Pleroma.Web.OStatus.NoteHandler do
require Logger
alias Pleroma.Web.{XML, OStatus}
alias Pleroma.{Object, User, Activity}
alias Pleroma.{Object, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI

View File

@ -8,7 +8,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.XML
alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.ActivityPub.ActivityPub
import Ecto.Query
def feed_redirect(conn, %{"nickname" => nickname} = params) do
user = User.get_cached_by_nickname(nickname)

View File

@ -295,6 +295,11 @@ defmodule Pleroma.Web.Router do
get("/host-meta", WebFinger.WebFingerController, :host_meta)
get("/webfinger", WebFinger.WebFingerController, :webfinger)
get("/nodeinfo", Nodeinfo.NodeinfoController, :schemas)
end
scope "/nodeinfo", Pleroma.Web do
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
end
end

View File

@ -3,6 +3,10 @@ defmodule Pleroma.Web.Streamer do
require Logger
alias Pleroma.{User, Notification}
def init(args) do
{:ok, args}
end
def start_link do
spawn(fn ->
# 30 seconds
@ -110,20 +114,26 @@ defmodule Pleroma.Web.Streamer do
def push_to_socket(topics, topic, item) do
Enum.each(topics[topic] || [], fn socket ->
json =
%{
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
"status.json",
activity: item,
for: socket.assigns[:user]
)
|> Jason.encode!()
}
|> Jason.encode!()
# Get the current user so we have up-to-date blocks etc.
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
blocks = user.info["blocks"] || []
send(socket.transport_pid, {:text, json})
unless item.actor in blocks do
json =
%{
event: "update",
payload:
Pleroma.Web.MastodonAPI.StatusView.render(
"status.json",
activity: item,
for: user
)
|> Jason.encode!()
}
|> Jason.encode!()
send(socket.transport_pid, {:text, json})
end
end)
end

View File

@ -92,7 +92,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
with %User{} = user <- User.get_cached_by_nickname(username),
true <- Pbkdf2.checkpw(password, user.password_hash),
%User{} = followed <- Repo.get(User, id),
%User{} = _followed <- Repo.get(User, id),
{:ok, follower} <- User.follow(user, followee),
{:ok, _activity} <- ActivityPub.follow(follower, followee) do
conn

View File

@ -1,7 +1,6 @@
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
alias Pleroma.{User, Activity, Repo, Object}
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.{OStatus, CommonAPI}
import Ecto.Query
@ -184,7 +183,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
defp parse_int(_, default), do: default
def search(user, %{"q" => query} = params) do
def search(_user, %{"q" => query} = params) do
limit = parse_int(params["rpp"], 20)
page = parse_int(params["page"], 1)
offset = (page - 1) * limit
@ -206,7 +205,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
order_by: [desc: :inserted_at]
)
activities = Repo.all(q)
_activities = Repo.all(q)
end
defp make_date do

View File

@ -347,7 +347,8 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
def update_profile(%{assigns: %{user: user}} = conn, params) do
params =
if bio = params["description"] do
Map.put(params, "bio", bio)
bio_brs = Regex.replace(~r/\r?\n/, bio, "<br>")
Map.put(params, "bio", bio_brs)
else
params
end

View File

@ -31,7 +31,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do
end
defp collect_context_ids(activities) do
contexts =
_contexts =
activities
|> Enum.reject(& &1.data["context_id"])
|> Enum.map(fn %{data: data} ->

View File

@ -2,7 +2,6 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do
use Pleroma.Web, :view
alias Pleroma.{Notification, User}
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MediaProxy
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.ActivityView

View File

@ -1,7 +1,7 @@
defmodule Pleroma.Web.WebFinger do
@httpoison Application.get_env(:pleroma, :httpoison)
alias Pleroma.{Repo, User, XmlBuilder}
alias Pleroma.{User, XmlBuilder}
alias Pleroma.Web
alias Pleroma.Web.{XML, Salmon, OStatus}
require Jason
@ -239,13 +239,14 @@ defmodule Pleroma.Web.WebFinger do
URI.parse(account).host
end
case find_lrdd_template(domain) do
{:ok, template} ->
address = String.replace(template, "{uri}", URI.encode(account))
address =
case find_lrdd_template(domain) do
{:ok, template} ->
String.replace(template, "{uri}", URI.encode(account))
_ ->
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
end
_ ->
"http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
end
with response <-
@httpoison.get(

View File

@ -14,7 +14,7 @@ defmodule Pleroma.Web.XML do
if res == "", do: nil, else: res
catch
e ->
_e ->
Logger.debug("Couldn't find xpath #{xpath} in XML doc")
nil
end

View File

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.cdeff2a56af285544b89.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.408cea515c3032097a51.js></script></body></html>
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.38e369a50eccc2857845.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.af121efa5ff89725b4c6.js></script></body></html>

View File

@ -2,7 +2,8 @@
"theme": "pleroma-dark",
"background": "/static/aurora_borealis.jpg",
"logo": "/static/logo.png",
"defaultPath": "/main/all",
"redirectRootNoLogin": "/main/all",
"redirectRootLogin": "/main/friends",
"chatDisabled": false,
"showInstanceSpecificPanel": true
"showInstanceSpecificPanel": false
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,2 @@
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,c){for(var p,l,s=0,f=[];s<o.length;s++)l=o[s],n[l]&&f.push.apply(f,n[l]),n[l]=0;for(p in c)Object.prototype.hasOwnProperty.call(c,p)&&(e[p]=c[p]);for(a&&a(o,c);f.length;)f.shift().call(null,t);if(c[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"ef2aee0b2db579c3a86a",2:"af121efa5ff89725b4c6"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.38e369a50eccc2857845.js.map

View File

@ -1,2 +0,0 @@
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"ef2aee0b2db579c3a86a",2:"408cea515c3032097a51"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.cdeff2a56af285544b89.js.map

View File

@ -33,6 +33,13 @@ defmodule Pleroma.NotificationTest do
assert nil == Notification.create_notification(activity, user)
end
test "it doesn't create a notification for user if he is the activity author" do
activity = insert(:note_activity)
author = User.get_by_ap_id(activity.data["actor"])
assert nil == Notification.create_notification(activity, author)
end
end
describe "get notification" do

View File

@ -26,7 +26,7 @@ defmodule Pleroma.Builders.ActivityBuilder do
end
def insert_list(times, data \\ %{}, opts \\ %{}) do
Enum.map(1..times, fn n ->
Enum.map(1..times, fn _n ->
{:ok, activity} = insert(data, opts)
activity
end)

View File

@ -367,7 +367,7 @@ defmodule HTTPoisonMock do
def post(
"https://social.heldscal.la/main/push/hub",
{:form, data},
{:form, _data},
"Content-type": "application/x-www-form-urlencoded"
) do
{:ok,
@ -711,11 +711,11 @@ defmodule HTTPoisonMock do
}"}
end
def post(url, body, headers) do
def post(url, _body, _headers) do
{:error, "Not implemented the mock response for post #{inspect(url)}"}
end
def post(url, body, headers, options) do
def post(url, _body, _headers, _options) do
{:error, "Not implemented the mock response for post #{inspect(url)}"}
end
end

View File

@ -63,7 +63,42 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
test "posting a status", %{conn: conn} do
user = insert(:user)
conn =
idempotency_key = "Pikachu rocks!"
conn_one =
conn
|> assign(:user, user)
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "false"
})
{:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
# Six hours
assert ttl > :timer.seconds(6 * 60 * 60 - 1)
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
json_response(conn_one, 200)
assert Repo.get(Activity, id)
conn_two =
conn
|> assign(:user, user)
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "false"
})
assert %{"id" => second_id} = json_response(conn_two, 200)
assert id == second_id
conn_three =
conn
|> assign(:user, user)
|> post("/api/v1/statuses", %{
@ -72,10 +107,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
"sensitive" => "false"
})
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
json_response(conn, 200)
assert %{"id" => third_id} = json_response(conn_three, 200)
assert Repo.get(Activity, id)
refute id == third_id
end
test "posting a sensitive status", %{conn: conn} do

View File

@ -0,0 +1,63 @@
defmodule Pleroma.Web.StreamerTest do
use Pleroma.DataCase
alias Pleroma.Web.Streamer
alias Pleroma.User
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
test "it sends to public" do
user = insert(:user)
other_user = insert(:user)
task =
Task.async(fn ->
assert_receive {:text, _}, 4_000
end)
fake_socket = %{
transport_pid: task.pid,
assigns: %{
user: user
}
}
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"})
topics = %{
"public" => [fake_socket]
}
Streamer.push_to_socket(topics, "public", activity)
Task.await(task)
end
test "it doesn't send to blocked users" do
user = insert(:user)
blocked_user = insert(:user)
{:ok, user} = User.block(user, blocked_user)
task =
Task.async(fn ->
refute_receive {:text, _}, 1_000
end)
fake_socket = %{
transport_pid: task.pid,
assigns: %{
user: user
}
}
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
topics = %{
"public" => [fake_socket]
}
Streamer.push_to_socket(topics, "public", activity)
Task.await(task)
end
end

View File

@ -257,8 +257,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
end
test "with credentials", %{conn: conn, user: current_user} do
other_user = insert(:user)
{:ok, activity} =
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user})
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
conn =
conn
@ -784,4 +786,18 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
assert status["id"] == activity.id
end
end
test "Convert newlines to <br> in bio", %{conn: conn} do
user = insert(:user)
conn =
conn
|> assign(:user, user)
|> post("/api/account/update_profile.json", %{
"description" => "Hello,\r\nWorld! I\n am a test."
})
user = Repo.get!(User, user.id)
assert user.bio == "Hello,<br>World! I<br> am a test."
end
end