# Pleroma: A lightweight social networking server # Copyright © 2017-2019 Pleroma Authors # SPDX-License-Identifier: AGPL-3.0-only defmodule Pleroma.Web.OStatus.OStatusController do use Pleroma.Web, :controller alias Fallback.RedirectController alias Pleroma.Activity alias Pleroma.Object alias Pleroma.User alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.ObjectView alias Pleroma.Web.ActivityPub.Visibility alias Pleroma.Web.Endpoint alias Pleroma.Web.Federator alias Pleroma.Web.Metadata.PlayerView alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus.ActivityRepresenter alias Pleroma.Web.OStatus.FeedRepresenter alias Pleroma.Web.Router alias Pleroma.Web.XML plug(Pleroma.Plugs.RateLimiter, :ap_routes when action in [:object, :activity]) plug(Pleroma.Web.FederatingPlug when action in [:salmon_incoming]) plug( Pleroma.Plugs.SetFormatPlug when action in [:feed_redirect, :object, :activity, :notice] ) action_fallback(:errors) def feed_redirect(%{assigns: %{format: "html"}} = conn, %{"nickname" => nickname}) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname_or_id(nickname)} do RedirectController.redirector_with_meta(conn, %{user: user}) end end def feed_redirect(%{assigns: %{format: format}} = conn, _params) when format in ["json", "activity+json"] do ActivityPubController.call(conn, :user) end def feed_redirect(conn, %{"nickname" => nickname}) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do redirect(conn, external: OStatus.feed_path(user)) end end def feed(conn, %{"nickname" => nickname} = params) do with {_, %User{} = user} <- {:fetch_user, User.get_cached_by_nickname(nickname)} do query_params = Map.take(params, ["max_id"]) |> Map.merge(%{"whole_db" => true, "actor_id" => user.ap_id}) activities = ActivityPub.fetch_public_activities(query_params) |> Enum.reverse() response = user |> FeedRepresenter.to_simple_form(activities, [user]) |> :xmerl.export_simple(:xmerl_xml) |> to_string conn |> put_resp_content_type("application/atom+xml") |> send_resp(200, response) end end defp decode_or_retry(body) do with {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do {:ok, doc} else _e -> with [decoded | _] <- Pleroma.Web.Salmon.decode(body), doc <- XML.parse_document(decoded), uri when not is_nil(uri) <- XML.string_from_xpath("/entry/author[1]/uri", doc), {:ok, _} <- Pleroma.Web.OStatus.make_user(uri, true), {:ok, magic_key} <- Pleroma.Web.Salmon.fetch_magic_key(body), {:ok, doc} <- Pleroma.Web.Salmon.decode_and_validate(magic_key, body) do {:ok, doc} end end end def salmon_incoming(conn, _) do {:ok, body, _conn} = read_body(conn) {:ok, doc} = decode_or_retry(body) Federator.incoming_doc(doc) conn |> send_resp(200, "") end def object(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid}) when format in ["json", "activity+json"] do ActivityPubController.call(conn, :object) end def object(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do with id <- o_status_url(conn, :object, uuid), {_, %Activity{} = activity} <- {:activity, Activity.get_create_by_object_ap_id_with_object(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case format do "html" -> redirect(conn, to: "/notice/#{activity.id}") _ -> represent_activity(conn, nil, activity, user) end else reason when reason in [{:public?, false}, {:activity, nil}] -> {:error, :not_found} e -> e end end def activity(%{assigns: %{format: format}} = conn, %{"uuid" => _uuid}) when format in ["json", "activity+json"] do ActivityPubController.call(conn, :activity) end def activity(%{assigns: %{format: format}} = conn, %{"uuid" => uuid}) do with id <- o_status_url(conn, :activity, uuid), {_, %Activity{} = activity} <- {:activity, Activity.normalize(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do case format do "html" -> redirect(conn, to: "/notice/#{activity.id}") _ -> represent_activity(conn, format, activity, user) end else reason when reason in [{:public?, false}, {:activity, nil}] -> {:error, :not_found} e -> e end end def notice(%{assigns: %{format: format}} = conn, %{"id" => id}) do with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id_with_object(id)}, {_, true} <- {:public?, Visibility.is_public?(activity)}, %User{} = user <- User.get_cached_by_ap_id(activity.data["actor"]) do cond do format == "html" && activity.data["type"] == "Create" -> %Object{} = object = Object.normalize(activity) RedirectController.redirector_with_meta( conn, %{ activity_id: activity.id, object: object, url: Router.Helpers.o_status_url(Endpoint, :notice, activity.id), user: user } ) format == "html" -> RedirectController.redirector(conn, nil) true -> represent_activity(conn, format, activity, user) end else reason when reason in [{:public?, false}, {:activity, nil}] -> conn |> put_status(404) |> RedirectController.redirector(nil, 404) e -> e end end # Returns an HTML embedded