]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
Merge pull request #1660 from LemmyNet/merge-apub-crates
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     comment::send_websocket_message as send_comment_message,
4     community::send_websocket_message as send_community_message,
5     post::send_websocket_message as send_post_message,
6     verify_activity,
7     verify_mod_action,
8     verify_person_in_community,
9   },
10   fetcher::{
11     community::get_or_fetch_and_upsert_community,
12     objects::get_or_fetch_and_insert_post_or_comment,
13     person::get_or_fetch_and_upsert_person,
14   },
15   ActorType,
16   CommunityType,
17   PostOrComment,
18 };
19 use activitystreams::activity::kind::DeleteType;
20 use lemmy_api_common::blocking;
21 use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler, PublicUrl};
22 use lemmy_db_queries::{
23   source::{comment::Comment_, community::Community_, post::Post_},
24   Crud,
25 };
26 use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
27 use lemmy_utils::LemmyError;
28 use lemmy_websocket::{LemmyContext, UserOperationCrud};
29 use url::Url;
30
31 /// This is very confusing, because there are four distinct cases to handle:
32 /// - user deletes their post
33 /// - user deletes their comment
34 /// - remote community mod deletes local community
35 /// - remote community deletes itself (triggered by a mod)
36 ///
37 /// TODO: we should probably change how community deletions work to simplify this. Probably by
38 /// wrapping it in an announce just like other activities, instead of having the community send it.
39 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
40 #[serde(rename_all = "camelCase")]
41 pub struct DeletePostCommentOrCommunity {
42   to: PublicUrl,
43   pub(in crate::activities::deletion) object: Url,
44   cc: [Url; 1],
45   #[serde(rename = "type")]
46   kind: DeleteType,
47   #[serde(flatten)]
48   common: ActivityCommonFields,
49 }
50
51 #[async_trait::async_trait(?Send)]
52 impl ActivityHandler for DeletePostCommentOrCommunity {
53   async fn verify(
54     &self,
55     context: &LemmyContext,
56     request_counter: &mut i32,
57   ) -> Result<(), LemmyError> {
58     verify_activity(self.common())?;
59     let object_community =
60       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await;
61     // deleting a community (set counter 0 to only fetch from local db)
62     if object_community.is_ok() {
63       verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
64     }
65     // deleting a post or comment
66     else {
67       verify_person_in_community(&self.common().actor, &self.cc, context, request_counter).await?;
68       let object_creator =
69         get_post_or_comment_actor_id(&self.object, context, request_counter).await?;
70       verify_urls_match(&self.common.actor, &object_creator)?;
71     }
72     Ok(())
73   }
74
75   async fn receive(
76     &self,
77     context: &LemmyContext,
78     request_counter: &mut i32,
79   ) -> Result<(), LemmyError> {
80     let object_community =
81       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await;
82     // deleting a community
83     if let Ok(community) = object_community {
84       if community.local {
85         // repeat these checks just to be sure
86         verify_person_in_community(&self.common().actor, &self.cc, context, request_counter)
87           .await?;
88         verify_mod_action(&self.common.actor, self.object.clone(), context).await?;
89         let mod_ =
90           get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
91         community.send_delete(mod_, context).await?;
92       }
93       let deleted_community = blocking(context.pool(), move |conn| {
94         Community::update_deleted(conn, community.id, true)
95       })
96       .await??;
97
98       send_community_message(
99         deleted_community.id,
100         UserOperationCrud::DeleteCommunity,
101         context,
102       )
103       .await
104     }
105     // deleting a post or comment
106     else {
107       match get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await? {
108         PostOrComment::Post(post) => {
109           let deleted_post = blocking(context.pool(), move |conn| {
110             Post::update_deleted(conn, post.id, true)
111           })
112           .await??;
113           send_post_message(deleted_post.id, UserOperationCrud::EditPost, context).await
114         }
115         PostOrComment::Comment(comment) => {
116           let deleted_comment = blocking(context.pool(), move |conn| {
117             Comment::update_deleted(conn, comment.id, true)
118           })
119           .await??;
120           send_comment_message(
121             deleted_comment.id,
122             vec![],
123             UserOperationCrud::EditComment,
124             context,
125           )
126           .await
127         }
128       }
129     }
130   }
131
132   fn common(&self) -> &ActivityCommonFields {
133     &self.common
134   }
135 }
136
137 async fn get_post_or_comment_actor_id(
138   object: &Url,
139   context: &LemmyContext,
140   request_counter: &mut i32,
141 ) -> Result<Url, LemmyError> {
142   let actor_id =
143     match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? {
144       PostOrComment::Post(post) => {
145         let creator_id = post.creator_id;
146         blocking(context.pool(), move |conn| Person::read(conn, creator_id))
147           .await??
148           .actor_id()
149       }
150       PostOrComment::Comment(comment) => {
151         let creator_id = comment.creator_id;
152         blocking(context.pool(), move |conn| Person::read(conn, creator_id))
153           .await??
154           .actor_id()
155       }
156     };
157   Ok(actor_id)
158 }