diff options
Diffstat (limited to 'lib/pleroma/web')
84 files changed, 1262 insertions, 211 deletions
diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 31455343c..9c1eb377f 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ActivityPub do alias Pleroma.{Activity, Repo, Object, Upload, User, Notification} alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF} @@ -52,10 +56,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + defp check_remote_limit(%{"object" => %{"content" => content}}) do + limit = Pleroma.Config.get([:instance, :remote_limit]) + String.length(content) <= limit + end + + defp check_remote_limit(_), do: true + def insert(map, local \\ true) when is_map(map) do with nil <- Activity.normalize(map), map <- lazy_put_activity_defaults(map), :ok <- check_actor_is_active(map["actor"]), + {_, true} <- {:remote_limit_error, check_remote_limit(map)}, {:ok, map} <- MRF.filter(map), :ok <- insert_full_object(map) do {recipients, _, _} = get_recipients(map) @@ -352,21 +364,18 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do @valid_visibilities ~w[direct unlisted public private] - defp restrict_visibility(query, %{visibility: "direct"}) do - public = "https://www.w3.org/ns/activitystreams#Public" + defp restrict_visibility(query, %{visibility: visibility}) + when visibility in @valid_visibilities do + query = + from( + a in query, + where: + fragment("activity_visibility(?, ?, ?) = ?", a.actor, a.recipients, a.data, ^visibility) + ) - from( - activity in query, - join: sender in User, - on: sender.ap_id == activity.actor, - # Are non-direct statuses with no to/cc possible? - where: - fragment( - "not (? && ?)", - [^public, sender.follower_address], - activity.recipients - ) - ) + Ecto.Adapters.SQL.to_sql(:all, Repo, query) + + query end defp restrict_visibility(_query, %{visibility: visibility}) @@ -382,6 +391,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> Map.put("type", ["Create", "Announce"]) |> Map.put("actor_id", user.ap_id) |> Map.put("whole_db", true) + |> Map.put("pinned_activity_ids", user.info.pinned_activities) recipients = if reading_user do @@ -499,6 +509,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp restrict_replies(query, _), do: query + defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or val == "1" do + from(activity in query, where: fragment("?->>'type' != 'Announce'", activity.data)) + end + + defp restrict_reblogs(query, _), do: query + # Only search through last 100_000 activities by default defp restrict_recent(query, %{"whole_db" => true}), do: query @@ -534,6 +550,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ) end + defp restrict_pinned(query, %{"pinned" => "true", "pinned_activity_ids" => ids}) do + from(activity in query, where: activity.id in ^ids) + end + + defp restrict_pinned(query, _), do: query + def fetch_activities_query(recipients, opts \\ %{}) do base_query = from( @@ -557,6 +579,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do |> restrict_media(opts) |> restrict_visibility(opts) |> restrict_replies(opts) + |> restrict_reblogs(opts) + |> restrict_pinned(opts) end def fetch_activities(recipients, opts \\ %{}) do @@ -722,8 +746,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do {"Content-Type", "application/activity+json"}, {"signature", signature}, {"digest", digest} - ], - hackney: [pool: :default] + ] ) end @@ -783,6 +806,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end end + def is_public?(%Object{data: %{"type" => "Tombstone"}}) do + false + end + def is_public?(activity) do "https://www.w3.org/ns/activitystreams#Public" in (activity.data["to"] ++ (activity.data["cc"] || [])) diff --git a/lib/pleroma/web/activity_pub/activity_pub_controller.ex b/lib/pleroma/web/activity_pub/activity_pub_controller.ex index 0317f3c8c..73ca07e84 100644 --- a/lib/pleroma/web/activity_pub/activity_pub_controller.ex +++ b/lib/pleroma/web/activity_pub/activity_pub_controller.ex @@ -1,10 +1,15 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ActivityPubController do use Pleroma.Web, :controller - alias Pleroma.{User, Object} + alias Pleroma.{Activity, User, Object} alias Pleroma.Web.ActivityPub.{ObjectView, UserView} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.Relay alias Pleroma.Web.ActivityPub.Utils + alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.Federator require Logger @@ -49,6 +54,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def activity(conn, %{"uuid" => uuid}) do + with ap_id <- o_status_url(conn, :activity, uuid), + %Activity{} = activity <- Activity.normalize(ap_id), + {_, true} <- {:public?, ActivityPub.is_public?(activity)} do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(ObjectView.render("object.json", %{object: activity})) + else + {:public?, false} -> + {:error, :not_found} + end + end + def following(conn, %{"nickname" => nickname, "page" => page}) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do @@ -89,19 +107,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end - def outbox(conn, %{"nickname" => nickname, "max_id" => max_id}) do + def outbox(conn, %{"nickname" => nickname} = params) do with %User{} = user <- User.get_cached_by_nickname(nickname), {:ok, user} <- Pleroma.Web.WebFinger.ensure_keys_present(user) do conn |> put_resp_header("content-type", "application/activity+json") - |> json(UserView.render("outbox.json", %{user: user, max_id: max_id})) + |> json(UserView.render("outbox.json", %{user: user, max_id: params["max_id"]})) end end - def outbox(conn, %{"nickname" => nickname}) do - outbox(conn, %{"nickname" => nickname, "max_id" => nil}) - end - def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do with %User{} = user <- User.get_cached_by_nickname(nickname), true <- Utils.recipient_in_message(user.ap_id, params), @@ -152,6 +166,79 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do end end + def read_inbox(%{assigns: %{user: user}} = conn, %{"nickname" => nickname} = params) do + if nickname == user.nickname do + conn + |> put_resp_header("content-type", "application/activity+json") + |> json(UserView.render("inbox.json", %{user: user, max_id: params["max_id"]})) + else + conn + |> put_status(:forbidden) + |> json("can't read inbox of #{nickname} as #{user.nickname}") + end + end + + def handle_user_activity(user, %{"type" => "Create"} = params) do + object = + params["object"] + |> Map.merge(Map.take(params, ["to", "cc"])) + |> Map.put("attributedTo", user.ap_id()) + |> Transmogrifier.fix_object() + + ActivityPub.create(%{ + to: params["to"], + actor: user, + context: object["context"], + object: object, + additional: Map.take(params, ["cc"]) + }) + end + + def handle_user_activity(user, %{"type" => "Delete"} = params) do + with %Object{} = object <- Object.normalize(params["object"]), + true <- user.info.is_moderator || user.ap_id == object.data["actor"], + {:ok, delete} <- ActivityPub.delete(object) do + {:ok, delete} + else + _ -> {:error, "Can't delete object"} + end + end + + def handle_user_activity(_, _) do + {:error, "Unhandled activity type"} + end + + def update_outbox( + %{assigns: %{user: user}} = conn, + %{"nickname" => nickname} = params + ) do + if nickname == user.nickname do + actor = user.ap_id() + + params = + params + |> Map.drop(["id"]) + |> Map.put("actor", actor) + |> Transmogrifier.fix_addressing() + + with {:ok, %Activity{} = activity} <- handle_user_activity(user, params) do + conn + |> put_status(:created) + |> put_resp_header("location", activity.data["id"]) + |> json(activity.data) + else + {:error, message} -> + conn + |> put_status(:bad_request) + |> json(message) + end + else + conn + |> put_status(:forbidden) + |> json("can't update outbox of #{nickname} as #{user.nickname}") + end + end + def errors(conn, {:error, :not_found}) do conn |> put_status(404) diff --git a/lib/pleroma/web/activity_pub/mrf.ex b/lib/pleroma/web/activity_pub/mrf.ex index 0a4e2bf80..eebea207c 100644 --- a/lib/pleroma/web/activity_pub/mrf.ex +++ b/lib/pleroma/web/activity_pub/mrf.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF do @callback filter(Map.t()) :: {:ok | :reject, Map.t()} diff --git a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex index 811947943..a93ccf386 100644 --- a/lib/pleroma/web/activity_pub/mrf/drop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/drop_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do require Logger @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex index 6fa48454a..895376c9d 100644 --- a/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex +++ b/lib/pleroma/web/activity_pub/mrf/ensure_re_prepended.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do alias Pleroma.Object diff --git a/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex new file mode 100644 index 000000000..a3f516ae7 --- /dev/null +++ b/lib/pleroma/web/activity_pub/mrf/hellthread_policy.ex @@ -0,0 +1,22 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + +defmodule Pleroma.Web.ActivityPub.MRF.HellthreadPolicy do + @behaviour Pleroma.Web.ActivityPub.MRF + + @impl true + def filter(%{"type" => "Create"} = object) do + threshold = Pleroma.Config.get([:mrf_hellthread, :threshold]) + recipients = (object["to"] || []) ++ (object["cc"] || []) + + if length(recipients) > threshold do + {:reject, nil} + else + {:ok, object} + end + end + + @impl true + def filter(object), do: {:ok, object} +end diff --git a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex index e26f60d26..40f37bdb1 100644 --- a/lib/pleroma/web/activity_pub/mrf/noop_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/noop_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex index c53cb1ad2..3d13cdb32 100644 --- a/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex +++ b/lib/pleroma/web/activity_pub/mrf/normalize_markup.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.NormalizeMarkup do alias Pleroma.HTML diff --git a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex index 627284083..4197be847 100644 --- a/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex +++ b/lib/pleroma/web/activity_pub/mrf/reject_non_public.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.RejectNonPublic do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 12fc3b181..798ba9687 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do alias Pleroma.User @behaviour Pleroma.Web.ActivityPub.MRF diff --git a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex index 3503d8692..a3b1f8aa0 100644 --- a/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex +++ b/lib/pleroma/web/activity_pub/mrf/user_allowlist.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.MRF.UserAllowListPolicy do alias Pleroma.Config diff --git a/lib/pleroma/web/activity_pub/relay.ex b/lib/pleroma/web/activity_pub/relay.ex index fcdc6b1c0..abddbc790 100644 --- a/lib/pleroma/web/activity_pub/relay.ex +++ b/lib/pleroma/web/activity_pub/relay.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Relay do alias Pleroma.{User, Object, Activity} alias Pleroma.Web.ActivityPub.ActivityPub diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index e6af4b211..87b7fc07f 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Transmogrifier do @moduledoc """ A module to handle coding from internal to wire ActivityPub and back. @@ -69,8 +73,8 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def fix_object(object) do object |> fix_actor - |> fix_attachments |> fix_url + |> fix_attachments |> fix_context |> fix_in_reply_to |> fix_emoji @@ -170,8 +174,14 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do attachments = attachment |> Enum.map(fn data -> - url = [%{"type" => "Link", "mediaType" => data["mediaType"], "href" => data["url"]}] - Map.put(data, "url", url) + media_type = data["mediaType"] || data["mimeType"] + href = data["url"] || data["href"] + + url = [%{"type" => "Link", "mediaType" => media_type, "href" => href}] + + data + |> Map.put("mediaType", media_type) + |> Map.put("url", url) end) object @@ -190,7 +200,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do |> Map.put("url", url["href"]) end - def fix_url(%{"url" => url} = object) when is_list(url) do + def fix_url(%{"type" => "Video", "url" => url} = object) when is_list(url) do + first_element = Enum.at(url, 0) + + link_element = + url + |> Enum.filter(fn x -> is_map(x) end) + |> Enum.filter(fn x -> x["mimeType"] == "text/html" end) + |> Enum.at(0) + + object + |> Map.put("attachment", [first_element]) + |> Map.put("url", link_element["href"]) + end + + def fix_url(%{"type" => object_type, "url" => url} = object) + when object_type != "Video" and is_list(url) do first_element = Enum.at(url, 0) url_string = diff --git a/lib/pleroma/web/activity_pub/utils.ex b/lib/pleroma/web/activity_pub/utils.ex index 074622f2b..b313996db 100644 --- a/lib/pleroma/web/activity_pub/utils.ex +++ b/lib/pleroma/web/activity_pub/utils.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.Utils do alias Pleroma.{Repo, Web, Object, Activity, User, Notification} alias Pleroma.Web.Router.Helpers diff --git a/lib/pleroma/web/activity_pub/views/object_view.ex b/lib/pleroma/web/activity_pub/views/object_view.ex index ff664636c..b5c9bf8d0 100644 --- a/lib/pleroma/web/activity_pub/views/object_view.ex +++ b/lib/pleroma/web/activity_pub/views/object_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.ObjectView do use Pleroma.Web, :view alias Pleroma.{Object, Activity} diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index 869934172..fe8248107 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ActivityPub.UserView do use Pleroma.Web, :view alias Pleroma.Web.Salmon @@ -172,6 +176,53 @@ defmodule Pleroma.Web.ActivityPub.UserView do end end + def render("inbox.json", %{user: user, max_id: max_qid}) do + params = %{ + "limit" => "10" + } + + params = + if max_qid != nil do + Map.put(params, "max_id", max_qid) + else + params + end + + activities = ActivityPub.fetch_activities([user.ap_id | user.following], params) + + min_id = Enum.at(Enum.reverse(activities), 0).id + max_id = Enum.at(activities, 0).id + + collection = + Enum.map(activities, fn act -> + {:ok, data} = Transmogrifier.prepare_outgoing(act.data) + data + end) + + iri = "#{user.ap_id}/inbox" + + page = %{ + "id" => "#{iri}?max_id=#{max_id}", + "type" => "OrderedCollectionPage", + "partOf" => iri, + "totalItems" => -1, + "orderedItems" => collection, + "next" => "#{iri}?max_id=#{min_id - 1}" + } + + if max_qid == nil do + %{ + "id" => iri, + "type" => "OrderedCollection", + "totalItems" => -1, + "first" => page + } + |> Map.merge(Utils.make_json_ld_header()) + else + page |> Map.merge(Utils.make_json_ld_header()) + end + end + def collection(collection, iri, page, show_items \\ true, total \\ nil) do offset = (page - 1) * 10 items = Enum.slice(collection, offset, 10) diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index 4d73cf219..dc01f46f3 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -1,6 +1,10 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.AdminAPI.AdminAPIController do use Pleroma.Web, :controller - alias Pleroma.{User, Repo} + alias Pleroma.User alias Pleroma.Web.ActivityPub.Relay import Pleroma.Web.ControllerHelper, only: [json_response: 3] @@ -10,13 +14,8 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do action_fallback(:errors) def user_delete(conn, %{"nickname" => nickname}) do - user = User.get_by_nickname(nickname) - - if user.local == true do - User.delete(user) - else - User.delete(user) - end + User.get_by_nickname(nickname) + |> User.delete() conn |> json(nickname) @@ -26,7 +25,7 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do conn, %{"nickname" => nickname, "email" => email, "password" => password} ) do - new_user = %{ + user_data = %{ nickname: nickname, name: nickname, email: email, @@ -35,11 +34,11 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do bio: "." } - User.register_changeset(%User{}, new_user) - |> Repo.insert!() + changeset = User.register_changeset(%User{}, user_data, confirmed: true) + {:ok, user} = User.register(changeset) conn - |> json(new_user.nickname) + |> json(user.nickname) end def tag_users(conn, %{"nicknames" => nicknames, "tags" => tags}) do diff --git a/lib/pleroma/web/channels/user_socket.ex b/lib/pleroma/web/channels/user_socket.ex index 9918d3b49..aed8475fd 100644 --- a/lib/pleroma/web/channels/user_socket.ex +++ b/lib/pleroma/web/channels/user_socket.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.UserSocket do use Phoenix.Socket alias Pleroma.User diff --git a/lib/pleroma/web/chat_channel.ex b/lib/pleroma/web/chat_channel.ex index 37eba8c3f..fe63ede66 100644 --- a/lib/pleroma/web/chat_channel.ex +++ b/lib/pleroma/web/chat_channel.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ChatChannel do use Phoenix.Channel alias Pleroma.Web.ChatChannel.ChatChannelState diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index f01d36370..2902905fd 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.CommonAPI do alias Pleroma.{User, Repo, Activity, Object} alias Pleroma.Web.ActivityPub.ActivityPub @@ -10,6 +14,7 @@ defmodule Pleroma.Web.CommonAPI do with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id), %Object{} = object <- Object.normalize(object_id), true <- user.info.is_moderator || user.ap_id == object.data["actor"], + {:ok, _} <- unpin(activity_id, user), {:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} end @@ -98,7 +103,7 @@ defmodule Pleroma.Web.CommonAPI do attachments, tags, get_content_type(data["content_type"]), - data["no_attachment_links"] + Enum.member?([true, "true"], data["no_attachment_links"]) ), context <- make_context(inReplyTo), cw <- data["spoiler_text"], @@ -120,7 +125,7 @@ defmodule Pleroma.Web.CommonAPI do Map.put( object, "emoji", - Formatter.get_emoji(status) + (Formatter.get_emoji(status) ++ Formatter.get_emoji(data["spoiler_text"])) |> Enum.reduce(%{}, fn {name, file}, acc -> Map.put(acc, name, "#{Pleroma.Web.Endpoint.static_url()}#{file}") end) @@ -160,4 +165,48 @@ defmodule Pleroma.Web.CommonAPI do object: Pleroma.Web.ActivityPub.UserView.render("user.json", %{user: user}) }) end + + def pin(id_or_ap_id, %{ap_id: user_ap_id} = user) do + with %Activity{ + actor: ^user_ap_id, + data: %{ + "type" => "Create", + "object" => %{ + "to" => object_to, + "type" => "Note" + } + } + } = activity <- get_by_id_or_ap_id(id_or_ap_id), + true <- Enum.member?(object_to, "https://www.w3.org/ns/activitystreams#Public"), + %{valid?: true} = info_changeset <- + Pleroma.User.Info.add_pinnned_activity(user.info, activity), + changeset <- + Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset), + {:ok, _user} <- User.update_and_set_cache(changeset) do + {:ok, activity} + else + %{errors: [pinned_activities: {err, _}]} -> + {:error, err} + + _ -> + {:error, "Could not pin"} + end + end + + def unpin(id_or_ap_id, user) do + with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id), + %{valid?: true} = info_changeset <- + Pleroma.User.Info.remove_pinnned_activity(user.info, activity), + changeset <- + Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_changeset), + {:ok, _user} <- User.update_and_set_cache(changeset) do + {:ok, activity} + else + %{errors: [pinned_activities: {err, _}]} -> + {:error, err} + + _ -> + {:error, "Could not unpin"} + end + end end diff --git a/lib/pleroma/web/common_api/utils.ex b/lib/pleroma/web/common_api/utils.ex index 142283684..7e30d224c 100644 --- a/lib/pleroma/web/common_api/utils.ex +++ b/lib/pleroma/web/common_api/utils.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.CommonAPI.Utils do alias Calendar.Strftime alias Comeonin.Pbkdf2 @@ -132,7 +136,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do def format_input(text, mentions, _tags, "text/html") do text |> Formatter.html_escape("text/html") - |> String.replace(~r/\r?\n/, "<br>") |> (&{[], &1}).() |> Formatter.add_user_links(mentions) |> Formatter.finalize() @@ -146,7 +149,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do |> Formatter.mentions_escape(mentions) |> Earmark.as_html!() |> Formatter.html_escape("text/html") - |> String.replace(~r/\r?\n/, "") |> (&{[], &1}).() |> Formatter.add_user_links(mentions) |> Formatter.add_hashtag_links(tags) diff --git a/lib/pleroma/web/controller_helper.ex b/lib/pleroma/web/controller_helper.ex index ddf958811..14e3d19fd 100644 --- a/lib/pleroma/web/controller_helper.ex +++ b/lib/pleroma/web/controller_helper.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ControllerHelper do use Pleroma.Web, :controller diff --git a/lib/pleroma/web/endpoint.ex b/lib/pleroma/web/endpoint.ex index d79f61b2e..0b4ce9cc4 100644 --- a/lib/pleroma/web/endpoint.ex +++ b/lib/pleroma/web/endpoint.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Endpoint do use Phoenix.Endpoint, otp_app: :pleroma @@ -21,7 +25,7 @@ defmodule Pleroma.Web.Endpoint do at: "/", from: :pleroma, only: - ~w(index.html static finmoji emoji packs sounds images instance sw.js favicon.png schemas) + ~w(index.html static finmoji emoji packs sounds images instance sw.js favicon.png schemas doc) ) # Code reloading can be explicitly enabled under the diff --git a/lib/pleroma/web/federator/federator.ex b/lib/pleroma/web/federator/federator.ex index a9c7aecd5..f3a0e18b8 100644 --- a/lib/pleroma/web/federator/federator.ex +++ b/lib/pleroma/web/federator/federator.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Federator do use GenServer alias Pleroma.User @@ -13,7 +17,6 @@ defmodule Pleroma.Web.Federator do @websub Application.get_env(:pleroma, :websub) @ostatus Application.get_env(:pleroma, :ostatus) - @max_jobs 20 def init(args) do {:ok, args} @@ -164,7 +167,7 @@ defmodule Pleroma.Web.Federator do end def maybe_start_job(running_jobs, queue) do - if :sets.size(running_jobs) < @max_jobs && queue != [] do + if :sets.size(running_jobs) < Pleroma.Config.get([__MODULE__, :max_jobs]) && queue != [] do {{type, payload}, queue} = queue_pop(queue) {:ok, pid} = Task.start(fn -> handle(type, payload) end) mref = Process.monitor(pid) diff --git a/lib/pleroma/web/federator/retry_queue.ex b/lib/pleroma/web/federator/retry_queue.ex index 510b4315d..e0ce251d2 100644 --- a/lib/pleroma/web/federator/retry_queue.ex +++ b/lib/pleroma/web/federator/retry_queue.ex @@ -1,22 +1,34 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Federator.RetryQueue do use GenServer require Logger - # initial timeout, 5 min - @initial_timeout 30_000 - @max_retries 5 - def init(args) do - {:ok, args} + queue_table = :ets.new(:pleroma_retry_queue, [:bag, :protected]) + + {:ok, %{args | queue_table: queue_table, running_jobs: :sets.new()}} end def start_link() do - enabled = Pleroma.Config.get([:retry_queue, :enabled], false) + enabled = + if Mix.env() == :test, do: true, else: Pleroma.Config.get([__MODULE__, :enabled], false) if enabled do Logger.info("Starting retry queue") - GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__) + + linkres = + GenServer.start_link( + __MODULE__, + %{delivered: 0, dropped: 0, queue_table: nil, running_jobs: nil}, + name: __MODULE__ + ) + + maybe_kickoff_timer() + linkres else Logger.info("Retry queue disabled") :ignore @@ -27,24 +39,133 @@ defmodule Pleroma.Web.Federator.RetryQueue do GenServer.cast(__MODULE__, {:maybe_enqueue, data, transport, retries + 1}) end + def get_stats() do + GenServer.call(__MODULE__, :get_stats) + end + + def reset_stats() do + GenServer.call(__MODULE__, :reset_stats) + end + def get_retry_params(retries) do - if retries > @max_retries do + if retries > Pleroma.Config.get([__MODULE__, :max_retries]) do {:drop, "Max retries reached"} else {:retry, growth_function(retries)} end end - def handle_cast({:maybe_enqueue, data, transport, retries}, %{dropped: drop_count} = state) do + def get_retry_timer_interval() do + Pleroma.Config.get([:retry_queue, :interval], 1000) + end + + defp ets_count_expires(table, current_time) do + :ets.select_count( + table, + [ + { + {:"$1", :"$2"}, + [{:"=<", :"$1", {:const, current_time}}], + [true] + } + ] + ) + end + + defp ets_pop_n_expired(table, current_time, desired) do + {popped, _continuation} = + :ets.select( + table, + [ + { + {:"$1", :"$2"}, + [{:"=<", :"$1", {:const, current_time}}], + [:"$_"] + } + ], + desired + ) + + popped + |> Enum.each(fn e -> + :ets.delete_object(table, e) + end) + + popped + end + + def maybe_start_job(running_jobs, queue_table) do + # we don't want to hit the ets or the DateTime more times than we have to + # could optimize slightly further by not using the count, and instead grabbing + # up to N objects early... + current_time = DateTime.to_unix(DateTime.utc_now()) + n_running_jobs = :sets.size(running_jobs) + + if n_running_jobs < Pleroma.Config.get([__MODULE__, :max_jobs]) do + n_ready_jobs = ets_count_expires(queue_table, current_time) + + if n_ready_jobs > 0 do + # figure out how many we could start + available_job_slots = Pleroma.Config.get([__MODULE__, :max_jobs]) - n_running_jobs + start_n_jobs(running_jobs, queue_table, current_time, available_job_slots) + else + running_jobs + end + else + running_jobs + end + end + + defp start_n_jobs(running_jobs, _queue_table, _current_time, 0) do + running_jobs + end + + defp start_n_jobs(running_jobs, queue_table, current_time, available_job_slots) + when available_job_slots > 0 do + candidates = ets_pop_n_expired(queue_table, current_time, available_job_slots) + + candidates + |> List.foldl(running_jobs, fn {_, e}, rj -> + {:ok, pid} = Task.start(fn -> worker(e) end) + mref = Process.monitor(pid) + :sets.add_element(mref, rj) + end) + end + + def worker({:send, data, transport, retries}) do + case transport.publish_one(data) do + {:ok, _} -> + GenServer.cast(__MODULE__, :inc_delivered) + :delivered + + {:error, _reason} -> + enqueue(data, transport, retries) + :retry + end + end + + def handle_call(:get_stats, _from, %{delivered: delivery_count, dropped: drop_count} = state) do + {:reply, %{delivered: delivery_count, dropped: drop_count}, state} + end + + def handle_call(:reset_stats, _from, %{delivered: delivery_count, dropped: drop_count} = state) do + {:reply, %{delivered: delivery_count, dropped: drop_count}, + %{state | delivered: 0, dropped: 0}} + end + + def handle_cast(:reset_stats, state) do + {:noreply, %{state | delivered: 0, dropped: 0}} + end + + def handle_cast( + {:maybe_enqueue, data, transport, retries}, + %{dropped: drop_count, queue_table: queue_table, running_jobs: running_jobs} = state + ) do case get_retry_params(retries) do {:retry, timeout} -> - Process.send_after( - __MODULE__, - {:send, data, transport, retries}, - timeout - ) - - {:noreply, state} + :ets.insert(queue_table, {timeout, {:send, data, transport, retries}}) + running_jobs = maybe_start_job(running_jobs, queue_table) + {:noreply, %{state | running_jobs: running_jobs}} {:drop, message} -> Logger.debug(message) @@ -52,6 +173,20 @@ defmodule Pleroma.Web.Federator.RetryQueue do end end + def handle_cast(:kickoff_timer, state) do + retry_interval = get_retry_timer_interval() + Process.send_after(__MODULE__, :retry_timer_run, retry_interval) + {:noreply, state} + end + + def handle_cast(:inc_delivered, %{delivered: delivery_count} = state) do + {:noreply, %{state | delivered: delivery_count + 1}} + end + + def handle_cast(:inc_dropped, %{dropped: drop_count} = state) do + {:noreply, %{state | dropped: drop_count + 1}} + end + def handle_info({:send, data, transport, retries}, %{delivered: delivery_count} = state) do case transport.publish_one(data) do {:ok, _} -> @@ -63,12 +198,40 @@ defmodule Pleroma.Web.Federator.RetryQueue do end end + def handle_info( + :retry_timer_run, + %{queue_table: queue_table, running_jobs: running_jobs} = state + ) do + maybe_kickoff_timer() + running_jobs = maybe_start_job(running_jobs, queue_table) + {:noreply, %{state | running_jobs: running_jobs}} + end + + def handle_info({:DOWN, ref, :process, _pid, _reason}, state) do + %{running_jobs: running_jobs, queue_table: queue_table} = state + running_jobs = :sets.del_element(ref, running_jobs) + running_jobs = maybe_start_job(running_jobs, queue_table) + {:noreply, %{state | running_jobs: running_jobs}} + end + def handle_info(unknown, state) do Logger.debug("RetryQueue: don't know what to do with #{inspect(unknown)}, ignoring") {:noreply, state} end - defp growth_function(retries) do - round(@initial_timeout * :math.pow(retries, 3)) + if Mix.env() == :test do + defp growth_function(_retries) do + _shutit = Pleroma.Config.get([__MODULE__, :initial_timeout]) + DateTime.to_unix(DateTime.utc_now()) - 1 + end + else + defp growth_function(retries) do + round(Pleroma.Config.get([__MODULE__, :initial_timeout]) * :math.pow(retries, 3)) + + DateTime.to_unix(DateTime.utc_now()) + end + end + + defp maybe_kickoff_timer() do + GenServer.cast(__MODULE__, :kickoff_timer) end end diff --git a/lib/pleroma/web/gettext.ex b/lib/pleroma/web/gettext.ex index 501545581..1328b46cc 100644 --- a/lib/pleroma/web/gettext.ex +++ b/lib/pleroma/web/gettext.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Gettext do @moduledoc """ A module providing Internationalization with a gettext-based API. diff --git a/lib/pleroma/web/http_signatures/http_signatures.ex b/lib/pleroma/web/http_signatures/http_signatures.ex index 0e54debd5..e81f9e27a 100644 --- a/lib/pleroma/web/http_signatures/http_signatures.ex +++ b/lib/pleroma/web/http_signatures/http_signatures.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + # https://tools.ietf.org/html/draft-cavage-http-signatures-08 defmodule Pleroma.Web.HTTPSignatures do alias Pleroma.User diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 665b75437..e00a3fb87 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do use Pleroma.Web, :controller alias Pleroma.{Repo, Object, Activity, User, Notification, Stats} @@ -110,7 +114,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def user(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do - with %User{} = user <- Repo.get(User, id) do + with %User{} = user <- Repo.get(User, id), + true <- User.auth_active?(user) || user.id == for_user.id || User.superuser?(for_user) do account = AccountView.render("account.json", %{user: user, for: for_user}) json(conn, account) else @@ -251,13 +256,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do with %User{} = user <- Repo.get(User, params["id"]) do - # Since Pleroma has no "pinned" posts feature, we'll just set an empty list here - activities = - if params["pinned"] == "true" do - [] - else - ActivityPub.fetch_user_activities(user, reading_user, params) - end + activities = ActivityPub.fetch_user_activities(user, reading_user, params) conn |> add_link_headers(:user_statuses, activities, params["id"]) @@ -404,6 +403,27 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end + def pin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do + with {:ok, activity} <- CommonAPI.pin(ap_id_or_id, user) do + conn + |> put_view(StatusView) + |> try_render("status.json", %{activity: activity, for: user, as: :activity}) + else + {:error, reason} -> + conn + |> put_resp_content_type("application/json") + |> send_resp(:bad_request, Jason.encode!(%{"error" => reason})) + end + end + + def unpin_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do + with {:ok, activity} <- CommonAPI.unpin(ap_id_or_id, user) do + conn + |> put_view(StatusView) + |> try_render("status.json", %{activity: activity, for: user, as: :activity}) + end + end + def notifications(%{assigns: %{user: user}} = conn, params) do notifications = Notification.for_user(user, params) @@ -699,11 +719,9 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - # TODO: Use proper query def blocks(%{assigns: %{user: user}} = conn, _) do - with blocked_users <- user.info.blocks || [], - accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do - res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) + with blocked_accounts <- User.blocked_users(user) do + res = AccountView.render("accounts.json", users: blocked_accounts, for: user, as: :user) json(conn, res) end end @@ -722,11 +740,14 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do json(conn, %{}) end - def status_search(query) do + def status_search(user, query) do fetched = if Regex.match?(~r/https?:/, query) do - with {:ok, object} <- ActivityPub.fetch_object_from_id(query) do - [Activity.get_create_activity_by_object_ap_id(object.data["id"])] + with {:ok, object} <- ActivityPub.fetch_object_from_id(query), + %Activity{} = activity <- + Activity.get_create_activity_by_object_ap_id(object.data["id"]), + true <- ActivityPub.visible_for_user?(activity, user) do + [activity] else _e -> [] end @@ -753,7 +774,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def search2(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = User.search(query, params["resolve"] == "true") - statuses = status_search(query) + statuses = status_search(user, query) tags_path = Web.base_url() <> "/tag/" @@ -777,7 +798,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do def search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) do accounts = User.search(query, params["resolve"] == "true") - statuses = status_search(query) + statuses = status_search(user, query) tags = String.split(query) @@ -961,7 +982,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do max_toot_chars: limit }, rights: %{ - delete_others_notice: !!user.info.is_moderator + delete_others_notice: !!user.info.is_moderator, + admin: !!user.info.is_admin }, compose: %{ me: "#{user.id}", diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index ebcf9230b..bfd6b8b22 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.AccountView do use Pleroma.Web, :view alias Pleroma.User @@ -7,10 +11,55 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do alias Pleroma.HTML def render("accounts.json", %{users: users} = opts) do - render_many(users, AccountView, "account.json", opts) + users + |> render_many(AccountView, "account.json", opts) + |> Enum.filter(&Enum.any?/1) end def render("account.json", %{user: user} = opts) do + if User.visible_for?(user, opts[:for]), + do: do_render("account.json", opts), + else: %{} + end + + def render("mention.json", %{user: user}) do + %{ + id: to_string(user.id), + acct: user.nickname, + username: username_from_nickname(user.nickname), + url: user.ap_id + } + end + + def render("relationship.json", %{user: user, target: target}) do + follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target) + + requested = + if follow_activity do + follow_activity.data["state"] == "pending" + else + false + end + + %{ + id: to_string(target.id), + following: User.following?(user, target), + followed_by: User.following?(target, user), + blocking: User.blocks?(user, target), + muting: false, + muting_notifications: false, + requested: requested, + domain_blocking: false, + showing_reblogs: false, + endorsed: false + } + end + + def render("relationships.json", %{user: user, targets: targets}) do + render_many(targets, AccountView, "relationship.json", user: user, as: :target) + end + + defp do_render("account.json", %{user: user} = opts) do image = User.avatar_url(user) |> MediaProxy.url() header = User.banner_url(user) |> MediaProxy.url() user_info = User.user_info(user) @@ -62,48 +111,12 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do # Pleroma extension pleroma: %{ + confirmation_pending: user_info.confirmation_pending, tags: user.tags } } end - def render("mention.json", %{user: user}) do - %{ - id: to_string(user.id), - acct: user.nickname, - username: username_from_nickname(user.nickname), - url: user.ap_id - } - end - - def render("relationship.json", %{user: user, target: target}) do - follow_activity = Pleroma.Web.ActivityPub.Utils.fetch_latest_follow(user, target) - - requested = - if follow_activity do - follow_activity.data["state"] == "pending" - else - false - end - - %{ - id: to_string(target.id), - following: User.following?(user, target), - followed_by: User.following?(target, user), - blocking: User.blocks?(user, target), - muting: false, - muting_notifications: false, - requested: requested, - domain_blocking: false, - showing_reblogs: false, - endorsed: false - } - end - - def render("relationships.json", %{user: user, targets: targets}) do - render_many(targets, AccountView, "relationship.json", user: user, as: :target) - end - defp username_from_nickname(string) when is_binary(string) do hd(String.split(string, "@")) end diff --git a/lib/pleroma/web/mastodon_api/views/filter_view.ex b/lib/pleroma/web/mastodon_api/views/filter_view.ex index 6bd687d46..1052a449d 100644 --- a/lib/pleroma/web/mastodon_api/views/filter_view.ex +++ b/lib/pleroma/web/mastodon_api/views/filter_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.FilterView do use Pleroma.Web, :view alias Pleroma.Web.MastodonAPI.FilterView diff --git a/lib/pleroma/web/mastodon_api/views/list_view.ex b/lib/pleroma/web/mastodon_api/views/list_view.ex index 1a1b7430b..0f86e2512 100644 --- a/lib/pleroma/web/mastodon_api/views/list_view.ex +++ b/lib/pleroma/web/mastodon_api/views/list_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.ListView do use Pleroma.Web, :view alias Pleroma.Web.MastodonAPI.ListView diff --git a/lib/pleroma/web/mastodon_api/views/mastodon_view.ex b/lib/pleroma/web/mastodon_api/views/mastodon_view.ex index 1fd05d9f1..33b9a74be 100644 --- a/lib/pleroma/web/mastodon_api/views/mastodon_view.ex +++ b/lib/pleroma/web/mastodon_api/views/mastodon_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.MastodonView do use Pleroma.Web, :view import Phoenix.HTML diff --git a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex index 67e86294e..e86b789c5 100644 --- a/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex +++ b/lib/pleroma/web/mastodon_api/views/push_subscription_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do use Pleroma.Web, :view diff --git a/lib/pleroma/web/mastodon_api/views/status_view.ex b/lib/pleroma/web/mastodon_api/views/status_view.ex index 46c559e3a..db543ffe5 100644 --- a/lib/pleroma/web/mastodon_api/views/status_view.ex +++ b/lib/pleroma/web/mastodon_api/views/status_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.StatusView do use Pleroma.Web, :view @@ -72,6 +76,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: false, favourited: false, muted: false, + pinned: pinned?(activity, user), sensitive: false, spoiler_text: "", visibility: "public", @@ -106,7 +111,6 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) attachment_data = object["attachment"] || [] - attachment_data = attachment_data ++ if object["type"] == "Video", do: [object], else: [] attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment) created_at = Utils.to_masto_date(object["published"]) @@ -117,7 +121,11 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do content = object |> render_content() - |> HTML.filter_tags(User.html_filter_policy(opts[:for])) + |> HTML.get_cached_scrubbed_html_for_object( + User.html_filter_policy(opts[:for]), + activity, + __MODULE__ + ) %{ id: to_string(activity.id), @@ -135,6 +143,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do reblogged: present?(repeated), favourited: present?(favorited), muted: false, + pinned: pinned?(activity, user), sensitive: sensitive, spoiler_text: object["summary"] || "", visibility: get_visibility(object), @@ -288,4 +297,7 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do defp present?(nil), do: false defp present?(false), do: false defp present?(_), do: true + + defp pinned?(%Activity{id: id}, %User{info: %{pinned_activities: pinned_activities}}), + do: id in pinned_activities end diff --git a/lib/pleroma/web/mastodon_api/websocket_handler.ex b/lib/pleroma/web/mastodon_api/websocket_handler.ex index 11e0e1696..c0254c8e6 100644 --- a/lib/pleroma/web/mastodon_api/websocket_handler.ex +++ b/lib/pleroma/web/mastodon_api/websocket_handler.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MastodonAPI.WebsocketHandler do require Logger diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex index 63140feb9..de79cad73 100644 --- a/lib/pleroma/web/media_proxy/controller.ex +++ b/lib/pleroma/web/media_proxy/controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy.MediaProxyController do use Pleroma.Web, :controller alias Pleroma.{Web.MediaProxy, ReverseProxy} diff --git a/lib/pleroma/web/media_proxy/media_proxy.ex b/lib/pleroma/web/media_proxy/media_proxy.ex index 902ab1b77..e1eb1472d 100644 --- a/lib/pleroma/web/media_proxy/media_proxy.ex +++ b/lib/pleroma/web/media_proxy/media_proxy.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.MediaProxy do @base64_opts [padding: false] diff --git a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex index 44c11f40a..11b97164d 100644 --- a/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex +++ b/lib/pleroma/web/nodeinfo/nodeinfo_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Nodeinfo.NodeinfoController do use Pleroma.Web, :controller @@ -132,8 +136,10 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do banner: Keyword.get(instance, :banner_upload_limit), background: Keyword.get(instance, :background_upload_limit) }, + accountActivationRequired: Keyword.get(instance, :account_activation_required, false), invitesEnabled: Keyword.get(instance, :invites_enabled, false), - features: features + features: features, + restrictedNicknames: Pleroma.Config.get([Pleroma.User, :restricted_nicknames]) } } diff --git a/lib/pleroma/web/oauth/app.ex b/lib/pleroma/web/oauth/app.ex index b3273bc6e..967ac04b5 100644 --- a/lib/pleroma/web/oauth/app.ex +++ b/lib/pleroma/web/oauth/app.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.App do use Ecto.Schema import Ecto.{Changeset} diff --git a/lib/pleroma/web/oauth/authorization.ex b/lib/pleroma/web/oauth/authorization.ex index 2cad4550a..cc4b74bc5 100644 --- a/lib/pleroma/web/oauth/authorization.ex +++ b/lib/pleroma/web/oauth/authorization.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.Authorization do use Ecto.Schema diff --git a/lib/pleroma/web/oauth/fallback_controller.ex b/lib/pleroma/web/oauth/fallback_controller.ex index 3927cdb64..1eeda3d24 100644 --- a/lib/pleroma/web/oauth/fallback_controller.ex +++ b/lib/pleroma/web/oauth/fallback_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.FallbackController do use Pleroma.Web, :controller alias Pleroma.Web.OAuth.OAuthController diff --git a/lib/pleroma/web/oauth/oauth_controller.ex b/lib/pleroma/web/oauth/oauth_controller.ex index 20c2e799b..4d4e85836 100644 --- a/lib/pleroma/web/oauth/oauth_controller.ex +++ b/lib/pleroma/web/oauth/oauth_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.OAuthController do use Pleroma.Web, :controller @@ -31,6 +35,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do }) do with %User{} = user <- User.get_by_nickname_or_email(name), true <- Pbkdf2.checkpw(password, user.password_hash), + {:auth_active, true} <- {:auth_active, User.auth_active?(user)}, %App{} = app <- Repo.get_by(App, client_id: client_id), {:ok, auth} <- Authorization.create_authorization(app, user) do # Special case: Local MastodonFE. @@ -63,6 +68,15 @@ defmodule Pleroma.Web.OAuth.OAuthController do redirect(conn, external: url) end + else + {:auth_active, false} -> + conn + |> put_flash(:error, "Account confirmation pending") + |> put_status(:forbidden) + |> authorize(params) + + error -> + error end end @@ -101,6 +115,7 @@ defmodule Pleroma.Web.OAuth.OAuthController do with %App{} = app <- get_app_from_request(conn, params), %User{} = user <- User.get_by_nickname_or_email(name), true <- Pbkdf2.checkpw(password, user.password_hash), + {:auth_active, true} <- {:auth_active, User.auth_active?(user)}, {:ok, auth} <- Authorization.create_authorization(app, user), {:ok, token} <- Token.exchange_token(app, auth) do response = %{ @@ -113,6 +128,11 @@ defmodule Pleroma.Web.OAuth.OAuthController do json(conn, response) else + {:auth_active, false} -> + conn + |> put_status(:forbidden) + |> json(%{error: "Account confirmation pending"}) + _error -> put_status(conn, 400) |> json(%{error: "Invalid credentials"}) diff --git a/lib/pleroma/web/oauth/oauth_view.ex b/lib/pleroma/web/oauth/oauth_view.ex index b3923fcf5..9b37a91c5 100644 --- a/lib/pleroma/web/oauth/oauth_view.ex +++ b/lib/pleroma/web/oauth/oauth_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.OAuthView do use Pleroma.Web, :view import Phoenix.HTML.Form diff --git a/lib/pleroma/web/oauth/token.ex b/lib/pleroma/web/oauth/token.ex index a77d5af35..f0ebc63f6 100644 --- a/lib/pleroma/web/oauth/token.ex +++ b/lib/pleroma/web/oauth/token.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OAuth.Token do use Ecto.Schema diff --git a/lib/pleroma/web/ostatus/activity_representer.ex b/lib/pleroma/web/ostatus/activity_representer.ex index 537bd9f77..94b1a7ad1 100644 --- a/lib/pleroma/web/ostatus/activity_representer.ex +++ b/lib/pleroma/web/ostatus/activity_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.ActivityRepresenter do alias Pleroma.{Activity, User, Object} alias Pleroma.Web.OStatus.UserRepresenter diff --git a/lib/pleroma/web/ostatus/feed_representer.ex b/lib/pleroma/web/ostatus/feed_representer.ex index 279672673..934d4042f 100644 --- a/lib/pleroma/web/ostatus/feed_representer.ex +++ b/lib/pleroma/web/ostatus/feed_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.FeedRepresenter do alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.{UserRepresenter, ActivityRepresenter} diff --git a/lib/pleroma/web/ostatus/handlers/delete_handler.ex b/lib/pleroma/web/ostatus/handlers/delete_handler.ex index 6330d7f64..01b52f08f 100644 --- a/lib/pleroma/web/ostatus/handlers/delete_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/delete_handler.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.DeleteHandler do require Logger alias Pleroma.Web.XML diff --git a/lib/pleroma/web/ostatus/handlers/follow_handler.ex b/lib/pleroma/web/ostatus/handlers/follow_handler.ex index 162407e04..becdf2fbf 100644 --- a/lib/pleroma/web/ostatus/handlers/follow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/follow_handler.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.FollowHandler do alias Pleroma.Web.{XML, OStatus} alias Pleroma.Web.ActivityPub.ActivityPub diff --git a/lib/pleroma/web/ostatus/handlers/note_handler.ex b/lib/pleroma/web/ostatus/handlers/note_handler.ex index 0d4080291..5aeed46f0 100644 --- a/lib/pleroma/web/ostatus/handlers/note_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/note_handler.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.NoteHandler do require Logger alias Pleroma.Web.{XML, OStatus} diff --git a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex index a115bf4c8..1c64f3c3d 100644 --- a/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex +++ b/lib/pleroma/web/ostatus/handlers/unfollow_handler.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.UnfollowHandler do alias Pleroma.Web.{XML, OStatus} alias Pleroma.Web.ActivityPub.ActivityPub diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index c6440c20e..bb28cd786 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus do @httpoison Application.get_env(:pleroma, :httpoison) diff --git a/lib/pleroma/web/ostatus/ostatus_controller.ex b/lib/pleroma/web/ostatus/ostatus_controller.ex index 5dbee20e1..be648a6ee 100644 --- a/lib/pleroma/web/ostatus/ostatus_controller.ex +++ b/lib/pleroma/web/ostatus/ostatus_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.OStatusController do use Pleroma.Web, :controller @@ -112,23 +116,27 @@ defmodule Pleroma.Web.OStatus.OStatusController do end def activity(conn, %{"uuid" => uuid}) do - with id <- o_status_url(conn, :activity, uuid), - {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, - {_, true} <- {:public?, ActivityPub.is_public?(activity)}, - %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do - case format = get_format(conn) do - "html" -> redirect(conn, to: "/notice/#{activity.id}") - _ -> represent_activity(conn, format, activity, user) - end + if get_format(conn) == "activity+json" do + ActivityPubController.call(conn, :activity) else - {:public?, false} -> - {:error, :not_found} + with id <- o_status_url(conn, :activity, uuid), + {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, + {_, true} <- {:public?, ActivityPub.is_public?(activity)}, + %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do + case format = get_format(conn) do + "html" -> redirect(conn, to: "/notice/#{activity.id}") + _ -> represent_activity(conn, format, activity, user) + end + else + {:public?, false} -> + {:error, :not_found} - {:activity, nil} -> - {:error, :not_found} + {:activity, nil} -> + {:error, :not_found} - e -> - e + e -> + e + end end end diff --git a/lib/pleroma/web/ostatus/user_representer.ex b/lib/pleroma/web/ostatus/user_representer.ex index 2e696506e..852be6eb4 100644 --- a/lib/pleroma/web/ostatus/user_representer.ex +++ b/lib/pleroma/web/ostatus/user_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.OStatus.UserRepresenter do alias Pleroma.User diff --git a/lib/pleroma/web/push/push.ex b/lib/pleroma/web/push/push.ex index 477943450..ffd2aac91 100644 --- a/lib/pleroma/web/push/push.ex +++ b/lib/pleroma/web/push/push.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Push do use GenServer diff --git a/lib/pleroma/web/push/subscription.ex b/lib/pleroma/web/push/subscription.ex index 1ad405daf..82b30950c 100644 --- a/lib/pleroma/web/push/subscription.ex +++ b/lib/pleroma/web/push/subscription.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Push.Subscription do use Ecto.Schema import Ecto.Changeset diff --git a/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex b/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex new file mode 100644 index 000000000..91019961d --- /dev/null +++ b/lib/pleroma/web/rich_media/controllers/rich_media_controller.ex @@ -0,0 +1,17 @@ +defmodule Pleroma.Web.RichMedia.RichMediaController do + use Pleroma.Web, :controller + + import Pleroma.Web.ControllerHelper, only: [json_response: 3] + + def parse(conn, %{"url" => url}) do + case Pleroma.Web.RichMedia.Parser.parse(url) do + {:ok, data} -> + conn + |> json_response(200, data) + + {:error, msg} -> + conn + |> json_response(404, msg) + end + end +end diff --git a/lib/pleroma/web/rich_media/parser.ex b/lib/pleroma/web/rich_media/parser.ex new file mode 100644 index 000000000..fe092bf19 --- /dev/null +++ b/lib/pleroma/web/rich_media/parser.ex @@ -0,0 +1,33 @@ +defmodule Pleroma.Web.RichMedia.Parser do + @parsers [Pleroma.Web.RichMedia.Parsers.OGP, Pleroma.Web.RichMedia.Parsers.TwitterCard] + + if Mix.env() == :test do + def parse(url), do: parse_url(url) + else + def parse(url), + do: Cachex.fetch!(:rich_media_cache, url, fn _ -> parse_url(url) end) + end + + defp parse_url(url) do + {:ok, %Tesla.Env{body: html}} = Pleroma.HTTP.get(url) + + html |> maybe_parse() |> get_parsed_data() + end + + defp maybe_parse(html) do + Enum.reduce_while(@parsers, %{}, fn parser, acc -> + case parser.parse(html, acc) do + {:ok, data} -> {:halt, data} + {:error, _msg} -> {:cont, acc} + end + end) + end + + defp get_parsed_data(data) when data == %{} do + {:error, "No metadata found"} + end + + defp get_parsed_data(data) do + {:ok, data} + end +end diff --git a/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex new file mode 100644 index 000000000..4a7c5eae0 --- /dev/null +++ b/lib/pleroma/web/rich_media/parsers/meta_tags_parser.ex @@ -0,0 +1,30 @@ +defmodule Pleroma.Web.RichMedia.Parsers.MetaTagsParser do + def parse(html, data, prefix, error_message, key_name, value_name \\ "content") do + with elements = [_ | _] <- get_elements(html, key_name, prefix), + meta_data = + Enum.reduce(elements, data, fn el, acc -> + attributes = normalize_attributes(el, prefix, key_name, value_name) + + Map.merge(acc, attributes) + end) do + {:ok, meta_data} + else + _e -> {:error, error_message} + end + end + + defp get_elements(html, key_name, prefix) do + html |> Floki.find("meta[#{key_name}^='#{prefix}:']") + end + + defp normalize_attributes(html_node, prefix, key_name, value_name) do + {_tag, attributes, _children} = html_node + + data = + Enum.into(attributes, %{}, fn {name, value} -> + {name, String.trim_leading(value, "#{prefix}:")} + end) + + %{String.to_atom(data[key_name]) => data[value_name]} + end +end diff --git a/lib/pleroma/web/rich_media/parsers/ogp.ex b/lib/pleroma/web/rich_media/parsers/ogp.ex new file mode 100644 index 000000000..0e1a0e719 --- /dev/null +++ b/lib/pleroma/web/rich_media/parsers/ogp.ex @@ -0,0 +1,11 @@ +defmodule Pleroma.Web.RichMedia.Parsers.OGP do + def parse(html, data) do + Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse( + html, + data, + "og", + "No OGP metadata found", + "property" + ) + end +end diff --git a/lib/pleroma/web/rich_media/parsers/twitter_card.ex b/lib/pleroma/web/rich_media/parsers/twitter_card.ex new file mode 100644 index 000000000..a317c3e78 --- /dev/null +++ b/lib/pleroma/web/rich_media/parsers/twitter_card.ex @@ -0,0 +1,11 @@ +defmodule Pleroma.Web.RichMedia.Parsers.TwitterCard do + def parse(html, data) do + Pleroma.Web.RichMedia.Parsers.MetaTagsParser.parse( + html, + data, + "twitter", + "No twitter card metadata found", + "name" + ) + end +end diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 59784549d..5ef99bec5 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Router do use Pleroma.Web, :router @@ -133,6 +137,7 @@ defmodule Pleroma.Web.Router do scope "/api/pleroma", Pleroma.Web.TwitterAPI do pipe_through(:authenticated_api) + post("/blocks_import", UtilController, :blocks_import) post("/follow_import", UtilController, :follow_import) post("/change_password", UtilController, :change_password) post("/delete_account", UtilController, :delete_account) @@ -183,6 +188,8 @@ defmodule Pleroma.Web.Router do post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status) post("/statuses/:id/favourite", MastodonAPIController, :fav_status) post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status) + post("/statuses/:id/pin", MastodonAPIController, :pin_status) + post("/statuses/:id/unpin", MastodonAPIController, :unpin_status) post("/notifications/clear", MastodonAPIController, :clear_notifications) post("/notifications/dismiss", MastodonAPIController, :dismiss_notification) @@ -227,6 +234,12 @@ defmodule Pleroma.Web.Router do put("/settings", MastodonAPIController, :put_settings) end + scope "/api", Pleroma.Web.RichMedia do + pipe_through(:authenticated_api) + + get("/rich_media/parse", RichMediaController, :parse) + end + scope "/api/v1", Pleroma.Web.MastodonAPI do pipe_through(:api) get("/instance", MastodonAPIController, :masto_instance) @@ -277,12 +290,22 @@ defmodule Pleroma.Web.Router do get("/statuses/followers", TwitterAPI.Controller, :followers) get("/statuses/friends", TwitterAPI.Controller, :friends) + get("/statuses/blocks", TwitterAPI.Controller, :blocks) get("/statuses/show/:id", TwitterAPI.Controller, :fetch_status) get("/statusnet/conversation/:id", TwitterAPI.Controller, :fetch_conversation) post("/account/register", TwitterAPI.Controller, :register) post("/account/password_reset", TwitterAPI.Controller, :password_reset) + get( + "/account/confirm_email/:user_id/:token", + TwitterAPI.Controller, + :confirm_email, + as: :confirm_email + ) + + post("/account/resend_confirmation_email", TwitterAPI.Controller, :resend_confirmation_email) + get("/search", TwitterAPI.Controller, :search) get("/statusnet/tags/timeline/:tag", TwitterAPI.Controller, :public_and_external_timeline) end @@ -332,6 +355,9 @@ defmodule Pleroma.Web.Router do post("/statuses/unretweet/:id", TwitterAPI.Controller, :unretweet) post("/statuses/destroy/:id", TwitterAPI.Controller, :delete_post) + post("/statuses/pin/:id", TwitterAPI.Controller, :pin) + post("/statuses/unpin/:id", TwitterAPI.Controller, :unpin) + get("/pleroma/friend_requests", TwitterAPI.Controller, :friend_requests) post("/pleroma/friendships/approve", TwitterAPI.Controller, :approve_friend_request) post("/pleroma/friendships/deny", TwitterAPI.Controller, :deny_friend_request) @@ -407,6 +433,27 @@ defmodule Pleroma.Web.Router do get("/users/:nickname/outbox", ActivityPubController, :outbox) end + pipeline :activitypub_client do + plug(:accepts, ["activity+json"]) + plug(:fetch_session) + plug(Pleroma.Plugs.OAuthPlug) + plug(Pleroma.Plugs.BasicAuthDecoderPlug) + plug(Pleroma.Plugs.UserFetcherPlug) + plug(Pleroma.Plugs.SessionAuthenticationPlug) + plug(Pleroma.Plugs.LegacyAuthenticationPlug) + plug(Pleroma.Plugs.AuthenticationPlug) + plug(Pleroma.Plugs.UserEnabledPlug) + plug(Pleroma.Plugs.SetUserSessionIdPlug) + plug(Pleroma.Plugs.EnsureUserKeyPlug) + end + + scope "/", Pleroma.Web.ActivityPub do + pipe_through([:activitypub_client]) + + get("/users/:nickname/inbox", ActivityPubController, :read_inbox) + post("/users/:nickname/outbox", ActivityPubController, :update_outbox) + end + scope "/relay", Pleroma.Web.ActivityPub do pipe_through(:ap_relay) get("/", ActivityPubController, :relay) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index b67b1333f..e41657da1 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Salmon do @httpoison Application.get_env(:pleroma, :httpoison) @@ -157,16 +161,21 @@ defmodule Pleroma.Web.Salmon do |> Enum.filter(fn user -> user && !user.local end) end - defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do + # push an activity to remote accounts + # + defp send_to_user(%{info: %{salmon: salmon}}, feed, poster), + do: send_to_user(salmon, feed, poster) + + defp send_to_user(url, feed, poster) when is_binary(url) do with {:ok, %{status: code}} <- poster.( - salmon, + url, feed, [{"Content-Type", "application/magic-envelope+xml"}] ) do - Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end) + Logger.debug(fn -> "Pushed to #{url}, code #{code}" end) else - e -> Logger.debug(fn -> "Pushing Salmon to #{salmon} failed, #{inspect(e)}" end) + e -> Logger.debug(fn -> "Pushing Salmon to #{url} failed, #{inspect(e)}" end) end end @@ -180,6 +189,11 @@ defmodule Pleroma.Web.Salmon do "Undo", "Delete" ] + + @doc """ + Publishes an activity to remote accounts + """ + @spec publish(User.t(), Pleroma.Activity.t(), Pleroma.HTTP.t()) :: none def publish(user, activity, poster \\ &@httpoison.post/3) def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster) diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index e1eecba4d..3136b1b9d 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Streamer do use GenServer require Logger diff --git a/lib/pleroma/web/twitter_api/controllers/util_controller.ex b/lib/pleroma/web/twitter_api/controllers/util_controller.ex index 38653f0b8..a79072f3d 100644 --- a/lib/pleroma/web/twitter_api/controllers/util_controller.ex +++ b/lib/pleroma/web/twitter_api/controllers/util_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.UtilController do use Pleroma.Web, :controller require Logger @@ -174,6 +178,8 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"), private: if(Keyword.get(instance, :public, true), do: "0", else: "1"), vapidPublicKey: vapid_public_key, + accountActivationRequired: + if(Keyword.get(instance, :account_activation_required, false), do: "1", else: "0"), invitesEnabled: if(Keyword.get(instance, :invites_enabled, false), do: "1", else: "0") } @@ -234,21 +240,22 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do follow_import(conn, %{"list" => File.read!(listfile.path)}) end - def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do - Task.start(fn -> - String.split(list) - |> Enum.map(fn account -> - with %User{} = follower <- User.get_cached_by_ap_id(user.ap_id), - %User{} = followed <- User.get_or_fetch(account), - {:ok, follower} <- User.maybe_direct_follow(follower, followed) do - ActivityPub.follow(follower, followed) - else - err -> Logger.debug("follow_import: following #{account} failed with #{inspect(err)}") - end - end) - end) + def follow_import(%{assigns: %{user: follower}} = conn, %{"list" => list}) do + with followed_identifiers <- String.split(list), + {:ok, _} = Task.start(fn -> User.follow_import(follower, followed_identifiers) end) do + json(conn, "job started") + end + end + + def blocks_import(conn, %{"list" => %Plug.Upload{} = listfile}) do + blocks_import(conn, %{"list" => File.read!(listfile.path)}) + end - json(conn, "job started") + def blocks_import(%{assigns: %{user: blocker}} = conn, %{"list" => list}) do + with blocked_identifiers <- String.split(list), + {:ok, _} = Task.start(fn -> User.blocks_import(blocker, blocked_identifiers) end) do + json(conn, "job started") + end end def change_password(%{assigns: %{user: user}} = conn, params) do diff --git a/lib/pleroma/web/twitter_api/representers/activity_representer.ex b/lib/pleroma/web/twitter_api/representers/activity_representer.ex index 2808192b0..4f8f228ab 100644 --- a/lib/pleroma/web/twitter_api/representers/activity_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/activity_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + # THIS MODULE IS DEPRECATED! DON'T USE IT! # USE THE Pleroma.Web.TwitterAPI.Views.ActivityView MODULE! defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do @@ -149,6 +153,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) + pinned = activity.id in user.info.pinned_activities mentions = opts[:mentioned] || [] @@ -171,19 +176,14 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do HTML.filter_tags(content, User.html_filter_policy(opts[:for])) |> Formatter.emojify(object["emoji"]) - video = - if object["type"] == "Video" do - [object] - else - [] - end - - attachments = (object["attachment"] || []) ++ video + attachments = object["attachment"] || [] reply_parent = Activity.get_in_reply_to_activity(activity) reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor) + summary = HTML.strip_tags(object["summary"]) + %{ "id" => activity.id, "uri" => activity.data["object"]["id"], @@ -205,12 +205,14 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do "repeat_num" => announcement_count, "favorited" => to_boolean(favorited), "repeated" => to_boolean(repeated), + "pinned" => pinned, "external_url" => object["external_url"] || object["id"], "tags" => tags, "activity_type" => "post", "possibly_sensitive" => possibly_sensitive, "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object), - "summary" => object["summary"] + "summary" => summary, + "summary_html" => summary |> Formatter.emojify(object["emoji"]) } end diff --git a/lib/pleroma/web/twitter_api/representers/base_representer.ex b/lib/pleroma/web/twitter_api/representers/base_representer.ex index f32a21d47..3d31e6079 100644 --- a/lib/pleroma/web/twitter_api/representers/base_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/base_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.Representers.BaseRepresenter do defmacro __using__(_opts) do quote do diff --git a/lib/pleroma/web/twitter_api/representers/object_representer.ex b/lib/pleroma/web/twitter_api/representers/object_representer.ex index d5291a397..47130ba06 100644 --- a/lib/pleroma/web/twitter_api/representers/object_representer.ex +++ b/lib/pleroma/web/twitter_api/representers/object_representer.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do use Pleroma.Web.TwitterAPI.Representers.BaseRepresenter alias Pleroma.Object diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 90b8345c5..7a63724f1 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -1,8 +1,14 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.TwitterAPI do alias Pleroma.{UserInviteToken, User, Activity, Repo, Object} + alias Pleroma.{UserEmail, Mailer} alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.TwitterAPI.UserView alias Pleroma.Web.CommonAPI + import Ecto.Query def create_status(%User{} = user, %{"status" => _} = data) do @@ -76,6 +82,14 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do end end + def pin(%User{} = user, ap_id_or_id) do + CommonAPI.pin(ap_id_or_id, user) + end + + def unpin(%User{} = user, ap_id_or_id) do + CommonAPI.unpin(ap_id_or_id, user) + end + def fav(%User{} = user, ap_id_or_id) do with {:ok, _fav, %{data: %{"id" => id}}} <- CommonAPI.favorite(ap_id_or_id, user), %Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do @@ -134,22 +148,28 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do password: params["password"], password_confirmation: params["confirm"], captcha_solution: params["captcha_solution"], - captcha_token: params["captcha_token"] + captcha_token: params["captcha_token"], + captcha_answer_data: params["captcha_answer_data"] } captcha_enabled = Pleroma.Config.get([Pleroma.Captcha, :enabled]) # true if captcha is disabled or enabled and valid, false otherwise captcha_ok = if !captcha_enabled do - true + :ok else - Pleroma.Captcha.validate(params[:captcha_token], params[:captcha_solution]) + Pleroma.Captcha.validate( + params[:captcha_token], + params[:captcha_solution], + params[:captcha_answer_data] + ) end # Captcha invalid - if not captcha_ok do + if captcha_ok != :ok do + {:error, error} = captcha_ok # I have no idea how this error handling works - {:error, %{error: Jason.encode!(%{captcha: ["Invalid CAPTCHA"]})}} + {:error, %{error: Jason.encode!(%{captcha: [error]})}} else registrations_open = Pleroma.Config.get([:instance, :registrations_open]) @@ -161,10 +181,11 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do cond do registrations_open || (!is_nil(token) && !token.used) -> - changeset = User.register_changeset(%User{info: %{}}, params) + changeset = User.register_changeset(%User{}, params) - with {:ok, user} <- Repo.insert(changeset) do + with {:ok, user} <- User.register(changeset) do !registrations_open && UserInviteToken.mark_as_used(token.token) + {:ok, user} else {:error, changeset} -> @@ -189,8 +210,8 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do %User{local: true} = user <- User.get_by_nickname_or_email(nickname_or_email), {:ok, token_record} <- Pleroma.PasswordResetToken.create_token(user) do user - |> Pleroma.UserEmail.password_reset_email(token_record.token) - |> Pleroma.Mailer.deliver() + |> UserEmail.password_reset_email(token_record.token) + |> Mailer.deliver() else false -> {:error, "bad user identifier"} diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 327620302..1c728166c 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.Controller do use Pleroma.Web, :controller @@ -96,10 +100,15 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end def show_user(conn, params) do - with {:ok, shown} <- TwitterAPI.get_user(params) do + for_user = conn.assigns.user + + with {:ok, shown} <- TwitterAPI.get_user(params), + true <- + User.auth_active?(shown) || + (for_user && (for_user.id == shown.id || User.superuser?(for_user))) do params = - if user = conn.assigns.user do - %{user: shown, for: user} + if for_user do + %{user: shown, for: for_user} else %{user: shown} end @@ -110,12 +119,26 @@ defmodule Pleroma.Web.TwitterAPI.Controller do else {:error, msg} -> bad_request_reply(conn, msg) + + false -> + conn + |> put_status(404) + |> json(%{error: "Unconfirmed user"}) end end def user_timeline(%{assigns: %{user: user}} = conn, params) do case TwitterAPI.get_user(user, params) do {:ok, target_user} -> + # Twitter and ActivityPub use a different name and sense for this parameter. + {include_rts, params} = Map.pop(params, "include_rts") + + params = + case include_rts do + x when x == "false" or x == "0" -> Map.put(params, "exclude_reblogs", "true") + _ -> params + end + activities = ActivityPub.fetch_user_activities(target_user, user, params) conn @@ -352,6 +375,30 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end + def pin(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, + {:ok, activity} <- TwitterAPI.pin(user, id) do + conn + |> put_view(ActivityView) + |> render("activity.json", %{activity: activity, for: user}) + else + {:error, message} -> bad_request_reply(conn, message) + err -> err + end + end + + def unpin(%{assigns: %{user: user}} = conn, %{"id" => id}) do + with {_, {:ok, id}} <- {:param_cast, Ecto.Type.cast(:integer, id)}, + {:ok, activity} <- TwitterAPI.unpin(user, id) do + conn + |> put_view(ActivityView) + |> render("activity.json", %{activity: activity, for: user}) + else + {:error, message} -> bad_request_reply(conn, message) + err -> err + end + end + def register(conn, params) do with {:ok, user} <- TwitterAPI.register_user(params) do conn @@ -372,6 +419,29 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end + def confirm_email(conn, %{"user_id" => uid, "token" => token}) do + with %User{} = user <- Repo.get(User, uid), + true <- user.local, + true <- user.info.confirmation_pending, + true <- user.info.confirmation_token == token, + info_change <- User.Info.confirmation_changeset(user.info, :confirmed), + changeset <- Changeset.change(user) |> Changeset.put_embed(:info, info_change), + {:ok, _} <- User.update_and_set_cache(changeset) do + conn + |> redirect(to: "/") + end + end + + def resend_confirmation_email(conn, params) do + nickname_or_email = params["email"] || params["nickname"] + + with %User{} = user <- User.get_by_nickname_or_email(nickname_or_email), + {:ok, _} <- User.try_send_confirmation_email(user) do + conn + |> json_response(:no_content, "") + end + end + def update_avatar(%{assigns: %{user: user}} = conn, params) do {:ok, object} = ActivityPub.upload(params, type: :avatar) change = Changeset.change(user, %{avatar: object.data}) @@ -426,8 +496,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end def followers(%{assigns: %{user: for_user}} = conn, params) do + {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) + with {:ok, user} <- TwitterAPI.get_user(for_user, params), - {:ok, followers} <- User.get_followers(user) do + {:ok, followers} <- User.get_followers(user, page) do followers = cond do for_user && user.id == for_user.id -> followers @@ -444,8 +516,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end def friends(%{assigns: %{user: for_user}} = conn, params) do + {:ok, page} = Ecto.Type.cast(:integer, params["page"] || 1) + with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), - {:ok, friends} <- User.get_friends(user) do + {:ok, friends} <- User.get_friends(user, page) do friends = cond do for_user && user.id == for_user.id -> friends @@ -461,6 +535,14 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end + def blocks(%{assigns: %{user: user}} = conn, _params) do + with blocked_users <- User.blocked_users(user) do + conn + |> put_view(UserView) + |> render("index.json", %{users: blocked_users, for: user}) + end + end + def friend_requests(conn, params) do with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), {:ok, friend_requests} <- User.get_follow_requests(user) do @@ -616,7 +698,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do json_reply(conn, 403, json) end - def only_if_public_instance(conn = %{conn: %{assigns: %{user: _user}}}, _), do: conn + def only_if_public_instance(%{assigns: %{user: %User{}}} = conn, _), do: conn def only_if_public_instance(conn, _) do if Keyword.get(Application.get_env(:pleroma, :instance), :public) do diff --git a/lib/pleroma/web/twitter_api/views/activity_view.ex b/lib/pleroma/web/twitter_api/views/activity_view.ex index 91d086740..108e7bfc5 100644 --- a/lib/pleroma/web/twitter_api/views/activity_view.ex +++ b/lib/pleroma/web/twitter_api/views/activity_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.ActivityView do use Pleroma.Web, :view alias Pleroma.Web.CommonAPI.Utils @@ -7,11 +11,11 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter alias Pleroma.Activity + alias Pleroma.HTML alias Pleroma.Object alias Pleroma.User alias Pleroma.Repo alias Pleroma.Formatter - alias Pleroma.HTML import Ecto.Query require Logger @@ -90,11 +94,27 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do ap_id == "https://www.w3.org/ns/activitystreams#Public" -> nil + user = User.get_cached_by_ap_id(ap_id) -> + user + + user = User.get_by_guessed_nickname(ap_id) -> + user + true -> - User.get_cached_by_ap_id(ap_id) + error_user(ap_id) end end + defp error_user(ap_id) do + %User{ + name: ap_id, + ap_id: ap_id, + info: %User.Info{}, + nickname: "erroruser@example.com", + inserted_at: NaiveDateTime.utc_now() + } + end + def render("index.json", opts) do context_ids = collect_context_ids(opts.activities) users = collect_users(opts.activities) @@ -223,6 +243,7 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do announcement_count = object["announcement_count"] || 0 favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) + pinned = activity.id in user.info.pinned_activities attentions = activity.recipients @@ -241,18 +262,26 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do html = content - |> HTML.filter_tags(User.html_filter_policy(opts[:for])) + |> HTML.get_cached_scrubbed_html_for_object( + User.html_filter_policy(opts[:for]), + activity, + __MODULE__ + ) |> Formatter.emojify(object["emoji"]) text = - content - |> String.replace(~r/<br\s?\/?>/, "\n") - |> HTML.strip_tags() + if content do + content + |> String.replace(~r/<br\s?\/?>/, "\n") + |> HTML.get_cached_stripped_html_for_object(activity, __MODULE__) + end reply_parent = Activity.get_in_reply_to_activity(activity) reply_user = reply_parent && User.get_cached_by_ap_id(reply_parent.actor) + summary = HTML.strip_tags(summary) + %{ "id" => activity.id, "uri" => activity.data["object"]["id"], @@ -274,12 +303,14 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do "repeat_num" => announcement_count, "favorited" => !!favorited, "repeated" => !!repeated, + "pinned" => pinned, "external_url" => object["external_url"] || object["id"], "tags" => tags, "activity_type" => "post", "possibly_sensitive" => possibly_sensitive, "visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object), - "summary" => summary + "summary" => summary, + "summary_html" => summary |> Formatter.emojify(object["emoji"]) } end @@ -301,7 +332,8 @@ defmodule Pleroma.Web.TwitterAPI.ActivityView do {summary, content} end - def render_content(%{"type" => object_type} = object) when object_type in ["Article", "Page"] do + def render_content(%{"type" => object_type} = object) + when object_type in ["Article", "Page", "Video"] do summary = object["name"] || object["summary"] content = diff --git a/lib/pleroma/web/twitter_api/views/notification_view.ex b/lib/pleroma/web/twitter_api/views/notification_view.ex index 9eeb3afdc..d6a1c0a4d 100644 --- a/lib/pleroma/web/twitter_api/views/notification_view.ex +++ b/lib/pleroma/web/twitter_api/views/notification_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.NotificationView do use Pleroma.Web, :view alias Pleroma.{Notification, User} diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index 8a88d72b1..a8cf83613 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.UserView do use Pleroma.Web, :view alias Pleroma.User @@ -11,18 +15,44 @@ defmodule Pleroma.Web.TwitterAPI.UserView do end def render("index.json", %{users: users, for: user}) do - render_many(users, Pleroma.Web.TwitterAPI.UserView, "user.json", for: user) + users + |> render_many(Pleroma.Web.TwitterAPI.UserView, "user.json", for: user) + |> Enum.filter(&Enum.any?/1) end def render("user.json", %{user: user = %User{}} = assigns) do + if User.visible_for?(user, assigns[:for]), + do: do_render("user.json", assigns), + else: %{} + end + + def render("short.json", %{ + user: %User{ + nickname: nickname, + id: id, + ap_id: ap_id, + name: name + } + }) do + %{ + "fullname" => name, + "id" => id, + "ostatus_uri" => ap_id, + "profile_url" => ap_id, + "screen_name" => nickname + } + end + + defp do_render("user.json", %{user: user = %User{}} = assigns) do + for_user = assigns[:for] image = User.avatar_url(user) |> MediaProxy.url() {following, follows_you, statusnet_blocking} = - if assigns[:for] do + if for_user do { - User.following?(assigns[:for], user), - User.following?(user, assigns[:for]), - User.blocks?(assigns[:for], user) + User.following?(for_user, user), + User.following?(user, for_user), + User.blocks?(for_user, user) } else {false, false, false} @@ -47,7 +77,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do data = %{ "created_at" => user.inserted_at |> Utils.format_naive_asctime(), "description" => HTML.strip_tags((user.bio || "") |> String.replace("<br>", "\n")), - "description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(assigns[:for])), + "description_html" => HTML.filter_tags(user.bio, User.html_filter_policy(for_user)), "favourites_count" => 0, "followers_count" => user_info[:follower_count], "following" => following, @@ -66,7 +96,8 @@ defmodule Pleroma.Web.TwitterAPI.UserView do "profile_image_url_profile_size" => image, "profile_image_url_original" => image, "rights" => %{ - "delete_others_notice" => !!user.info.is_moderator + "delete_others_notice" => !!user.info.is_moderator, + "admin" => !!user.info.is_admin }, "screen_name" => user.nickname, "statuses_count" => user_info[:note_count], @@ -81,6 +112,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do # Pleroma extension "pleroma" => %{ + "confirmation_pending" => user_info.confirmation_pending, "tags" => user.tags } } @@ -92,23 +124,6 @@ defmodule Pleroma.Web.TwitterAPI.UserView do end end - def render("short.json", %{ - user: %User{ - nickname: nickname, - id: id, - ap_id: ap_id, - name: name - } - }) do - %{ - "fullname" => name, - "id" => id, - "ostatus_uri" => ap_id, - "profile_url" => ap_id, - "screen_name" => nickname - } - end - defp image_url(%{"url" => [%{"href" => href} | _]}), do: href defp image_url(_), do: nil diff --git a/lib/pleroma/web/twitter_api/views/util_view.ex b/lib/pleroma/web/twitter_api/views/util_view.ex index 71b04e6cc..f4050650e 100644 --- a/lib/pleroma/web/twitter_api/views/util_view.ex +++ b/lib/pleroma/web/twitter_api/views/util_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.TwitterAPI.UtilView do use Pleroma.Web, :view import Phoenix.HTML.Form diff --git a/lib/pleroma/web/views/error_helpers.ex b/lib/pleroma/web/views/error_helpers.ex index 3981b270d..bc08e60e4 100644 --- a/lib/pleroma/web/views/error_helpers.ex +++ b/lib/pleroma/web/views/error_helpers.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ErrorHelpers do @moduledoc """ Conveniences for translating and building error messages. diff --git a/lib/pleroma/web/views/error_view.ex b/lib/pleroma/web/views/error_view.ex index 7106031ae..86a1744b7 100644 --- a/lib/pleroma/web/views/error_view.ex +++ b/lib/pleroma/web/views/error_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.ErrorView do use Pleroma.Web, :view diff --git a/lib/pleroma/web/views/layout_view.ex b/lib/pleroma/web/views/layout_view.ex index d4d4c3bd3..e5183701d 100644 --- a/lib/pleroma/web/views/layout_view.ex +++ b/lib/pleroma/web/views/layout_view.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.LayoutView do use Pleroma.Web, :view end diff --git a/lib/pleroma/web/web.ex b/lib/pleroma/web/web.ex index b82242a78..74b13f929 100644 --- a/lib/pleroma/web/web.ex +++ b/lib/pleroma/web/web.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web do @moduledoc """ A module that keeps using definitions for controllers, diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 47c733da2..0a6338312 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.WebFinger do @httpoison Application.get_env(:pleroma, :httpoison) diff --git a/lib/pleroma/web/web_finger/web_finger_controller.ex b/lib/pleroma/web/web_finger/web_finger_controller.ex index 8c60300a4..b77c75ec5 100644 --- a/lib/pleroma/web/web_finger/web_finger_controller.ex +++ b/lib/pleroma/web/web_finger/web_finger_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.WebFinger.WebFingerController do use Pleroma.Web, :controller diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 8cb07006f..3a287edd9 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Websub do alias Ecto.Changeset alias Pleroma.Repo diff --git a/lib/pleroma/web/websub/websub_client_subscription.ex b/lib/pleroma/web/websub/websub_client_subscription.ex index 8cea02939..105b0069f 100644 --- a/lib/pleroma/web/websub/websub_client_subscription.ex +++ b/lib/pleroma/web/websub/websub_client_subscription.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Websub.WebsubClientSubscription do use Ecto.Schema alias Pleroma.User diff --git a/lib/pleroma/web/websub/websub_controller.ex b/lib/pleroma/web/websub/websub_controller.ex index c1934ba92..27304d988 100644 --- a/lib/pleroma/web/websub/websub_controller.ex +++ b/lib/pleroma/web/websub/websub_controller.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Websub.WebsubController do use Pleroma.Web, :controller alias Pleroma.{Repo, User} diff --git a/lib/pleroma/web/websub/websub_server_subscription.ex b/lib/pleroma/web/websub/websub_server_subscription.ex index 0e5248a73..d0ef548da 100644 --- a/lib/pleroma/web/websub/websub_server_subscription.ex +++ b/lib/pleroma/web/websub/websub_server_subscription.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.Websub.WebsubServerSubscription do use Ecto.Schema diff --git a/lib/pleroma/web/xml/xml.ex b/lib/pleroma/web/xml/xml.ex index b3ccf4a55..df50aac9c 100644 --- a/lib/pleroma/web/xml/xml.ex +++ b/lib/pleroma/web/xml/xml.ex @@ -1,3 +1,7 @@ +# Pleroma: A lightweight social networking server +# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/> +# SPDX-License-Identifier: AGPL-3.0-only + defmodule Pleroma.Web.XML do require Logger |