]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/deletion/delete.rs
Sanitize html (#3708)
[lemmy.git] / crates / apub / src / activities / deletion / delete.rs
1 use crate::{
2   activities::{
3     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
4     generate_activity_id,
5   },
6   insert_received_activity,
7   objects::person::ApubPerson,
8   protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
9 };
10 use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::ActivityHandler};
11 use lemmy_api_common::{context::LemmyContext, utils::sanitize_html_opt};
12 use lemmy_db_schema::{
13   source::{
14     comment::{Comment, CommentUpdateForm},
15     community::{Community, CommunityUpdateForm},
16     moderator::{
17       ModRemoveComment,
18       ModRemoveCommentForm,
19       ModRemoveCommunity,
20       ModRemoveCommunityForm,
21       ModRemovePost,
22       ModRemovePostForm,
23     },
24     post::{Post, PostUpdateForm},
25   },
26   traits::Crud,
27 };
28 use lemmy_utils::error::{LemmyError, LemmyErrorType};
29 use url::Url;
30
31 #[async_trait::async_trait]
32 impl ActivityHandler for Delete {
33   type DataType = LemmyContext;
34   type Error = LemmyError;
35
36   fn id(&self) -> &Url {
37     &self.id
38   }
39
40   fn actor(&self) -> &Url {
41     self.actor.inner()
42   }
43
44   #[tracing::instrument(skip_all)]
45   async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
46     insert_received_activity(&self.id, context).await?;
47     verify_delete_activity(self, self.summary.is_some(), context).await?;
48     Ok(())
49   }
50
51   #[tracing::instrument(skip_all)]
52   async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
53     if let Some(reason) = self.summary {
54       // We set reason to empty string if it doesn't exist, to distinguish between delete and
55       // remove. Here we change it back to option, so we don't write it to db.
56       let reason = if reason.is_empty() {
57         None
58       } else {
59         Some(reason)
60       };
61       receive_remove_action(
62         &self.actor.dereference(context).await?,
63         self.object.id(),
64         reason,
65         context,
66       )
67       .await
68     } else {
69       receive_delete_action(self.object.id(), &self.actor, true, context).await
70     }
71   }
72 }
73
74 impl Delete {
75   pub(in crate::activities::deletion) fn new(
76     actor: &ApubPerson,
77     object: DeletableObjects,
78     to: Url,
79     community: Option<&Community>,
80     summary: Option<String>,
81     context: &Data<LemmyContext>,
82   ) -> Result<Delete, LemmyError> {
83     let id = generate_activity_id(
84       DeleteType::Delete,
85       &context.settings().get_protocol_and_hostname(),
86     )?;
87     let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
88     Ok(Delete {
89       actor: actor.actor_id.clone().into(),
90       to: vec![to],
91       object: IdOrNestedObject::Id(object.id()),
92       cc: cc.into_iter().collect(),
93       kind: DeleteType::Delete,
94       summary,
95       id,
96       audience: community.map(|c| c.actor_id.clone().into()),
97     })
98   }
99 }
100
101 #[tracing::instrument(skip_all)]
102 pub(in crate::activities) async fn receive_remove_action(
103   actor: &ApubPerson,
104   object: &Url,
105   reason: Option<String>,
106   context: &Data<LemmyContext>,
107 ) -> Result<(), LemmyError> {
108   let reason = sanitize_html_opt(&reason);
109
110   match DeletableObjects::read_from_db(object, context).await? {
111     DeletableObjects::Community(community) => {
112       if community.local {
113         return Err(LemmyErrorType::OnlyLocalAdminCanRemoveCommunity)?;
114       }
115       let form = ModRemoveCommunityForm {
116         mod_person_id: actor.id,
117         community_id: community.id,
118         removed: Some(true),
119         reason,
120         expires: None,
121       };
122       ModRemoveCommunity::create(&mut context.pool(), &form).await?;
123       Community::update(
124         &mut context.pool(),
125         community.id,
126         &CommunityUpdateForm::builder().removed(Some(true)).build(),
127       )
128       .await?;
129     }
130     DeletableObjects::Post(post) => {
131       let form = ModRemovePostForm {
132         mod_person_id: actor.id,
133         post_id: post.id,
134         removed: Some(true),
135         reason,
136       };
137       ModRemovePost::create(&mut context.pool(), &form).await?;
138       Post::update(
139         &mut context.pool(),
140         post.id,
141         &PostUpdateForm::builder().removed(Some(true)).build(),
142       )
143       .await?;
144     }
145     DeletableObjects::Comment(comment) => {
146       let form = ModRemoveCommentForm {
147         mod_person_id: actor.id,
148         comment_id: comment.id,
149         removed: Some(true),
150         reason,
151       };
152       ModRemoveComment::create(&mut context.pool(), &form).await?;
153       Comment::update(
154         &mut context.pool(),
155         comment.id,
156         &CommentUpdateForm::builder().removed(Some(true)).build(),
157       )
158       .await?;
159     }
160     DeletableObjects::PrivateMessage(_) => unimplemented!(),
161   }
162   Ok(())
163 }