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