X-Git-Url: https://git.squeep.com/?a=blobdiff_plain;f=lib%2Fpleroma%2Fuser.ex;h=aba8742a09be5dfd4c639324a7e859f3a72ce1c8;hb=056305dfa745f679ce0082c08abb06725432dc5d;hp=6a8129ac84b2572432d80e2cd77d76b27cbdcd6b;hpb=4fd9df100f61dfe4731934eca86500472d866f60;p=akkoma diff --git a/lib/pleroma/user.ex b/lib/pleroma/user.ex index 6a8129ac8..aba8742a0 100644 --- a/lib/pleroma/user.ex +++ b/lib/pleroma/user.ex @@ -67,7 +67,8 @@ defmodule Pleroma.User do %{ following_count: length(user.following) - oneself, note_count: user.info["note_count"] || 0, - follower_count: user.info["follower_count"] || 0 + follower_count: user.info["follower_count"] || 0, + locked: user.info["locked"] || false } end @@ -167,28 +168,70 @@ defmodule Pleroma.User do end end + def maybe_direct_follow(%User{} = follower, %User{info: info} = followed) do + user_info = user_info(followed) + + should_direct_follow = + cond do + # if the account is locked, don't pre-create the relationship + user_info[:locked] == true -> + false + + # if the users are blocking each other, we shouldn't even be here, but check for it anyway + User.blocks?(follower, followed) == true or User.blocks?(followed, follower) == true -> + false + + # if OStatus, then there is no three-way handshake to follow + User.ap_enabled?(followed) != true -> + true + + # if there are no other reasons not to, just pre-create the relationship + true -> + true + end + + if should_direct_follow do + follow(follower, followed) + else + {:ok, follower} + end + end + + def maybe_follow(%User{} = follower, %User{info: info} = followed) do + if not following?(follower, followed) do + follow(follower, followed) + else + {:ok, follower} + end + end + def follow(%User{} = follower, %User{info: info} = followed) do ap_followers = followed.follower_address - if following?(follower, followed) or info["deactivated"] do - {:error, "Could not follow user: #{followed.nickname} is already on your list."} - else - if !followed.local && follower.local && !ap_enabled?(followed) do - Websub.subscribe(follower, followed) - end + cond do + following?(follower, followed) or info["deactivated"] -> + {:error, "Could not follow user: #{followed.nickname} is already on your list."} - following = - [ap_followers | follower.following] - |> Enum.uniq() + blocks?(followed, follower) -> + {:error, "Could not follow user: #{followed.nickname} blocked you."} - follower = - follower - |> follow_changeset(%{following: following}) - |> update_and_set_cache + true -> + if !followed.local && follower.local && !ap_enabled?(followed) do + Websub.subscribe(follower, followed) + end - {:ok, _} = update_follower_count(followed) + following = + [ap_followers | follower.following] + |> Enum.uniq() - follower + follower = + follower + |> follow_changeset(%{following: following}) + |> update_and_set_cache + + {:ok, _} = update_follower_count(followed) + + follower end end @@ -217,15 +260,19 @@ defmodule Pleroma.User do Enum.member?(follower.following, followed.follower_address) end + def locked?(%User{} = user) do + user.info["locked"] || false + end + def get_by_ap_id(ap_id) do Repo.get_by(User, ap_id: ap_id) end def update_and_set_cache(changeset) do with {:ok, user} <- Repo.update(changeset) do - Cachex.set(:user_cache, "ap_id:#{user.ap_id}", user) - Cachex.set(:user_cache, "nickname:#{user.nickname}", user) - Cachex.set(:user_cache, "user_info:#{user.id}", user_info(user)) + Cachex.put(:user_cache, "ap_id:#{user.ap_id}", user) + Cachex.put(:user_cache, "nickname:#{user.nickname}", user) + Cachex.put(:user_cache, "user_info:#{user.id}", user_info(user)) {:ok, user} else e -> e @@ -239,12 +286,12 @@ defmodule Pleroma.User do def get_cached_by_ap_id(ap_id) do key = "ap_id:#{ap_id}" - Cachex.get!(:user_cache, key, fallback: fn _ -> get_by_ap_id(ap_id) end) + Cachex.fetch!(:user_cache, key, fn _ -> get_by_ap_id(ap_id) end) end def get_cached_by_nickname(nickname) do key = "nickname:#{nickname}" - Cachex.get!(:user_cache, key, fallback: fn _ -> get_or_fetch_by_nickname(nickname) end) + Cachex.fetch!(:user_cache, key, fn _ -> get_or_fetch_by_nickname(nickname) end) end def get_by_nickname(nickname) do @@ -260,7 +307,7 @@ defmodule Pleroma.User do def get_cached_user_info(user) do key = "user_info:#{user.id}" - Cachex.get!(:user_cache, key, fallback: fn _ -> user_info(user) end) + Cachex.fetch!(:user_cache, key, fn _ -> user_info(user) end) end def fetch_by_nickname(nickname) do @@ -314,6 +361,40 @@ defmodule Pleroma.User do {:ok, Repo.all(q)} end + def get_follow_requests_query(%User{} = user) do + from( + a in Activity, + where: + fragment( + "? ->> 'type' = 'Follow'", + a.data + ), + where: + fragment( + "? ->> 'state' = 'pending'", + a.data + ), + where: + fragment( + "? @> ?", + a.data, + ^%{"object" => user.ap_id} + ) + ) + end + + def get_follow_requests(%User{} = user) do + q = get_follow_requests_query(user) + reqs = Repo.all(q) + + users = + Enum.map(reqs, fn req -> req.actor end) + |> Enum.uniq() + |> Enum.map(fn ap_id -> get_by_ap_id(ap_id) end) + + {:ok, users} + 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) @@ -424,15 +505,33 @@ defmodule Pleroma.User do Repo.all(q) end - def block(user, %{ap_id: ap_id}) do - blocks = user.info["blocks"] || [] + def block(blocker, %User{ap_id: ap_id} = blocked) do + # sever any follow relationships to prevent leaks per activitypub (Pleroma issue #213) + blocker = + if following?(blocker, blocked) do + {:ok, blocker, _} = unfollow(blocker, blocked) + blocker + else + blocker + end + + if following?(blocked, blocker) do + unfollow(blocked, blocker) + end + + blocks = blocker.info["blocks"] || [] new_blocks = Enum.uniq([ap_id | blocks]) - new_info = Map.put(user.info, "blocks", new_blocks) + new_info = Map.put(blocker.info, "blocks", new_blocks) - cs = User.info_changeset(user, %{info: new_info}) + cs = User.info_changeset(blocker, %{info: new_info}) update_and_set_cache(cs) end + # helper to handle the block given only an actor's AP id + def block(blocker, %{ap_id: ap_id}) 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) @@ -444,7 +543,31 @@ defmodule Pleroma.User do def blocks?(user, %{ap_id: ap_id}) do blocks = user.info["blocks"] || [] - Enum.member?(blocks, ap_id) + domain_blocks = user.info["domain_blocks"] || [] + %{host: host} = URI.parse(ap_id) + + Enum.member?(blocks, ap_id) || + Enum.any?(domain_blocks, fn domain -> + host == domain + end) + 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) + + cs = User.info_changeset(user, %{info: new_info}) + update_and_set_cache(cs) + 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) + + cs = User.info_changeset(user, %{info: new_info}) + update_and_set_cache(cs) end def local_user_query() do