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