diff options
author | Rin Toshaka <rinpatch@sdf.org> | 2018-12-05 14:16:55 +0100 |
---|---|---|
committer | Rin Toshaka <rinpatch@sdf.org> | 2018-12-05 14:16:55 +0100 |
commit | 9938fa3293478e0dfe5f8d82c1526e0cd3747cf1 (patch) | |
tree | ff6497bc68b1588cf3b54cc6b9e9135ef69a17d1 /lib | |
parent | 6396f1b58f69b727e801df4b52a32c47da08e517 (diff) | |
parent | 22eff1791204864d0536e71c13649d0030c409a7 (diff) | |
download | pleroma-9938fa3293478e0dfe5f8d82c1526e0cd3747cf1.tar.gz |
Merge develop to refactor/mix-tasks
Diffstat (limited to 'lib')
33 files changed, 672 insertions, 304 deletions
diff --git a/lib/mix/tasks/pleroma/sample_psql.eex b/lib/mix/tasks/pleroma/sample_psql.eex index 66f76752f..c89b34ef2 100644 --- a/lib/mix/tasks/pleroma/sample_psql.eex +++ b/lib/mix/tasks/pleroma/sample_psql.eex @@ -1,9 +1,7 @@ -CREATE USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; --- in case someone runs this second time accidentally -ALTER USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>' CREATEDB; -CREATE DATABASE <%= dbname %>; -ALTER DATABASE <%= dbname %> OWNER TO <%= dbuser %>; -\c <%= dbname %>; +CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>'; +CREATE DATABASE pleroma_dev OWNER pleroma; +\c pleroma_dev; --Extensions made by ecto.migrate that need superuser access CREATE EXTENSION IF NOT EXISTS citext; CREATE EXTENSION IF NOT EXISTS pg_trgm; +CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; diff --git a/lib/pleroma/formatter.ex b/lib/pleroma/formatter.ex index 26bb17377..1a5c07c8a 100644 --- a/lib/pleroma/formatter.ex +++ b/lib/pleroma/formatter.ex @@ -116,8 +116,8 @@ defmodule Pleroma.Formatter do subs ++ Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} -> ap_id = - if is_binary(info["source_data"]["url"]) do - info["source_data"]["url"] + if is_binary(info.source_data["url"]) do + info.source_data["url"] else ap_id end diff --git a/lib/pleroma/http/connection.ex b/lib/pleroma/http/connection.ex new file mode 100644 index 000000000..5e8f2aabd --- /dev/null +++ b/lib/pleroma/http/connection.ex @@ -0,0 +1,27 @@ +defmodule Pleroma.HTTP.Connection do + @moduledoc """ + Connection for http-requests. + """ + + @hackney_options [pool: :default] + @adapter Application.get_env(:tesla, :adapter) + + @doc """ + Configure a client connection + + # Returns + + Tesla.Env.client + """ + @spec new(Keyword.t()) :: Tesla.Env.client() + def new(opts \\ []) do + Tesla.client([], {@adapter, hackney_options(opts)}) + end + + # fetch Hackney options + # + defp hackney_options(opts \\ []) do + options = Keyword.get(opts, :adapter, []) + @hackney_options ++ options + end +end diff --git a/lib/pleroma/http/http.ex b/lib/pleroma/http/http.ex index e64266ae7..3c0256575 100644 --- a/lib/pleroma/http/http.ex +++ b/lib/pleroma/http/http.ex @@ -1,14 +1,42 @@ defmodule Pleroma.HTTP do - require HTTPoison + @moduledoc """ + """ + + alias Pleroma.HTTP.Connection + alias Pleroma.HTTP.RequestBuilder, as: Builder + + @doc """ + Builds and perform http request. + + # Arguments: + `method` - :get, :post, :put, :delete + `url` + `body` + `headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]` + `options` - custom, per-request middleware or adapter options + + # Returns: + `{:ok, %Tesla.Env{}}` or `{:error, error}` + + """ def request(method, url, body \\ "", headers \\ [], options \\ []) do options = process_request_options(options) |> process_sni_options(url) - HTTPoison.request(method, url, body, headers, options) + %{} + |> Builder.method(method) + |> Builder.headers(headers) + |> Builder.opts(options) + |> Builder.url(url) + |> Builder.add_param(:body, :body, body) + |> Enum.into([]) + |> (&Tesla.request(Connection.new(), &1)).() end + defp process_sni_options(options, nil), do: options + defp process_sni_options(options, url) do uri = URI.parse(url) host = uri.host |> to_charlist() @@ -22,7 +50,7 @@ defmodule Pleroma.HTTP do def process_request_options(options) do config = Application.get_env(:pleroma, :http, []) proxy = Keyword.get(config, :proxy_url, nil) - options = options ++ [hackney: [pool: :default]] + options = options ++ [adapter: [pool: :default]] case proxy do nil -> options @@ -30,8 +58,19 @@ defmodule Pleroma.HTTP do end end - def get(url, headers \\ [], options \\ []), do: request(:get, url, "", headers, options) + @doc """ + Performs GET request. + + See `Pleroma.HTTP.request/5` + """ + def get(url, headers \\ [], options \\ []), + do: request(:get, url, "", headers, options) + + @doc """ + Performs POST request. + See `Pleroma.HTTP.request/5` + """ def post(url, body, headers \\ [], options \\ []), do: request(:post, url, body, headers, options) end diff --git a/lib/pleroma/http/request_builder.ex b/lib/pleroma/http/request_builder.ex new file mode 100644 index 000000000..5aee2b8ae --- /dev/null +++ b/lib/pleroma/http/request_builder.ex @@ -0,0 +1,126 @@ +defmodule Pleroma.HTTP.RequestBuilder do + @moduledoc """ + Helper functions for building Tesla requests + """ + + @doc """ + Specify the request method when building a request + + ## Parameters + + - request (Map) - Collected request options + - m (atom) - Request method + + ## Returns + + Map + """ + @spec method(map(), atom) :: map() + def method(request, m) do + Map.put_new(request, :method, m) + end + + @doc """ + Specify the request method when building a request + + ## Parameters + + - request (Map) - Collected request options + - u (String) - Request URL + + ## Returns + + Map + """ + @spec url(map(), String.t()) :: map() + def url(request, u) do + Map.put_new(request, :url, u) + end + + @doc """ + Add headers to the request + """ + @spec headers(map(), list(tuple)) :: map() + def headers(request, h) do + Map.put_new(request, :headers, h) + end + + @doc """ + Add custom, per-request middleware or adapter options to the request + """ + @spec opts(map(), Keyword.t()) :: map() + def opts(request, options) do + Map.put_new(request, :opts, options) + end + + @doc """ + Add optional parameters to the request + + ## Parameters + + - request (Map) - Collected request options + - definitions (Map) - Map of parameter name to parameter location. + - options (KeywordList) - The provided optional parameters + + ## Returns + + Map + """ + @spec add_optional_params(map(), %{optional(atom) => atom}, keyword()) :: map() + def add_optional_params(request, _, []), do: request + + def add_optional_params(request, definitions, [{key, value} | tail]) do + case definitions do + %{^key => location} -> + request + |> add_param(location, key, value) + |> add_optional_params(definitions, tail) + + _ -> + add_optional_params(request, definitions, tail) + end + end + + @doc """ + Add optional parameters to the request + + ## Parameters + + - request (Map) - Collected request options + - location (atom) - Where to put the parameter + - key (atom) - The name of the parameter + - value (any) - The value of the parameter + + ## Returns + + Map + """ + @spec add_param(map(), atom, atom, any()) :: map() + def add_param(request, :body, :body, value), do: Map.put(request, :body, value) + + def add_param(request, :body, key, value) do + request + |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0) + |> Map.update!( + :body, + &Tesla.Multipart.add_field(&1, key, Poison.encode!(value), + headers: [{:"Content-Type", "application/json"}] + ) + ) + end + + def add_param(request, :file, name, path) do + request + |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0) + |> Map.update!(:body, &Tesla.Multipart.add_file(&1, path, name: name)) + end + + def add_param(request, :form, name, value) do + request + |> Map.update(:body, %{name => value}, &Map.put(&1, name, value)) + end + + def add_param(request, location, key, value) do + Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}])) + end +end diff --git a/lib/pleroma/plugs/oauth_plug.ex b/lib/pleroma/plugs/oauth_plug.ex index 0380ce14d..630f15eec 100644 --- a/lib/pleroma/plugs/oauth_plug.ex +++ b/lib/pleroma/plugs/oauth_plug.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Plugs.OAuthPlug do with token when not is_nil(token) <- token, %Token{user_id: user_id} <- Repo.get_by(Token, token: token), %User{} = user <- Repo.get(User, user_id), - false <- !!user.info["deactivated"] do + false <- !!user.info.deactivated do conn |> assign(:user, user) else diff --git a/lib/pleroma/plugs/user_enabled_plug.ex b/lib/pleroma/plugs/user_enabled_plug.ex index 9c3285896..01482f47d 100644 --- a/lib/pleroma/plugs/user_enabled_plug.ex +++ b/lib/pleroma/plugs/user_enabled_plug.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Plugs.UserEnabledPlug do options end - def call(%{assigns: %{user: %User{info: %{"deactivated" => true}}}} = conn, _) do + def call(%{assigns: %{user: %User{info: %{deactivated: true}}}} = conn, _) do conn |> assign(:user, nil) end diff --git a/lib/pleroma/plugs/user_is_admin_plug.ex b/lib/pleroma/plugs/user_is_admin_plug.ex index 5312f1499..cf22ce5d0 100644 --- a/lib/pleroma/plugs/user_is_admin_plug.ex +++ b/lib/pleroma/plugs/user_is_admin_plug.ex @@ -6,7 +6,7 @@ defmodule Pleroma.Plugs.UserIsAdminPlug do options end - def call(%{assigns: %{user: %User{info: %{"is_admin" => true}}}} = conn, _) do + def call(%{assigns: %{user: %User{info: %{is_admin: true}}}} = conn, _) do conn end diff --git a/lib/pleroma/uploaders/mdii.ex b/lib/pleroma/uploaders/mdii.ex index 35d36d3e4..820cf88f5 100644 --- a/lib/pleroma/uploaders/mdii.ex +++ b/lib/pleroma/uploaders/mdii.ex @@ -20,7 +20,7 @@ defmodule Pleroma.Uploaders.MDII do extension = String.split(upload.name, ".") |> List.last() query = "#{cgi}?#{extension}" - with {:ok, %{status_code: 200, body: body}} <- @httpoison.post(query, file_data) do + with {:ok, %{status: 200, body: body}} <- @httpoison.post(query, file_data) do remote_file_name = String.split(body) |> List.first() public_url = "#{files}/#{remote_file_name}.#{extension}" {:ok, {:url, public_url}} diff --git a/lib/pleroma/uploaders/swift/keystone.ex b/lib/pleroma/uploaders/swift/keystone.ex index e578b3c61..4aed977b1 100644 --- a/lib/pleroma/uploaders/swift/keystone.ex +++ b/lib/pleroma/uploaders/swift/keystone.ex @@ -25,10 +25,10 @@ defmodule Pleroma.Uploaders.Swift.Keystone do ["Content-Type": "application/json"], hackney: [:insecure] ) do - {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> + {:ok, %Tesla.Env{status: 200, body: body}} -> body["access"]["token"]["id"] - {:ok, %HTTPoison.Response{status_code: _}} -> + {:ok, %Tesla.Env{status: _}} -> "" end end diff --git a/lib/pleroma/uploaders/swift/swift.ex b/lib/pleroma/uploaders/swift/swift.ex index 1e865f101..a5b3d2852 100644 --- a/lib/pleroma/uploaders/swift/swift.ex +++ b/lib/pleroma/uploaders/swift/swift.ex @@ -13,10 +13,10 @@ defmodule Pleroma.Uploaders.Swift.Client do token = Pleroma.Uploaders.Swift.Keystone.get_token() case put("#{filename}", body, "X-Auth-Token": token, "Content-Type": content_type) do - {:ok, %HTTPoison.Response{status_code: 201}} -> + {:ok, %Tesla.Env{status: 201}} -> {:ok, {:file, filename}} - {:ok, %HTTPoison.Response{status_code: 401}} -> + {:ok, %Tesla.Env{status: 401}} -> {:error, "Unauthorized, Bad Token"} {:error, _} -> diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 6e1d5559d..74ae5ef0d 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -4,6 +4,8 @@ defmodule Pleroma.User do import Ecto.{Changeset, Query} alias Pleroma.{Repo, User, Object, Web, Activity, Notification} alias Comeonin.Pbkdf2 + alias Pleroma.Formatter + alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils alias Pleroma.Web.{OStatus, Websub, OAuth} alias Pleroma.Web.ActivityPub.{Utils, ActivityPub} @@ -19,11 +21,11 @@ defmodule Pleroma.User do field(:ap_id, :string) field(:avatar, :map) field(:local, :boolean, default: true) - field(:info, :map, default: %{}) field(:follower_address, :string) field(:search_distance, :float, virtual: true) field(:last_refreshed_at, :naive_datetime) has_many(:notifications, Notification) + embeds_one(:info, Pleroma.User.Info) timestamps() end @@ -36,13 +38,13 @@ defmodule Pleroma.User do end def banner_url(user) do - case user.info["banner"] do + case user.info.banner do %{"url" => [%{"href" => href} | _]} -> href _ -> "#{Web.base_url()}/images/banner.png" end end - def profile_url(%User{info: %{"source_data" => %{"url" => url}}}), do: url + def profile_url(%User{info: %{source_data: %{"url" => url}}}), do: url def profile_url(%User{ap_id: ap_id}), do: ap_id def profile_url(_), do: nil @@ -61,9 +63,7 @@ defmodule Pleroma.User do end def info_changeset(struct, params \\ %{}) do - struct - |> cast(params, [:info]) - |> validate_required([:info]) + raise "NOT VALID ANYMORE" end def user_info(%User{} = user) do @@ -71,27 +71,34 @@ defmodule Pleroma.User do %{ following_count: length(user.following) - oneself, - note_count: user.info["note_count"] || 0, - follower_count: user.info["follower_count"] || 0, - locked: user.info["locked"] || false, - default_scope: user.info["default_scope"] || "public" + note_count: user.info.note_count, + follower_count: user.info.follower_count, + locked: user.info.locked, + default_scope: user.info.default_scope } end @email_regex ~r/^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/ def remote_user_creation(params) do + params = + params + |> Map.put(:info, params[:info] || %{}) + + info_cng = User.Info.remote_user_creation(%User.Info{}, params[:info]) + changes = %User{} - |> cast(params, [:bio, :name, :ap_id, :nickname, :info, :avatar]) + |> cast(params, [:bio, :name, :ap_id, :nickname, :avatar]) |> validate_required([:name, :ap_id]) |> unique_constraint(:nickname) |> validate_format(:nickname, @email_regex) |> validate_length(:bio, max: 5000) |> validate_length(:name, max: 100) |> put_change(:local, false) + |> put_embed(:info, info_cng) if changes.valid? do - case changes.changes[:info]["source_data"] do + case info_cng.changes[:source_data] do %{"followers" => followers} -> changes |> put_change(:follower_address, followers) @@ -109,7 +116,7 @@ defmodule Pleroma.User do def update_changeset(struct, params \\ %{}) do struct - |> cast(params, [:bio, :name]) + |> cast(params, [:bio, :name, :avatar]) |> unique_constraint(:nickname) |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) |> validate_length(:bio, max: 5000) @@ -121,12 +128,17 @@ defmodule Pleroma.User do params |> Map.put(:last_refreshed_at, NaiveDateTime.utc_now()) + info_cng = + struct.info + |> User.Info.user_upgrade(params[:info]) + struct - |> cast(params, [:bio, :name, :info, :follower_address, :avatar, :last_refreshed_at]) + |> cast(params, [:bio, :name, :follower_address, :avatar, :last_refreshed_at]) |> unique_constraint(:nickname) |> validate_format(:nickname, ~r/^[a-zA-Z\d]+$/) |> validate_length(:bio, max: 5000) |> validate_length(:name, max: 100) + |> put_embed(:info, info_cng) end def password_update_changeset(struct, params) do @@ -165,6 +177,7 @@ defmodule Pleroma.User do |> validate_format(:email, @email_regex) |> validate_length(:bio, max: 1000) |> validate_length(:name, min: 1, max: 100) + |> put_change(:info, %Pleroma.User.Info{}) if changeset.valid? do hashed = Pbkdf2.hashpwsalt(changeset.changes[:password]) @@ -191,7 +204,7 @@ defmodule Pleroma.User do def needs_update?(_), do: true - def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{"locked" => true}}) do + def maybe_direct_follow(%User{} = follower, %User{local: true, info: %{locked: true}}) do {:ok, follower} end @@ -222,7 +235,7 @@ defmodule Pleroma.User do ap_followers = followed.follower_address cond do - following?(follower, followed) or info["deactivated"] -> + following?(follower, followed) or info.deactivated -> {:error, "Could not follow user: #{followed.nickname} is already on your list."} deny_follow_blocked and blocks?(followed, follower) -> @@ -274,7 +287,7 @@ defmodule Pleroma.User do end def locked?(%User{} = user) do - user.info["locked"] || false + user.info.locked || false end def get_by_ap_id(ap_id) do @@ -411,22 +424,23 @@ defmodule Pleroma.User do end def increase_note_count(%User{} = user) do - note_count = (user.info["note_count"] || 0) + 1 - new_info = Map.put(user.info, "note_count", note_count) + info_cng = User.Info.add_to_note_count(user.info, 1) - cs = info_changeset(user, %{info: new_info}) + cng = + change(user) + |> put_embed(:info, info_cng) - update_and_set_cache(cs) + update_and_set_cache(cng) end def decrease_note_count(%User{} = user) do - note_count = user.info["note_count"] || 0 - note_count = if note_count <= 0, do: 0, else: note_count - 1 - new_info = Map.put(user.info, "note_count", note_count) + info_cng = User.Info.add_to_note_count(user.info, -1) - cs = info_changeset(user, %{info: new_info}) + cng = + change(user) + |> put_embed(:info, info_cng) - update_and_set_cache(cs) + update_and_set_cache(cng) end def update_note_count(%User{} = user) do @@ -439,11 +453,13 @@ defmodule Pleroma.User do note_count = Repo.one(note_count_query) - new_info = Map.put(user.info, "note_count", note_count) + info_cng = User.Info.set_note_count(user.info, note_count) - cs = info_changeset(user, %{info: new_info}) + cng = + change(user) + |> put_embed(:info, info_cng) - update_and_set_cache(cs) + update_and_set_cache(cng) end def update_follower_count(%User{} = user) do @@ -457,11 +473,15 @@ defmodule Pleroma.User do follower_count = Repo.one(follower_count_query) - new_info = Map.put(user.info, "follower_count", follower_count) + info_cng = + user.info + |> User.Info.set_follower_count(follower_count) - cs = info_changeset(user, %{info: new_info}) + cng = + change(user) + |> put_embed(:info, info_cng) - update_and_set_cache(cs) + update_and_set_cache(cng) end def get_users_from_set_query(ap_ids, false) do @@ -545,12 +565,15 @@ defmodule Pleroma.User do unfollow(blocked, blocker) end - blocks = blocker.info["blocks"] || [] - new_blocks = Enum.uniq([ap_id | blocks]) - new_info = Map.put(blocker.info, "blocks", new_blocks) + info_cng = + blocker.info + |> User.Info.add_to_block(ap_id) + + cng = + change(blocker) + |> put_embed(:info, info_cng) - cs = User.info_changeset(blocker, %{info: new_info}) - update_and_set_cache(cs) + update_and_set_cache(cng) end # helper to handle the block given only an actor's AP id @@ -558,18 +581,21 @@ defmodule Pleroma.User do block(blocker, User.get_by_ap_id(ap_id)) end - def unblock(user, %{ap_id: ap_id}) do - blocks = user.info["blocks"] || [] - new_blocks = List.delete(blocks, ap_id) - new_info = Map.put(user.info, "blocks", new_blocks) + def unblock(blocker, %{ap_id: ap_id}) do + info_cng = + blocker.info + |> User.Info.remove_from_block(ap_id) - cs = User.info_changeset(user, %{info: new_info}) - update_and_set_cache(cs) + cng = + change(blocker) + |> put_embed(:info, info_cng) + + update_and_set_cache(cng) end def blocks?(user, %{ap_id: ap_id}) do - blocks = user.info["blocks"] || [] - domain_blocks = user.info["domain_blocks"] || [] + blocks = user.info.blocks + domain_blocks = user.info.domain_blocks %{host: host} = URI.parse(ap_id) Enum.member?(blocks, ap_id) || @@ -579,21 +605,27 @@ defmodule Pleroma.User do end def block_domain(user, domain) do - domain_blocks = user.info["domain_blocks"] || [] - new_blocks = Enum.uniq([domain | domain_blocks]) - new_info = Map.put(user.info, "domain_blocks", new_blocks) + info_cng = + user.info + |> User.Info.add_to_domain_block(domain) + + cng = + change(user) + |> put_embed(:info, info_cng) - cs = User.info_changeset(user, %{info: new_info}) - update_and_set_cache(cs) + update_and_set_cache(cng) end def unblock_domain(user, domain) do - blocks = user.info["domain_blocks"] || [] - new_blocks = List.delete(blocks, domain) - new_info = Map.put(user.info, "domain_blocks", new_blocks) + info_cng = + user.info + |> User.Info.remove_from_domain_block(domain) - cs = User.info_changeset(user, %{info: new_info}) - update_and_set_cache(cs) + cng = + change(user) + |> put_embed(:info, info_cng) + + update_and_set_cache(cng) end def local_user_query() do @@ -613,9 +645,13 @@ defmodule Pleroma.User do end def deactivate(%User{} = user, status \\ true) do - new_info = Map.put(user.info, "deactivated", status) - cs = User.info_changeset(user, %{info: new_info}) - update_and_set_cache(cs) + info_cng = User.Info.set_activation_status(user.info, status) + + cng = + change(user) + |> put_embed(:info, info_cng) + + update_and_set_cache(cng) end def delete(%User{} = user) do @@ -649,7 +685,7 @@ defmodule Pleroma.User do {:ok, user} end - def html_filter_policy(%User{info: %{"no_rich_text" => true}}) do + def html_filter_policy(%User{info: %{no_rich_text: true}}) do Pleroma.HTML.Scrubber.TwitterText end @@ -683,7 +719,7 @@ defmodule Pleroma.User do user else changes = - %User{} + %User{info: %User.Info{}} |> cast(%{}, [:ap_id, :nickname, :local]) |> put_change(:ap_id, relay_uri) |> put_change(:nickname, nil) @@ -697,7 +733,7 @@ defmodule Pleroma.User do # AP style def public_key_from_info(%{ - "source_data" => %{"publicKey" => %{"publicKeyPem" => public_key_pem}} + source_data: %{"publicKey" => %{"publicKeyPem" => public_key_pem}} }) do key = :public_key.pem_decode(public_key_pem) @@ -708,7 +744,7 @@ defmodule Pleroma.User do end # OStatus Magic Key - def public_key_from_info(%{"magic_key" => magic_key}) do + def public_key_from_info(%{magic_key: magic_key}) do {:ok, Pleroma.Web.Salmon.decode_key(magic_key)} end @@ -730,11 +766,12 @@ defmodule Pleroma.User do |> Map.put(:name, blank?(data[:name]) || data[:nickname]) cs = User.remote_user_creation(data) + Repo.insert(cs, on_conflict: :replace_all, conflict_target: :nickname) end def ap_enabled?(%User{local: true}), do: true - def ap_enabled?(%User{info: info}), do: info["ap_enabled"] + def ap_enabled?(%User{info: info}), do: info.ap_enabled def ap_enabled?(_), do: false def get_or_fetch(uri_or_nickname) do @@ -768,4 +805,18 @@ defmodule Pleroma.User do :error end end + + def parse_bio(bio, user \\ %User{info: %{source_data: %{}}}) do + mentions = Formatter.parse_mentions(bio) + tags = Formatter.parse_tags(bio) + + emoji = + (user.info.source_data["tag"] || []) + |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) + |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> + {String.trim(name, ":"), url} + end) + + CommonUtils.format_input(bio, mentions, tags, "text/plain") |> Formatter.emojify(emoji) + end end diff --git a/lib/pleroma/user/info.ex b/lib/pleroma/user/info.ex new file mode 100644 index 000000000..49b2f0eda --- /dev/null +++ b/lib/pleroma/user/info.ex @@ -0,0 +1,165 @@ +defmodule Pleroma.User.Info do + use Ecto.Schema + import Ecto.Changeset + + embedded_schema do + field(:banner, :map, default: %{}) + field(:background, :map, default: %{}) + field(:source_data, :map, default: %{}) + field(:note_count, :integer, default: 0) + field(:follower_count, :integer, default: 0) + field(:locked, :boolean, default: false) + field(:default_scope, :string, default: "public") + field(:blocks, {:array, :string}, default: []) + field(:domain_blocks, {:array, :string}, default: []) + field(:deactivated, :boolean, default: false) + field(:no_rich_text, :boolean, default: false) + field(:ap_enabled, :boolean, default: false) + field(:is_moderator, :boolean, default: false) + field(:is_admin, :boolean, default: false) + field(:keys, :string, default: nil) + field(:settings, :map, default: nil) + field(:magic_key, :string, default: nil) + field(:uri, :string, default: nil) + field(:topic, :string, default: nil) + field(:hub, :string, default: nil) + field(:salmon, :string, default: nil) + + # Found in the wild + # ap_id -> Where is this used? + # bio -> Where is this used? + # avatar -> Where is this used? + # fqn -> Where is this used? + # host -> Where is this used? + # subject _> Where is this used? + end + + def set_activation_status(info, deactivated) do + params = %{deactivated: deactivated} + + info + |> cast(params, [:deactivated]) + |> validate_required([:deactivated]) + end + + def add_to_note_count(info, number) do + set_note_count(info, info.note_count + number) + end + + def set_note_count(info, number) do + params = %{note_count: Enum.max([0, number])} + + info + |> cast(params, [:note_count]) + |> validate_required([:note_count]) + end + + def set_follower_count(info, number) do + params = %{follower_count: Enum.max([0, number])} + + info + |> cast(params, [:follower_count]) + |> validate_required([:follower_count]) + end + + def set_blocks(info, blocks) do + params = %{blocks: blocks} + + info + |> cast(params, [:blocks]) + |> validate_required([:blocks]) + end + + def add_to_block(info, blocked) do + set_blocks(info, Enum.uniq([blocked | info.blocks])) + end + + def remove_from_block(info, blocked) do + set_blocks(info, List.delete(info.blocks, blocked)) + end + + def set_domain_blocks(info, domain_blocks) do + params = %{domain_blocks: domain_blocks} + + info + |> cast(params, [:domain_blocks]) + |> validate_required([:domain_blocks]) + end + + def add_to_domain_block(info, domain_blocked) do + set_domain_blocks(info, Enum.uniq([domain_blocked | info.domain_blocks])) + end + + def remove_from_domain_block(info, domain_blocked) do + set_domain_blocks(info, List.delete(info.domain_blocks, domain_blocked)) + end + + def set_keys(info, keys) do + params = %{keys: keys} + + info + |> cast(params, [:keys]) + |> validate_required([:keys]) + end + + def remote_user_creation(info, params) do + info + |> cast(params, [ + :ap_enabled, + :source_data, + :banner, + :locked, + :magic_key, + :uri, + :hub, + :topic, + :salmon + ]) + end + + def user_upgrade(info, params) do + info + |> cast(params, [ + :ap_enabled, + :source_data, + :banner, + :locked, + :magic_key + ]) + end + + def profile_update(info, params) do + info + |> cast(params, [ + :locked, + :no_rich_text, + :default_scope, + :banner, + :background + ]) + end + + def mastodon_profile_update(info, params) do + info + |> cast(params, [ + :locked, + :banner + ]) + end + + def set_source_data(info, source_data) do + params = %{source_data: source_data} + + info + |> cast(params, [:source_data]) + |> validate_required([:source_data]) + end + + def admin_api_update(info, params) do + info + |> cast(params, [ + :is_moderator, + :is_admin + ]) + end +end diff --git a/lib/pleroma/web/activity_pub/activity_pub.ex b/lib/pleroma/web/activity_pub/activity_pub.ex index 76c15cf21..60253a715 100644 --- a/lib/pleroma/web/activity_pub/activity_pub.ex +++ b/lib/pleroma/web/activity_pub/activity_pub.ex @@ -42,7 +42,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do defp check_actor_is_active(actor) do if not is_nil(actor) do with user <- User.get_cached_by_ap_id(actor), - false <- !!user.info["deactivated"] do + false <- user.info.deactivated do :ok else _e -> :reject @@ -509,8 +509,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do end defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do - blocks = info["blocks"] || [] - domain_blocks = info["domain_blocks"] || [] + blocks = info.blocks || [] + domain_blocks = info.domain_blocks || [] from( activity in query, @@ -676,7 +676,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do remote_inboxes = (Pleroma.Web.Salmon.remote_users(activity) ++ followers) |> Enum.filter(fn user -> User.ap_enabled?(user) end) - |> Enum.map(fn %{info: %{"source_data" => data}} -> + |> Enum.map(fn %{info: %{source_data: data}} -> (is_map(data["endpoints"]) && Map.get(data["endpoints"], "sharedInbox")) || data["inbox"] end) |> Enum.uniq() @@ -762,7 +762,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do Logger.info("Fetching #{id} via AP") with true <- String.starts_with?(id, "http"), - {:ok, %{body: body, status_code: code}} when code in 200..299 <- + {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( id, [Accept: "application/activity+json"], diff --git a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex index 86dcf5080..12fc3b181 100644 --- a/lib/pleroma/web/activity_pub/mrf/simple_policy.ex +++ b/lib/pleroma/web/activity_pub/mrf/simple_policy.ex @@ -23,7 +23,7 @@ defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defp check_media_removal( %{host: actor_host} = _actor_info, - %{"type" => "Create", "object" => %{"attachement" => child_attachment}} = object + %{"type" => "Create", "object" => %{"attachment" => child_attachment}} = object ) when length(child_attachment) > 0 do object = diff --git a/lib/pleroma/web/activity_pub/transmogrifier.ex b/lib/pleroma/web/activity_pub/transmogrifier.ex index 5864855b0..17b063609 100644 --- a/lib/pleroma/web/activity_pub/transmogrifier.ex +++ b/lib/pleroma/web/activity_pub/transmogrifier.ex @@ -447,7 +447,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do update_data = new_user_data |> Map.take([:name, :bio, :avatar]) - |> Map.put(:info, Map.merge(actor.info, %{"banner" => banner, "locked" => locked})) + |> Map.put(:info, %{"banner" => banner, "locked" => locked}) actor |> User.upgrade_changeset(update_data) @@ -850,10 +850,6 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do def upgrade_user_from_ap_id(ap_id, async \\ true) do with %User{local: false} = user <- User.get_by_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id) do - data = - data - |> Map.put(:info, Map.merge(user.info, data[:info])) - already_ap = User.ap_enabled?(user) {:ok, user} = diff --git a/lib/pleroma/web/activity_pub/views/user_view.ex b/lib/pleroma/web/activity_pub/views/user_view.ex index eb335813d..aaa777602 100644 --- a/lib/pleroma/web/activity_pub/views/user_view.ex +++ b/lib/pleroma/web/activity_pub/views/user_view.ex @@ -12,7 +12,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do # the instance itself is not a Person, but instead an Application def render("user.json", %{user: %{nickname: nil} = user}) do {:ok, user} = WebFinger.ensure_keys_present(user) - {:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"]) + {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -40,7 +40,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do def render("user.json", %{user: user}) do {:ok, user} = WebFinger.ensure_keys_present(user) - {:ok, _, public_key} = Salmon.keys_from_pem(user.info["keys"]) + {:ok, _, public_key} = Salmon.keys_from_pem(user.info.keys) public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key) public_key = :public_key.pem_encode([public_key]) @@ -55,7 +55,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "name" => user.name, "summary" => user.bio, "url" => user.ap_id, - "manuallyApprovesFollowers" => user.info["locked"] || false, + "manuallyApprovesFollowers" => user.info.locked, "publicKey" => %{ "id" => "#{user.ap_id}#main-key", "owner" => user.ap_id, @@ -72,7 +72,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do "type" => "Image", "url" => User.banner_url(user) }, - "tag" => user.info["source_data"]["tag"] || [] + "tag" => user.info.source_data["tag"] || [] } |> Map.merge(Utils.make_json_ld_header()) end diff --git a/lib/pleroma/web/admin_api/admin_api_controller.ex b/lib/pleroma/web/admin_api/admin_api_controller.ex index bcdb4ba37..2c67d9cda 100644 --- a/lib/pleroma/web/admin_api/admin_api_controller.ex +++ b/lib/pleroma/web/admin_api/admin_api_controller.ex @@ -45,21 +45,29 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do user = User.get_by_nickname(nickname) info = - user.info + %{} |> Map.put("is_" <> permission_group, true) - cng = User.info_changeset(user, %{info: info}) + info_cng = User.Info.admin_api_update(user.info, info) + + cng = + Ecto.Changeset.change(user) + |> Ecto.Changeset.put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(cng) conn - |> json(user.info) + |> json(info) end def right_get(conn, %{"nickname" => nickname}) do user = User.get_by_nickname(nickname) conn - |> json(user.info) + |> json(%{ + is_moderator: user.info.is_moderator, + is_admin: user.info.is_admin + }) end def right_add(conn, _) do @@ -84,14 +92,19 @@ defmodule Pleroma.Web.AdminAPI.AdminAPIController do user = User.get_by_nickname(nickname) info = - user.info + %{} |> Map.put("is_" <> permission_group, false) - cng = User.info_changeset(user, %{info: info}) + info_cng = User.Info.admin_api_update(user.info, info) + + cng = + Ecto.Changeset.change(user) + |> Ecto.Changeset.put_embed(:info, info_cng) + {:ok, user} = User.update_and_set_cache(cng) conn - |> json(user.info) + |> json(info) end end diff --git a/lib/pleroma/web/common_api/common_api.ex b/lib/pleroma/web/common_api/common_api.ex index 77e4dbbd7..e3385310f 100644 --- a/lib/pleroma/web/common_api/common_api.ex +++ b/lib/pleroma/web/common_api/common_api.ex @@ -8,7 +8,7 @@ defmodule Pleroma.Web.CommonAPI do def delete(activity_id, user) do with %Activity{data: %{"object" => %{"id" => object_id}}} <- Repo.get(Activity, activity_id), %Object{} = object <- Object.normalize(object_id), - true <- user.info["is_moderator"] || user.ap_id == object.data["actor"], + true <- user.info.is_moderator || user.ap_id == object.data["actor"], {:ok, delete} <- ActivityPub.delete(object) do {:ok, delete} end @@ -135,12 +135,13 @@ defmodule Pleroma.Web.CommonAPI do end end + # Updates the emojis for a user based on their profile def update(user) do user = with emoji <- emoji_from_profile(user), - source_data <- (user.info["source_data"] || %{}) |> Map.put("tag", emoji), - new_info <- Map.put(user.info, "source_data", source_data), - change <- User.info_changeset(user, %{info: new_info}), + source_data <- (user.info.source_data || %{}) |> Map.put("tag", emoji), + info_cng <- Pleroma.User.Info.set_source_data(user.info, source_data), + change <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), {:ok, user} <- User.update_and_set_cache(change) do user else diff --git a/lib/pleroma/web/federator/retry_queue.ex b/lib/pleroma/web/federator/retry_queue.ex index 06c094f26..13df40c80 100644 --- a/lib/pleroma/web/federator/retry_queue.ex +++ b/lib/pleroma/web/federator/retry_queue.ex @@ -17,7 +17,15 @@ defmodule Pleroma.Web.Federator.RetryQueue do end def start_link() do - GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__) + enabled = Pleroma.Config.get([:retry_queue, :enabled], false) + + if enabled do + Logger.info("Starting retry queue") + GenServer.start_link(__MODULE__, %{delivered: 0, dropped: 0}, name: __MODULE__) + else + Logger.info("Retry queue disabled") + :ignore + end end def enqueue(data, transport, retries \\ 0) do diff --git a/lib/pleroma/web/http_signatures/http_signatures.ex b/lib/pleroma/web/http_signatures/http_signatures.ex index 5e42a871b..0e54debd5 100644 --- a/lib/pleroma/web/http_signatures/http_signatures.ex +++ b/lib/pleroma/web/http_signatures/http_signatures.ex @@ -65,7 +65,7 @@ defmodule Pleroma.Web.HTTPSignatures do end def sign(user, headers) do - with {:ok, %{info: %{"keys" => keys}}} <- Pleroma.Web.WebFinger.ensure_keys_present(user), + with {:ok, %{info: %{keys: keys}}} <- Pleroma.Web.WebFinger.ensure_keys_present(user), {:ok, private_key, _} = Pleroma.Web.Salmon.keys_from_pem(keys) do sigstring = build_signing_string(headers, Map.keys(headers)) diff --git a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex index 009be50e7..ea64f163d 100644 --- a/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex +++ b/lib/pleroma/web/mastodon_api/mastodon_api_controller.ex @@ -32,67 +32,55 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end end - def update_credentials(%{assigns: %{user: user}} = conn, params) do - original_user = user - - params = - if bio = params["note"] do - Map.put(params, "bio", bio) - else - params + defp add_if_present( + map, + params, + params_field, + map_field, + value_function \\ fn x -> {:ok, x} end + ) do + if Map.has_key?(params, params_field) do + case value_function.(params[params_field]) do + {:ok, new_value} -> Map.put(map, map_field, new_value) + :error -> map end + else + map + end + end - params = - if name = params["display_name"] do - Map.put(params, "name", name) - else - params - end + def update_credentials(%{assigns: %{user: user}} = conn, params) do + original_user = user - user = - if avatar = params["avatar"] do - with %Plug.Upload{} <- avatar, - {:ok, object} <- ActivityPub.upload(avatar, type: :avatar), - change = Ecto.Changeset.change(user, %{avatar: object.data}), - {:ok, user} = User.update_and_set_cache(change) do - user + user_params = + %{} + |> add_if_present(params, "display_name", :name) + |> add_if_present(params, "note", :bio, fn value -> {:ok, User.parse_bio(value)} end) + |> add_if_present(params, "avatar", :avatar, fn value -> + with %Plug.Upload{} <- value, + {:ok, object} <- ActivityPub.upload(value, type: :avatar) do + {:ok, object.data} else - _e -> user + _ -> :error end - else - user - end + end) - user = - if banner = params["header"] do - with %Plug.Upload{} <- banner, - {:ok, object} <- ActivityPub.upload(banner, type: :banner), - new_info <- Map.put(user.info, "banner", object.data), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user + info_params = + %{} + |> add_if_present(params, "locked", :locked, fn value -> {:ok, value == "true"} end) + |> add_if_present(params, "header", :banner, fn value -> + with %Plug.Upload{} <- value, + {:ok, object} <- ActivityPub.upload(value, type: :banner) do + {:ok, object.data} else - _e -> user + _ -> :error end - else - user - end + end) - user = - if locked = params["locked"] do - with locked <- locked == "true", - new_info <- Map.put(user.info, "locked", locked), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user - else - _e -> user - end - else - user - end + info_cng = User.Info.mastodon_profile_update(user.info, info_params) - with changeset <- User.update_changeset(user, params), + with changeset <- User.update_changeset(user, user_params), + changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), {:ok, user} <- User.update_and_set_cache(changeset) do if original_user != user do CommonAPI.update(user) @@ -644,7 +632,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do # TODO: Use proper query def blocks(%{assigns: %{user: user}} = conn, _) do - with blocked_users <- user.info["blocks"] || [], + with blocked_users <- user.info.blocks || [], accounts <- Enum.map(blocked_users, fn ap_id -> User.get_cached_by_ap_id(ap_id) end) do res = AccountView.render("accounts.json", users: accounts, for: user, as: :user) json(conn, res) @@ -652,7 +640,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do end def domain_blocks(%{assigns: %{user: %{info: info}}} = conn, _) do - json(conn, info["domain_blocks"] || []) + json(conn, info.domain_blocks || []) end def block_domain(%{assigns: %{user: blocker}} = conn, %{"domain" => domain}) do @@ -900,11 +888,11 @@ 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 }, compose: %{ me: "#{user.id}", - default_privacy: user.info["default_scope"] || "public", + default_privacy: user.info.default_scope, default_sensitive: false }, media_attachments: %{ @@ -924,7 +912,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ] }, settings: - Map.get(user.info, "settings") || + Map.get(user.info, :settings) || %{ onboarded: true, home: %{ @@ -1180,7 +1168,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do user = user.nickname url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user) - with {:ok, %{status_code: 200, body: body}} <- + with {:ok, %{status: 200, body: body}} <- @httpoison.get(url, [], timeout: timeout, recv_timeout: timeout), {:ok, data} <- Jason.decode(body) do data2 = diff --git a/lib/pleroma/web/mastodon_api/views/account_view.ex b/lib/pleroma/web/mastodon_api/views/account_view.ex index b68845e16..bcfa8836e 100644 --- a/lib/pleroma/web/mastodon_api/views/account_view.ex +++ b/lib/pleroma/web/mastodon_api/views/account_view.ex @@ -14,10 +14,10 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do image = User.avatar_url(user) |> MediaProxy.url() header = User.banner_url(user) |> MediaProxy.url() user_info = User.user_info(user) - bot = (user.info["source_data"]["type"] || "Person") in ["Application", "Service"] + bot = (user.info.source_data["type"] || "Person") in ["Application", "Service"] emojis = - (user.info["source_data"]["tag"] || []) + (user.info.source_data["tag"] || []) |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> %{ @@ -29,7 +29,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do end) fields = - (user.info["source_data"]["attachment"] || []) + (user.info.source_data["attachment"] || []) |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) diff --git a/lib/pleroma/web/media_proxy/controller.ex b/lib/pleroma/web/media_proxy/controller.ex index 81ea5d510..e1b87e026 100644 --- a/lib/pleroma/web/media_proxy/controller.ex +++ b/lib/pleroma/web/media_proxy/controller.ex @@ -5,12 +5,12 @@ defmodule Pleroma.Web.MediaProxy.MediaProxyController do @default_proxy_opts [max_body_length: 25 * 1_048_576] def remote(conn, params = %{"sig" => sig64, "url" => url64}) do - with config <- Pleroma.Config.get([:media_proxy]), + with config <- Pleroma.Config.get([:media_proxy], []), true <- Keyword.get(config, :enabled, false), {:ok, url} <- MediaProxy.decode_url(sig64, url64), filename <- Path.basename(URI.parse(url).path), :ok <- filename_matches(Map.has_key?(params, "filename"), conn.request_path, url) do - ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_length)) + ReverseProxy.call(conn, url, Keyword.get(config, :proxy_opts, @default_proxy_opts)) else false -> send_resp(conn, 404, Plug.Conn.Status.reason_phrase(404)) diff --git a/lib/pleroma/web/ostatus/ostatus.ex b/lib/pleroma/web/ostatus/ostatus.ex index 1d0019d3b..67df354db 100644 --- a/lib/pleroma/web/ostatus/ostatus.ex +++ b/lib/pleroma/web/ostatus/ostatus.ex @@ -226,25 +226,21 @@ defmodule Pleroma.Web.OStatus do old_data = %{ avatar: user.avatar, bio: user.bio, - name: user.name, - info: user.info + name: user.name } with false <- user.local, avatar <- make_avatar_object(doc), bio <- string_from_xpath("//author[1]/summary", doc), name <- string_from_xpath("//author[1]/poco:displayName", doc), - info <- - Map.put(user.info, "banner", make_avatar_object(doc, "header") || user.info["banner"]), new_data <- %{ avatar: avatar || old_data.avatar, name: name || old_data.name, - bio: bio || old_data.bio, - info: info || old_data.info + bio: bio || old_data.bio }, false <- new_data == old_data do change = Ecto.Changeset.change(user, new_data) - Repo.update(change) + User.update_and_set_cache(change) else _ -> {:ok, user} @@ -350,13 +346,15 @@ defmodule Pleroma.Web.OStatus do def fetch_activity_from_atom_url(url) do with true <- String.starts_with?(url, "http"), - {:ok, %{body: body, status_code: code}} when code in 200..299 <- + {:ok, %{body: body, status: code}} when code in 200..299 <- @httpoison.get( url, [Accept: "application/atom+xml"], follow_redirect: true, - timeout: 10000, - recv_timeout: 20000 + adapter: [ + timeout: 10000, + recv_timeout: 20000 + ] ) do Logger.debug("Got document from #{url}, handling...") handle_incoming(body) diff --git a/lib/pleroma/web/router.ex b/lib/pleroma/web/router.ex index 09265954a..d6a9d5779 100644 --- a/lib/pleroma/web/router.ex +++ b/lib/pleroma/web/router.ex @@ -297,12 +297,6 @@ defmodule Pleroma.Web.Router do post("/account/update_profile_banner", TwitterAPI.Controller, :update_banner) post("/qvitter/update_background_image", TwitterAPI.Controller, :update_background) - post( - "/account/most_recent_notification", - TwitterAPI.Controller, - :update_most_recent_notification - ) - get("/statuses/home_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) diff --git a/lib/pleroma/web/salmon/salmon.ex b/lib/pleroma/web/salmon/salmon.ex index 562ec3d9c..97251c05e 100644 --- a/lib/pleroma/web/salmon/salmon.ex +++ b/lib/pleroma/web/salmon/salmon.ex @@ -157,15 +157,17 @@ defmodule Pleroma.Web.Salmon do |> Enum.filter(fn user -> user && !user.local end) end - defp send_to_user(%{info: %{"salmon" => salmon}}, feed, poster) do - with {:ok, %{status_code: code}} <- + defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do + with {:ok, %{status: code}} <- poster.( salmon, feed, [{"Content-Type", "application/magic-envelope+xml"}], - timeout: 10000, - recv_timeout: 20000, - hackney: [pool: :default] + adapter: [ + timeout: 10000, + recv_timeout: 20000, + pool: :default + ] ) do Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end) else @@ -185,7 +187,7 @@ defmodule Pleroma.Web.Salmon do ] def publish(user, activity, poster \\ &@httpoison.post/4) - def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster) + def publish(%{info: %{keys: keys}} = user, %{data: %{"type" => type}} = activity, poster) when type in @supported_activities do feed = ActivityRepresenter.to_simple_form(activity, user, true) diff --git a/lib/pleroma/web/streamer.ex b/lib/pleroma/web/streamer.ex index 306598157..99b8b7063 100644 --- a/lib/pleroma/web/streamer.ex +++ b/lib/pleroma/web/streamer.ex @@ -188,7 +188,7 @@ defmodule Pleroma.Web.Streamer do # Get the current user so we have up-to-date blocks etc. if socket.assigns[:user] do user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id) - blocks = user.info["blocks"] || [] + blocks = user.info.blocks || [] parent = Object.normalize(item.data["object"]) @@ -206,7 +206,7 @@ defmodule Pleroma.Web.Streamer do # Get the current user so we have up-to-date blocks etc. if socket.assigns[:user] do user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id) - blocks = user.info["blocks"] || [] + blocks = user.info.blocks || [] unless item.actor in blocks do send(socket.transport_pid, {:text, represent_update(item, user)}) diff --git a/lib/pleroma/web/twitter_api/twitter_api.ex b/lib/pleroma/web/twitter_api/twitter_api.ex index 6223580e1..c19a4f084 100644 --- a/lib/pleroma/web/twitter_api/twitter_api.ex +++ b/lib/pleroma/web/twitter_api/twitter_api.ex @@ -132,7 +132,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do params = %{ nickname: params["nickname"], name: params["fullname"], - bio: params["bio"], + bio: User.parse_bio(params["bio"]), email: params["email"], password: params["password"], password_confirmation: params["confirm"] @@ -148,7 +148,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do cond do registrations_open || (!is_nil(token) && !token.used) -> - changeset = User.register_changeset(%User{}, params) + changeset = User.register_changeset(%User{info: %{}}, params) with {:ok, user} <- Repo.insert(changeset) do !registrations_open && UserInviteToken.mark_as_used(token.token) @@ -279,14 +279,6 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do def get_external_profile(for_user, uri) do with %User{} = user <- User.get_or_fetch(uri) do - spawn(fn -> - with url <- user.info["topic"], - {:ok, %{body: body}} <- - @httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000) do - OStatus.handle_incoming(body) - end - end) - {:ok, UserView.render("show.json", %{user: user, for: for_user})} else _e -> diff --git a/lib/pleroma/web/twitter_api/twitter_api_controller.ex b/lib/pleroma/web/twitter_api/twitter_api_controller.ex index 064730867..961250d92 100644 --- a/lib/pleroma/web/twitter_api/twitter_api_controller.ex +++ b/lib/pleroma/web/twitter_api/twitter_api_controller.ex @@ -300,9 +300,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do def update_banner(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(%{"img" => params["banner"]}, type: :banner), - new_info <- Map.put(user.info, "banner", object.data), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do + new_info <- %{"banner" => object.data}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, user} <- User.update_and_set_cache(changeset) do CommonAPI.update(user) %{"url" => [%{"href" => href} | _]} = object.data response = %{url: href} |> Jason.encode!() @@ -314,9 +315,10 @@ defmodule Pleroma.Web.TwitterAPI.Controller do def update_background(%{assigns: %{user: user}} = conn, params) do with {:ok, object} <- ActivityPub.upload(params, type: :background), - new_info <- Map.put(user.info, "background", object.data), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, _user} <- User.update_and_set_cache(change) do + new_info <- %{"background" => object.data}, + info_cng <- User.Info.profile_update(user.info, new_info), + changeset <- Ecto.Changeset.change(user) |> Ecto.Changeset.put_embed(:info, info_cng), + {:ok, _user} <- User.update_and_set_cache(changeset) do %{"url" => [%{"href" => href} | _]} = object.data response = %{url: href} |> Jason.encode!() @@ -338,20 +340,6 @@ defmodule Pleroma.Web.TwitterAPI.Controller do end end - def update_most_recent_notification(%{assigns: %{user: user}} = conn, %{"id" => id}) do - with id when is_number(id) <- String.to_integer(id), - info <- user.info, - mrn <- max(id, user.info["most_recent_notification"] || 0), - updated_info <- Map.put(info, "most_recent_notification", mrn), - changeset <- User.info_changeset(user, %{info: updated_info}), - {:ok, _user} <- User.update_and_set_cache(changeset) do - conn - |> json_reply(200, Jason.encode!(mrn)) - else - _e -> bad_request_reply(conn, "Can't update.") - end - end - def followers(conn, params) do with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params), {:ok, followers} <- User.get_followers(user) do @@ -439,67 +427,41 @@ defmodule Pleroma.Web.TwitterAPI.Controller do json(conn, []) end - def update_profile(%{assigns: %{user: user}} = conn, params) do - params = - if bio = params["description"] do - mentions = Formatter.parse_mentions(bio) - tags = Formatter.parse_tags(bio) - - emoji = - (user.info["source_data"]["tag"] || []) - |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) - |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> - {String.trim(name, ":"), url} - end) - - bio_html = CommonUtils.format_input(bio, mentions, tags, "text/plain") - Map.put(params, "bio", bio_html |> Formatter.emojify(emoji)) - else - params - end - - user = - if locked = params["locked"] do - with locked <- locked == "true", - new_info <- Map.put(user.info, "locked", locked), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user + defp build_info_cng(user, params) do + info_params = + ["no_rich_text", "locked"] + |> Enum.reduce(%{}, fn key, res -> + if value = params[key] do + Map.put(res, key, value == "true") else - _e -> user + res end - else - user - end + end) - user = - if no_rich_text = params["no_rich_text"] do - with no_rich_text <- no_rich_text == "true", - new_info <- Map.put(user.info, "no_rich_text", no_rich_text), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user - else - _e -> user - end + info_params = + if value = params["default_scope"] do + Map.put(info_params, "default_scope", value) else - user + info_params end - user = - if default_scope = params["default_scope"] do - with new_info <- Map.put(user.info, "default_scope", default_scope), - change <- User.info_changeset(user, %{info: new_info}), - {:ok, user} <- User.update_and_set_cache(change) do - user - else - _e -> user - end - else - user - end + User.Info.profile_update(user.info, info_params) + end + + defp parse_profile_bio(user, params) do + if bio = params["description"] do + Map.put(params, "bio", User.parse_bio(bio, user)) + else + params + end + end + + def update_profile(%{assigns: %{user: user}} = conn, params) do + params = parse_profile_bio(user, params) + info_cng = build_info_cng(user, params) with changeset <- User.update_changeset(user, params), + changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng), {:ok, user} <- User.update_and_set_cache(changeset) do CommonAPI.update(user) render(conn, UserView, "user.json", %{user: user, for: user}) diff --git a/lib/pleroma/web/twitter_api/views/user_view.ex b/lib/pleroma/web/twitter_api/views/user_view.ex index a100a1127..b78024ed7 100644 --- a/lib/pleroma/web/twitter_api/views/user_view.ex +++ b/lib/pleroma/web/twitter_api/views/user_view.ex @@ -31,7 +31,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do user_info = User.get_cached_user_info(user) emoji = - (user.info["source_data"]["tag"] || []) + (user.info.source_data["tag"] || []) |> Enum.filter(fn %{"type" => t} -> t == "Emoji" end) |> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} -> {String.trim(name, ":"), url} @@ -40,7 +40,7 @@ defmodule Pleroma.Web.TwitterAPI.UserView do # ``fields`` is an array of mastodon profile field, containing ``{"name": "…", "value": "…"}``. # For example: [{"name": "Pronoun", "value": "she/her"}, …] fields = - (user.info["source_data"]["attachment"] || []) + (user.info.source_data["attachment"] || []) |> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) @@ -66,17 +66,17 @@ 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 }, "screen_name" => user.nickname, "statuses_count" => user_info[:note_count], "statusnet_profile_url" => user.ap_id, "cover_photo" => User.banner_url(user) |> MediaProxy.url(), - "background_image" => image_url(user.info["background"]) |> MediaProxy.url(), + "background_image" => image_url(user.info.background) |> MediaProxy.url(), "is_local" => user.local, - "locked" => !!user.info["locked"], - "default_scope" => user.info["default_scope"] || "public", - "no_rich_text" => user.info["no_rich_text"] || false, + "locked" => user.info.locked, + "default_scope" => user.info.default_scope, + "no_rich_text" => user.info.no_rich_text, "fields" => fields } diff --git a/lib/pleroma/web/web_finger/web_finger.ex b/lib/pleroma/web/web_finger/web_finger.ex index 9f554d286..99c65a6bf 100644 --- a/lib/pleroma/web/web_finger/web_finger.ex +++ b/lib/pleroma/web/web_finger/web_finger.ex @@ -45,7 +45,7 @@ defmodule Pleroma.Web.WebFinger do def represent_user(user, "JSON") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) + {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) magic_key = Salmon.encode_key(public) %{ @@ -83,7 +83,7 @@ defmodule Pleroma.Web.WebFinger do def represent_user(user, "XML") do {:ok, user} = ensure_keys_present(user) - {:ok, _private, public} = Salmon.keys_from_pem(user.info["keys"]) + {:ok, _private, public} = Salmon.keys_from_pem(user.info.keys) magic_key = Salmon.encode_key(public) { @@ -113,16 +113,22 @@ defmodule Pleroma.Web.WebFinger do # This seems a better fit in Salmon def ensure_keys_present(user) do - info = user.info || %{} + info = user.info - if info["keys"] do + if info.keys do {:ok, user} else {:ok, pem} = Salmon.generate_rsa_pem() - info = Map.put(info, "keys", pem) - Ecto.Changeset.change(user, info: info) - |> User.update_and_set_cache() + info_cng = + info + |> Pleroma.User.Info.set_keys(pem) + + cng = + Ecto.Changeset.change(user) + |> Ecto.Changeset.put_embed(:info, info_cng) + + User.update_and_set_cache(cng) end end @@ -214,7 +220,7 @@ defmodule Pleroma.Web.WebFinger do end def find_lrdd_template(domain) do - with {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- + with {:ok, %{status: status, body: body}} when status in 200..299 <- @httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do get_template_from_xml(body) else @@ -253,7 +259,7 @@ defmodule Pleroma.Web.WebFinger do [Accept: "application/xrd+xml,application/jrd+json"], follow_redirect: true ), - {:ok, %{status_code: status_code, body: body}} when status_code in 200..299 <- response do + {:ok, %{status: status, body: body}} when status in 200..299 <- response do doc = XML.parse_document(body) if doc != :error do diff --git a/lib/pleroma/web/websub/websub.ex b/lib/pleroma/web/websub/websub.ex index 396dcf045..0761b5475 100644 --- a/lib/pleroma/web/websub/websub.ex +++ b/lib/pleroma/web/websub/websub.ex @@ -146,7 +146,7 @@ defmodule Pleroma.Web.Websub do end def subscribe(subscriber, subscribed, requester \\ &request_subscription/1) do - topic = subscribed.info["topic"] + topic = subscribed.info.topic # FIXME: Race condition, use transactions {:ok, subscription} = with subscription when not is_nil(subscription) <- @@ -158,7 +158,7 @@ defmodule Pleroma.Web.Websub do _e -> subscription = %WebsubClientSubscription{ topic: topic, - hub: subscribed.info["hub"], + hub: subscribed.info.hub, subscribers: [subscriber.ap_id], state: "requested", secret: :crypto.strong_rand_bytes(8) |> Base.url_encode64(), @@ -173,7 +173,7 @@ defmodule Pleroma.Web.Websub do def gather_feed_data(topic, getter \\ &@httpoison.get/1) do with {:ok, response} <- getter.(topic), - status_code when status_code in 200..299 <- response.status_code, + status when status in 200..299 <- response.status, body <- response.body, doc <- XML.parse_document(body), uri when not is_nil(uri) <- XML.string_from_xpath("/feed/author[1]/uri", doc), @@ -221,7 +221,7 @@ defmodule Pleroma.Web.Websub do task = Task.async(websub_checker) - with {:ok, %{status_code: 202}} <- + with {:ok, %{status: 202}} <- poster.(websub.hub, {:form, data}, "Content-type": "application/x-www-form-urlencoded"), {:ok, websub} <- Task.yield(task, timeout) do {:ok, websub} @@ -257,7 +257,7 @@ defmodule Pleroma.Web.Websub do signature = sign(secret || "", xml) Logger.info(fn -> "Pushing #{topic} to #{callback}" end) - with {:ok, %{status_code: code}} <- + with {:ok, %{status: code}} <- @httpoison.post( callback, xml, @@ -265,9 +265,11 @@ defmodule Pleroma.Web.Websub do {"Content-Type", "application/atom+xml"}, {"X-Hub-Signature", "sha1=#{signature}"} ], - timeout: 10000, - recv_timeout: 20000, - hackney: [pool: :default] + adapter: [ + timeout: 10000, + recv_timeout: 20000, + pool: :default + ] ) do Logger.info(fn -> "Pushed to #{callback}, code #{code}" end) {:ok, code} |