3 community::send_activity_in_community,
8 verify_person_in_community,
10 activity_lists::AnnouncableActivities,
13 community::ApubCommunity,
16 private_message::ApubPrivateMessage,
19 activities::deletion::{delete::Delete, undo_delete::UndoDelete},
23 use activitypub_federation::{
25 fetch::object_id::ObjectId,
27 protocol::verification::verify_domains_match,
28 traits::{Actor, Object},
30 use lemmy_api_common::context::LemmyContext;
31 use lemmy_db_schema::{
32 newtypes::CommunityId,
34 comment::{Comment, CommentUpdateForm},
35 community::{Community, CommunityUpdateForm},
37 post::{Post, PostUpdateForm},
38 private_message::{PrivateMessage, PrivateMessageUpdateForm},
42 use lemmy_utils::error::LemmyError;
50 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
51 /// action was done by a normal user.
52 #[tracing::instrument(skip_all)]
53 pub(crate) async fn send_apub_delete_in_community(
56 object: DeletableObjects,
57 reason: Option<String>,
59 context: &Data<LemmyContext>,
60 ) -> Result<(), LemmyError> {
61 let actor = ApubPerson::from(actor);
62 let is_mod_action = reason.is_some();
63 let activity = if deleted {
64 let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
65 AnnouncableActivities::Delete(delete)
67 let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
68 AnnouncableActivities::UndoDelete(undo)
70 send_activity_in_community(
81 /// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
82 /// action was done by a normal user.
83 #[tracing::instrument(skip_all)]
84 pub(crate) async fn send_apub_delete_in_community_new(
86 community_id: CommunityId,
87 object: DeletableObjects,
88 reason: Option<String>,
90 context: Data<LemmyContext>,
91 ) -> Result<(), LemmyError> {
92 let community = Community::read(&mut context.pool(), community_id).await?;
93 let actor = ApubPerson::from(actor);
94 let is_mod_action = reason.is_some();
95 let activity = if deleted {
96 let delete = Delete::new(&actor, object, public(), Some(&community), reason, &context)?;
97 AnnouncableActivities::Delete(delete)
99 let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, &context)?;
100 AnnouncableActivities::UndoDelete(undo)
102 send_activity_in_community(
113 #[tracing::instrument(skip_all)]
114 pub(crate) async fn send_apub_delete_private_message(
118 context: Data<LemmyContext>,
119 ) -> Result<(), LemmyError> {
120 let recipient_id = pm.recipient_id;
121 let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
125 let deletable = DeletableObjects::PrivateMessage(pm.into());
126 let inbox = vec![recipient.shared_inbox_or_inbox()];
128 let delete = Delete::new(actor, deletable, recipient.id(), None, None, &context)?;
129 send_lemmy_activity(&context, delete, actor, inbox, true).await?;
131 let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, &context)?;
132 send_lemmy_activity(&context, undo, actor, inbox, true).await?;
137 pub enum DeletableObjects {
138 Community(ApubCommunity),
139 Comment(ApubComment),
141 PrivateMessage(ApubPrivateMessage),
144 impl DeletableObjects {
145 #[tracing::instrument(skip_all)]
146 pub(crate) async fn read_from_db(
148 context: &Data<LemmyContext>,
149 ) -> Result<DeletableObjects, LemmyError> {
150 if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
151 return Ok(DeletableObjects::Community(c));
153 if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
154 return Ok(DeletableObjects::Post(p));
156 if let Some(c) = ApubComment::read_from_id(ap_id.clone(), context).await? {
157 return Ok(DeletableObjects::Comment(c));
159 if let Some(p) = ApubPrivateMessage::read_from_id(ap_id.clone(), context).await? {
160 return Ok(DeletableObjects::PrivateMessage(p));
162 Err(diesel::NotFound.into())
165 pub(crate) fn id(&self) -> Url {
167 DeletableObjects::Community(c) => c.id(),
168 DeletableObjects::Comment(c) => c.ap_id.clone().into(),
169 DeletableObjects::Post(p) => p.ap_id.clone().into(),
170 DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
175 #[tracing::instrument(skip_all)]
176 pub(in crate::activities) async fn verify_delete_activity(
179 context: &Data<LemmyContext>,
180 ) -> Result<(), LemmyError> {
181 let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
183 DeletableObjects::Community(community) => {
184 verify_is_public(&activity.to, &[])?;
186 // can only do this check for local community, in remote case it would try to fetch the
187 // deleted community (which fails)
188 verify_person_in_community(&activity.actor, &community, context).await?;
190 // community deletion is always a mod (or admin) action
191 verify_mod_action(&activity.actor, activity.object.id(), community.id, context).await?;
193 DeletableObjects::Post(p) => {
194 verify_is_public(&activity.to, &[])?;
195 verify_delete_post_or_comment(
197 &p.ap_id.clone().into(),
198 &activity.community(context).await?,
204 DeletableObjects::Comment(c) => {
205 verify_is_public(&activity.to, &[])?;
206 verify_delete_post_or_comment(
208 &c.ap_id.clone().into(),
209 &activity.community(context).await?,
215 DeletableObjects::PrivateMessage(_) => {
216 verify_person(&activity.actor, context).await?;
217 verify_domains_match(activity.actor.inner(), activity.object.id())?;
223 #[tracing::instrument(skip_all)]
224 async fn verify_delete_post_or_comment(
225 actor: &ObjectId<ApubPerson>,
227 community: &ApubCommunity,
229 context: &Data<LemmyContext>,
230 ) -> Result<(), LemmyError> {
231 verify_person_in_community(actor, community, context).await?;
233 verify_mod_action(actor, object_id, community.id, context).await?;
235 // domain of post ap_id and post.creator ap_id are identical, so we just check the former
236 verify_domains_match(actor.inner(), object_id)?;
241 /// Write deletion or restoring of an object to the database, and send websocket message.
242 #[tracing::instrument(skip_all)]
243 async fn receive_delete_action(
245 actor: &ObjectId<ApubPerson>,
247 context: &Data<LemmyContext>,
248 ) -> Result<(), LemmyError> {
249 match DeletableObjects::read_from_db(object, context).await? {
250 DeletableObjects::Community(community) => {
252 let mod_: Person = actor.dereference(context).await?.deref().clone();
253 let object = DeletableObjects::Community(community.clone());
254 let c: Community = community.deref().deref().clone();
255 send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
261 &CommunityUpdateForm::builder()
262 .deleted(Some(deleted))
267 DeletableObjects::Post(post) => {
268 if deleted != post.deleted {
272 &PostUpdateForm::builder().deleted(Some(deleted)).build(),
277 DeletableObjects::Comment(comment) => {
278 if deleted != comment.deleted {
282 &CommentUpdateForm::builder().deleted(Some(deleted)).build(),
287 DeletableObjects::PrivateMessage(pm) => {
288 PrivateMessage::update(
291 &PrivateMessageUpdateForm::builder()
292 .deleted(Some(deleted))