]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
Moving settings to Database. (#2492)
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     community::announce::GetCommunity,
4     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
5     generate_activity_id,
6   },
7   check_apub_id_valid,
8   fetch_local_site_data,
9   local_instance,
10   objects::{community::ApubCommunity, person::ApubPerson},
11   protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
12 };
13 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
14 use activitystreams_kinds::activity::DeleteType;
15 use anyhow::anyhow;
16 use lemmy_api_common::utils::blocking;
17 use lemmy_db_schema::{
18   source::{
19     comment::{Comment, CommentUpdateForm},
20     community::{Community, CommunityUpdateForm},
21     moderator::{
22       ModRemoveComment,
23       ModRemoveCommentForm,
24       ModRemoveCommunity,
25       ModRemoveCommunityForm,
26       ModRemovePost,
27       ModRemovePostForm,
28     },
29     post::{Post, PostUpdateForm},
30   },
31   traits::Crud,
32 };
33 use lemmy_utils::error::LemmyError;
34 use lemmy_websocket::{
35   send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
36   LemmyContext,
37   UserOperationCrud,
38 };
39 use url::Url;
40
41 #[async_trait::async_trait(?Send)]
42 impl ActivityHandler for Delete {
43   type DataType = LemmyContext;
44   type Error = LemmyError;
45
46   fn id(&self) -> &Url {
47     &self.id
48   }
49
50   fn actor(&self) -> &Url {
51     self.actor.inner()
52   }
53
54   #[tracing::instrument(skip_all)]
55   async fn verify(
56     &self,
57     context: &Data<LemmyContext>,
58     request_counter: &mut i32,
59   ) -> Result<(), LemmyError> {
60     let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
61     check_apub_id_valid(self.id(), &local_site_data, context.settings())
62       .map_err(LemmyError::from_message)?;
63     verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
64     Ok(())
65   }
66
67   #[tracing::instrument(skip_all)]
68   async fn receive(
69     self,
70     context: &Data<LemmyContext>,
71     request_counter: &mut i32,
72   ) -> Result<(), LemmyError> {
73     if let Some(reason) = self.summary {
74       // We set reason to empty string if it doesn't exist, to distinguish between delete and
75       // remove. Here we change it back to option, so we don't write it to db.
76       let reason = if reason.is_empty() {
77         None
78       } else {
79         Some(reason)
80       };
81       receive_remove_action(
82         &self
83           .actor
84           .dereference(context, local_instance(context), request_counter)
85           .await?,
86         self.object.id(),
87         reason,
88         context,
89       )
90       .await
91     } else {
92       receive_delete_action(
93         self.object.id(),
94         &self.actor,
95         true,
96         context,
97         request_counter,
98       )
99       .await
100     }
101   }
102 }
103
104 impl Delete {
105   pub(in crate::activities::deletion) fn new(
106     actor: &ApubPerson,
107     object: DeletableObjects,
108     to: Url,
109     community: Option<&Community>,
110     summary: Option<String>,
111     context: &LemmyContext,
112   ) -> Result<Delete, LemmyError> {
113     let id = generate_activity_id(
114       DeleteType::Delete,
115       &context.settings().get_protocol_and_hostname(),
116     )?;
117     let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
118     Ok(Delete {
119       actor: ObjectId::new(actor.actor_id.clone()),
120       to: vec![to],
121       object: IdOrNestedObject::Id(object.id()),
122       cc: cc.into_iter().collect(),
123       kind: DeleteType::Delete,
124       summary,
125       id,
126       unparsed: Default::default(),
127     })
128   }
129 }
130
131 #[tracing::instrument(skip_all)]
132 pub(in crate::activities) async fn receive_remove_action(
133   actor: &ApubPerson,
134   object: &Url,
135   reason: Option<String>,
136   context: &LemmyContext,
137 ) -> Result<(), LemmyError> {
138   use UserOperationCrud::*;
139   match DeletableObjects::read_from_db(object, context).await? {
140     DeletableObjects::Community(community) => {
141       if community.local {
142         return Err(LemmyError::from_message(
143           "Only local admin can remove community",
144         ));
145       }
146       let form = ModRemoveCommunityForm {
147         mod_person_id: actor.id,
148         community_id: community.id,
149         removed: Some(true),
150         reason,
151         expires: None,
152       };
153       blocking(context.pool(), move |conn| {
154         ModRemoveCommunity::create(conn, &form)
155       })
156       .await??;
157       let deleted_community = blocking(context.pool(), move |conn| {
158         Community::update(
159           conn,
160           community.id,
161           &CommunityUpdateForm::builder().removed(Some(true)).build(),
162         )
163       })
164       .await??;
165
166       send_community_ws_message(deleted_community.id, RemoveCommunity, None, None, context).await?;
167     }
168     DeletableObjects::Post(post) => {
169       let form = ModRemovePostForm {
170         mod_person_id: actor.id,
171         post_id: post.id,
172         removed: Some(true),
173         reason,
174       };
175       blocking(context.pool(), move |conn| {
176         ModRemovePost::create(conn, &form)
177       })
178       .await??;
179       let removed_post = blocking(context.pool(), move |conn| {
180         Post::update(
181           conn,
182           post.id,
183           &PostUpdateForm::builder().removed(Some(true)).build(),
184         )
185       })
186       .await??;
187
188       send_post_ws_message(removed_post.id, RemovePost, None, None, context).await?;
189     }
190     DeletableObjects::Comment(comment) => {
191       let form = ModRemoveCommentForm {
192         mod_person_id: actor.id,
193         comment_id: comment.id,
194         removed: Some(true),
195         reason,
196       };
197       blocking(context.pool(), move |conn| {
198         ModRemoveComment::create(conn, &form)
199       })
200       .await??;
201       let removed_comment = blocking(context.pool(), move |conn| {
202         Comment::update(
203           conn,
204           comment.id,
205           &CommentUpdateForm::builder().removed(Some(true)).build(),
206         )
207       })
208       .await??;
209
210       send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
211     }
212     DeletableObjects::PrivateMessage(_) => unimplemented!(),
213   }
214   Ok(())
215 }
216
217 #[async_trait::async_trait(?Send)]
218 impl GetCommunity for Delete {
219   #[tracing::instrument(skip_all)]
220   async fn get_community(
221     &self,
222     context: &LemmyContext,
223     _request_counter: &mut i32,
224   ) -> Result<ApubCommunity, LemmyError> {
225     let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
226       DeletableObjects::Community(c) => c.id,
227       DeletableObjects::Comment(c) => {
228         let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
229         post.community_id
230       }
231       DeletableObjects::Post(p) => p.community_id,
232       DeletableObjects::PrivateMessage(_) => {
233         return Err(anyhow!("Private message is not part of community").into())
234       }
235     };
236     let community = blocking(context.pool(), move |conn| {
237       Community::read(conn, community_id)
238     })
239     .await??;
240     Ok(community.into())
241   }
242 }