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