get_local_user_view_from_jwt,
is_mod_or_admin,
};
-use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{
source::{
comment::Comment,
)
.await?;
+ let res = send_comment_ws_message(
+ data.comment_id,
+ UserOperationCrud::DeleteComment,
+ websocket_id,
+ None, // TODO a comment delete might clear forms?
+ Some(local_user_view.person.id),
+ recipient_ids,
+ context,
+ )
+ .await?;
+
// Send the apub message
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
- send_apub_delete(
- &local_user_view.person.clone().into(),
- &community.clone().into(),
- DeletableObjects::Comment(Box::new(updated_comment.into())),
+ let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ community,
+ deletable,
+ None,
deleted,
context,
)
.await?;
- send_comment_ws_message(
- data.comment_id,
- UserOperationCrud::DeleteComment,
- websocket_id,
- None, // TODO a comment delete might clear forms?
- Some(local_user_view.person.id),
- recipient_ids,
- context,
- )
- .await
+ Ok(res)
}
}
)
.await?;
+ let res = send_comment_ws_message(
+ data.comment_id,
+ UserOperationCrud::RemoveComment,
+ websocket_id,
+ None, // TODO maybe this might clear other forms
+ Some(local_user_view.person.id),
+ recipient_ids,
+ context,
+ )
+ .await?;
+
// Send the apub message
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_comment.post.community_id)
})
.await??;
- send_apub_remove(
- &local_user_view.person.clone().into(),
- &community.into(),
- DeletableObjects::Comment(Box::new(updated_comment.into())),
- data.reason.clone().unwrap_or_else(|| "".to_string()),
+ let deletable = DeletableObjects::Comment(Box::new(updated_comment.clone().into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ community,
+ deletable,
+ data.reason.clone().or_else(|| Some("".to_string())),
removed,
context,
)
.await?;
- send_comment_ws_message(
- data.comment_id,
- UserOperationCrud::RemoveComment,
- websocket_id,
- None, // TODO maybe this might clear other forms
- Some(local_user_view.person.id),
- recipient_ids,
- context,
- )
- .await
+ Ok(res)
}
}
use crate::PerformCrud;
use actix_web::web::Data;
use lemmy_api_common::{blocking, community::*, get_local_user_view_from_jwt, is_admin};
-use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{
source::{
community::Community,
.map_err(LemmyError::from)
.map_err(|e| e.with_message("couldnt_update_community"))?;
- // Send apub messages
- send_apub_delete(
- &local_user_view.person.clone().into(),
- &updated_community.clone().into(),
- DeletableObjects::Community(Box::new(updated_community.into())),
- deleted,
- context,
- )
- .await?;
-
- send_community_ws_message(
+ let res = send_community_ws_message(
data.community_id,
UserOperationCrud::DeleteCommunity,
websocket_id,
Some(local_user_view.person.id),
context,
)
- .await
+ .await?;
+
+ // Send apub messages
+ let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ updated_community,
+ deletable,
+ None,
+ deleted,
+ context,
+ )
+ .await?;
+
+ Ok(res)
}
}
})
.await??;
- // Apub messages
- send_apub_remove(
- &local_user_view.person.clone().into(),
- &updated_community.clone().into(),
- DeletableObjects::Community(Box::new(updated_community.into())),
- data.reason.clone().unwrap_or_else(|| "".to_string()),
- removed,
- context,
- )
- .await?;
-
- send_community_ws_message(
+ let res = send_community_ws_message(
data.community_id,
UserOperationCrud::RemoveCommunity,
websocket_id,
Some(local_user_view.person.id),
context,
)
- .await
+ .await?;
+
+ // Apub messages
+ let deletable = DeletableObjects::Community(Box::new(updated_community.clone().into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ updated_community,
+ deletable,
+ data.reason.clone().or_else(|| Some("".to_string())),
+ removed,
+ context,
+ )
+ .await?;
+ Ok(res)
}
}
is_mod_or_admin,
post::*,
};
-use lemmy_apub::activities::deletion::{send_apub_delete, send_apub_remove, DeletableObjects};
+use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{
source::{
community::Community,
})
.await??;
+ let res = send_post_ws_message(
+ data.post_id,
+ UserOperationCrud::DeletePost,
+ websocket_id,
+ Some(local_user_view.person.id),
+ context,
+ )
+ .await?;
+
// apub updates
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
- send_apub_delete(
- &local_user_view.person.clone().into(),
- &community.into(),
- DeletableObjects::Post(Box::new(updated_post.into())),
+ let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ community,
+ deletable,
+ None,
deleted,
context,
)
.await?;
-
- send_post_ws_message(
- data.post_id,
- UserOperationCrud::DeletePost,
- websocket_id,
- Some(local_user_view.person.id),
- context,
- )
- .await
+ Ok(res)
}
}
})
.await??;
+ let res = send_post_ws_message(
+ data.post_id,
+ UserOperationCrud::RemovePost,
+ websocket_id,
+ Some(local_user_view.person.id),
+ context,
+ )
+ .await?;
+
// apub updates
let community = blocking(context.pool(), move |conn| {
Community::read(conn, orig_post.community_id)
})
.await??;
- send_apub_remove(
- &local_user_view.person.clone().into(),
- &community.into(),
- DeletableObjects::Post(Box::new(updated_post.into())),
- data.reason.clone().unwrap_or_else(|| "".to_string()),
+ let deletable = DeletableObjects::Post(Box::new(updated_post.into()));
+ send_apub_delete_in_community(
+ local_user_view.person,
+ community,
+ deletable,
+ data.reason.clone().or_else(|| Some("".to_string())),
removed,
context,
)
.await?;
-
- send_post_ws_message(
- data.post_id,
- UserOperationCrud::RemovePost,
- websocket_id,
- Some(local_user_view.person.id),
- context,
- )
- .await
+ Ok(res)
}
}
use lemmy_apub::{
generate_local_apub_endpoint,
protocol::activities::{
- private_message::create_or_update::CreateOrUpdatePrivateMessage,
+ create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType,
},
EndpointType,
get_local_user_view_from_jwt,
person::{DeletePrivateMessage, PrivateMessageResponse},
};
-use lemmy_apub::protocol::activities::private_message::{
- delete::DeletePrivateMessage as DeletePrivateMessageApub,
- undo_delete::UndoDeletePrivateMessage,
-};
-use lemmy_db_schema::{
- source::private_message::PrivateMessage,
- traits::{Crud, DeleteableOrRemoveable},
-};
+use lemmy_apub::activities::deletion::send_apub_delete_private_message;
+use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
use lemmy_utils::{ConnectionId, LemmyError};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
.map_err(|e| e.with_message("couldnt_update_private_message"))?;
// Send the apub update
- if data.deleted {
- DeletePrivateMessageApub::send(
- &local_user_view.person.into(),
- &updated_private_message
- .blank_out_deleted_or_removed_info()
- .into(),
- context,
- )
- .await?;
- } else {
- UndoDeletePrivateMessage::send(
- &local_user_view.person.into(),
- &updated_private_message.into(),
- context,
- )
- .await?;
- }
+ send_apub_delete_private_message(
+ &local_user_view.person.into(),
+ updated_private_message,
+ data.deleted,
+ context,
+ )
+ .await?;
let op = UserOperationCrud::DeletePrivateMessage;
send_pm_ws_message(data.private_message_id, op, websocket_id, context).await
person::{EditPrivateMessage, PrivateMessageResponse},
};
use lemmy_apub::protocol::activities::{
- private_message::create_or_update::CreateOrUpdatePrivateMessage,
+ create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType,
};
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
- "object": {
- "id": "http://ds9.lemmy.ml/post/1",
- "type": "Tombstone"
- },
+ "object": "http://ds9.lemmy.ml/post/1",
"cc": [
"http://enterprise.lemmy.ml/c/main"
],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
- "object": {
- "id": "http://ds9.lemmy.ml/comment/1",
- "type": "Tombstone"
- },
+ "object": "http://ds9.lemmy.ml/comment/1",
"cc": [
"http://enterprise.lemmy.ml/c/main"
],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
- "object": {
- "id": "http://ds9.lemmy.ml/post/1",
- "type": "Tombstone"
- },
+ "object": "http://ds9.lemmy.ml/post/1",
"cc": [
"http://enterprise.lemmy.ml/c/main"
],
"to": [
"https://www.w3.org/ns/activitystreams#Public"
],
- "object": {
- "id": "http://ds9.lemmy.ml/comment/1",
- "type": "Tombstone"
- },
+ "object": "http://ds9.lemmy.ml/comment/1",
"cc": [
"http://enterprise.lemmy.ml/c/main"
],
--- /dev/null
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ {
+ "ostatus": "http://ostatus.org#",
+ "atomUri": "ostatus:atomUri"
+ }
+ ],
+ "id": "https://mastodon.madrid/users/felix/statuses/107773559874184870#delete",
+ "type": "Delete",
+ "actor": "https://mastodon.madrid/users/felix",
+ "to": [
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "object": {
+ "id": "https://mastodon.madrid/users/felix/statuses/107773559874184870",
+ "type": "Tombstone",
+ "atomUri": "https://mastodon.madrid/users/felix/statuses/107773559874184870"
+ },
+ "signature": {
+ "type": "RsaSignature2017",
+ "creator": "https://mastodon.madrid/users/felix#main-key",
+ "created": "2022-02-10T11:54:18Z",
+ "signatureValue": "NjGnbkvouSP/cSusR7+sz39iEYxWXCu6nFmBXU3t8ETPkmbpMF5ASeJixXvpTOqbOfkMoWfXncw+jDsbqZ3ELaHGG1gZ5wHWym7mk7YCjQokpF3oPhTWmlEJCVKgewXMrfI4Ok8GGsUMGzuki9EyBDGc/UNBMEAhcxV5Huu7QSQDowcbIwxS3ImxFmtKFceh6mv/kMiXUerCgkYSm6rYZeXZGMTUpvcn9gP6X6Ed6UsrLjCSb3Fj0Naz7LHtzZXRSZDZF/SX2Vw/xKJIgEGzSCv+LKZGvEEkK8PPfMJJhi8cBJebkqOnBGtE6gYK2z2cm/oGorZtXU2L05pXmLAlYQ=="
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://greenish.red/schemas/litepub-0.1.jsonld",
+ {
+ "@language": "und"
+ }
+ ],
+ "actor": "https://greenish.red/users/vpzom",
+ "attachment": [],
+ "attributedTo": "https://greenish.red/users/vpzom",
+ "cc": [],
+ "conversation": null,
+ "id": "https://greenish.red/activities/52f0b259-596e-429f-8a1b-c0b455f8932b",
+ "object": "https://greenish.red/objects/38e2b983-ebf5-4387-9bc2-3b80305469c9",
+ "tag": [
+ {
+ "href": "https://voyager.lemmy.ml/c/main",
+ "name": "@main@voyager.lemmy.ml",
+ "type": "Mention"
+ },
+ {
+ "href": "https://voyager.lemmy.ml/u/dess_voy_41u2",
+ "name": "@dess_voy_41u2@voyager.lemmy.ml",
+ "type": "Mention"
+ }
+ ],
+ "to": [
+ "https://greenish.red/users/vpzom/followers",
+ "https://voyager.lemmy.ml/c/main",
+ "https://voyager.lemmy.ml/u/dess_voy_41u2",
+ "https://www.w3.org/ns/activitystreams#Public"
+ ],
+ "type": "Delete"
+}
\ No newline at end of file
use crate::{
activities::{
check_community_deleted_or_removed,
- comment::get_notif_recipients,
community::{announce::GetCommunity, send_activity_in_community},
+ create_or_update::get_comment_notif_recipients,
generate_activity_id,
verify_activity,
verify_is_public,
) -> Result<(), LemmyError> {
let comment = ApubComment::from_apub(self.object, context, request_counter).await?;
let do_send_email = self.kind == CreateOrUpdateType::Create;
- let recipients = get_notif_recipients(
+ let recipients = get_comment_notif_recipients(
&self.actor,
&comment,
do_send_email,
use lemmy_utils::{utils::scrape_text_for_mentions, LemmyError};
use lemmy_websocket::{send::send_local_notifs, LemmyContext};
-pub mod create_or_update;
+pub mod comment;
+pub mod post;
+pub mod private_message;
#[tracing::instrument(skip_all)]
-async fn get_notif_recipients(
+async fn get_comment_notif_recipients(
actor: &ObjectId<ApubPerson>,
comment: &Comment,
do_send_email: bool,
activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::{
- private_message::create_or_update::CreateOrUpdatePrivateMessage,
+ create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType,
},
};
use crate::{
activities::{
- community::{announce::GetCommunity, send_activity_in_community},
+ community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
verify_activity,
- verify_is_public,
},
- activity_lists::AnnouncableActivities,
objects::{community::ApubCommunity, person::ApubPerson},
- protocol::activities::deletion::delete::Delete,
+ protocol::activities::deletion::delete::{Delete, IdOrNestedObject},
};
-use activitystreams_kinds::{activity::DeleteType, public};
+use activitystreams_kinds::activity::DeleteType;
+use anyhow::anyhow;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
+use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
use lemmy_db_schema::{
source::{
comment::Comment,
ModRemovePost,
ModRemovePostForm,
},
+ person::Person,
post::Post,
},
traits::Crud,
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_is_public(&self.to, &[])?;
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
- let community = self.get_community(context, request_counter).await?;
- verify_delete_activity(
- &self.object.id,
- &self.actor,
- &community,
- self.summary.is_some(),
- context,
- request_counter,
- )
- .await?;
+ verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
Ok(())
}
};
receive_remove_action(
&self.actor,
- &self.object.id,
+ self.object.id(),
reason,
context,
request_counter,
)
.await
} else {
- receive_delete_action(&self.object.id, &self.actor, true, context, request_counter).await
+ receive_delete_action(
+ self.object.id(),
+ &self.actor,
+ true,
+ context,
+ request_counter,
+ )
+ .await
}
}
}
impl Delete {
pub(in crate::activities::deletion) fn new(
- actor: &ApubPerson,
+ actor: &Person,
object: DeletableObjects,
+ to: Url,
+ community: Option<&Community>,
summary: Option<String>,
context: &LemmyContext,
) -> Result<Delete, LemmyError> {
+ let id = generate_activity_id(
+ DeleteType::Delete,
+ &context.settings().get_protocol_and_hostname(),
+ )?;
+ let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
Ok(Delete {
- actor: ObjectId::new(actor.actor_id()),
- to: vec![public()],
- object: object.to_tombstone()?,
+ actor: ObjectId::new(actor.actor_id.clone()),
+ to: vec![to],
+ object: IdOrNestedObject::Id(object.id()),
+ cc: cc.into_iter().collect(),
kind: DeleteType::Delete,
summary,
- id: generate_activity_id(
- DeleteType::Delete,
- &context.settings().get_protocol_and_hostname(),
- )?,
+ id,
unparsed: Default::default(),
})
}
-
- #[tracing::instrument(skip_all)]
- pub(in crate::activities::deletion) async fn send(
- actor: &ApubPerson,
- community: &ApubCommunity,
- object: DeletableObjects,
- summary: Option<String>,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let delete = Delete::new(actor, object, summary, context)?;
- let delete_id = delete.id.clone();
-
- let activity = AnnouncableActivities::Delete(delete);
- send_activity_in_community(activity, &delete_id, actor, community, vec![], context).await
- }
}
#[tracing::instrument(skip_all)]
send_comment_ws_message_simple(removed_comment.id, RemoveComment, context).await?;
}
+ DeletableObjects::PrivateMessage(_) => unimplemented!(),
}
Ok(())
}
context: &LemmyContext,
_request_counter: &mut i32,
) -> Result<ApubCommunity, LemmyError> {
- let community_id = match DeletableObjects::read_from_db(&self.object.id, context).await? {
+ let community_id = match DeletableObjects::read_from_db(self.object.id(), context).await? {
DeletableObjects::Community(c) => c.id,
DeletableObjects::Comment(c) => {
let post = blocking(context.pool(), move |conn| Post::read(conn, c.post_id)).await??;
post.community_id
}
DeletableObjects::Post(p) => p.community_id,
+ DeletableObjects::PrivateMessage(_) => {
+ return Err(anyhow!("Private message is not part of community").into())
+ }
};
let community = blocking(context.pool(), move |conn| {
Community::read(conn, community_id)
use crate::{
- activities::{verify_mod_action, verify_person_in_community},
- objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
- protocol::{
- activities::deletion::{delete::Delete, undo_delete::UndoDelete},
- objects::tombstone::Tombstone,
+ activities::{
+ community::{announce::GetCommunity, send_activity_in_community},
+ send_lemmy_activity,
+ verify_is_public,
+ verify_mod_action,
+ verify_person,
+ verify_person_in_community,
},
+ activity_lists::AnnouncableActivities,
+ objects::{
+ comment::ApubComment,
+ community::ApubCommunity,
+ person::ApubPerson,
+ post::ApubPost,
+ private_message::ApubPrivateMessage,
+ },
+ protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
};
+use activitystreams_kinds::public;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{object_id::ObjectId, traits::ApubObject, verify::verify_domains_match};
-use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
+use lemmy_apub_lib::{
+ object_id::ObjectId,
+ traits::{ActorType, ApubObject},
+ verify::verify_domains_match,
+};
+use lemmy_db_schema::{
+ source::{
+ comment::Comment,
+ community::Community,
+ person::Person,
+ post::Post,
+ private_message::PrivateMessage,
+ },
+ traits::Crud,
+};
use lemmy_utils::LemmyError;
use lemmy_websocket::{
- send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
+ send::{
+ send_comment_ws_message_simple,
+ send_community_ws_message,
+ send_pm_ws_message,
+ send_post_ws_message,
+ },
LemmyContext,
UserOperationCrud,
};
+use std::ops::Deref;
use url::Url;
pub mod delete;
pub mod undo_delete;
+/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
+/// action was done by a normal user.
#[tracing::instrument(skip_all)]
-pub async fn send_apub_delete(
- actor: &ApubPerson,
- community: &ApubCommunity,
+pub async fn send_apub_delete_in_community(
+ actor: Person,
+ community: Community,
object: DeletableObjects,
+ reason: Option<String>,
deleted: bool,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- if deleted {
- Delete::send(actor, community, object, None, context).await
+ let (id, activity) = if deleted {
+ let delete = Delete::new(&actor, object, public(), Some(&community), reason, context)?;
+ (delete.id.clone(), AnnouncableActivities::Delete(delete))
} else {
- UndoDelete::send(actor, community, object, None, context).await
- }
+ let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, context)?;
+ (undo.id.clone(), AnnouncableActivities::UndoDelete(undo))
+ };
+ send_activity_in_community(
+ activity,
+ &id,
+ &ApubPerson::from(actor),
+ &community.into(),
+ vec![],
+ context,
+ )
+ .await
}
-// TODO: remove reason is actually optional in lemmy. we set an empty string in that case, but its
-// ugly
#[tracing::instrument(skip_all)]
-pub async fn send_apub_remove(
+pub async fn send_apub_delete_private_message(
actor: &ApubPerson,
- community: &ApubCommunity,
- object: DeletableObjects,
- reason: String,
- removed: bool,
+ pm: PrivateMessage,
+ deleted: bool,
context: &LemmyContext,
) -> Result<(), LemmyError> {
- if removed {
- Delete::send(actor, community, object, Some(reason), context).await
+ let recipient_id = pm.recipient_id;
+ let recipient: ApubPerson =
+ blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
+ .await??
+ .into();
+
+ let deletable = DeletableObjects::PrivateMessage(Box::new(pm.into()));
+ let inbox = vec![recipient.shared_inbox_or_inbox_url()];
+ if deleted {
+ let delete = Delete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
+ let id = delete.id.clone();
+ send_lemmy_activity(context, &delete, &id, actor, inbox, true).await?;
} else {
- UndoDelete::send(actor, community, object, Some(reason), context).await
- }
+ let undo = UndoDelete::new(actor, deletable, recipient.actor_id(), None, None, context)?;
+ let id = undo.id.clone();
+ send_lemmy_activity(context, &undo, &id, actor, inbox, true).await?;
+ };
+ Ok(())
}
pub enum DeletableObjects {
Community(Box<ApubCommunity>),
Comment(Box<ApubComment>),
Post(Box<ApubPost>),
+ PrivateMessage(Box<ApubPrivateMessage>),
}
impl DeletableObjects {
if let Some(c) = ApubComment::read_from_apub_id(ap_id.clone(), context).await? {
return Ok(DeletableObjects::Comment(Box::new(c)));
}
+ if let Some(p) = ApubPrivateMessage::read_from_apub_id(ap_id.clone(), context).await? {
+ return Ok(DeletableObjects::PrivateMessage(Box::new(p)));
+ }
Err(diesel::NotFound.into())
}
- pub(crate) fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
+ pub(crate) fn id(&self) -> Url {
match self {
- DeletableObjects::Community(c) => c.to_tombstone(),
- DeletableObjects::Comment(c) => c.to_tombstone(),
- DeletableObjects::Post(p) => p.to_tombstone(),
+ DeletableObjects::Community(c) => c.actor_id(),
+ DeletableObjects::Comment(c) => c.ap_id.clone().into(),
+ DeletableObjects::Post(p) => p.ap_id.clone().into(),
+ DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
}
}
}
#[tracing::instrument(skip_all)]
pub(in crate::activities) async fn verify_delete_activity(
- object: &Url,
- actor: &ObjectId<ApubPerson>,
- community: &ApubCommunity,
+ activity: &Delete,
is_mod_action: bool,
context: &LemmyContext,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- let object = DeletableObjects::read_from_db(object, context).await?;
+ let object = DeletableObjects::read_from_db(activity.object.id(), context).await?;
match object {
DeletableObjects::Community(community) => {
+ verify_is_public(&activity.to, &[])?;
if community.local {
// can only do this check for local community, in remote case it would try to fetch the
// deleted community (which fails)
- verify_person_in_community(actor, &community, context, request_counter).await?;
+ verify_person_in_community(&activity.actor, &community, context, request_counter).await?;
}
// community deletion is always a mod (or admin) action
- verify_mod_action(actor, &community, context, request_counter).await?;
+ verify_mod_action(&activity.actor, &community, context, request_counter).await?;
}
DeletableObjects::Post(p) => {
- verify_delete_activity_post_or_comment(
- actor,
+ verify_is_public(&activity.to, &[])?;
+ verify_delete_post_or_comment(
+ &activity.actor,
&p.ap_id.clone().into(),
- community,
+ &activity.get_community(context, request_counter).await?,
is_mod_action,
context,
request_counter,
.await?;
}
DeletableObjects::Comment(c) => {
- verify_delete_activity_post_or_comment(
- actor,
+ verify_is_public(&activity.to, &[])?;
+ verify_delete_post_or_comment(
+ &activity.actor,
&c.ap_id.clone().into(),
- community,
+ &activity.get_community(context, request_counter).await?,
is_mod_action,
context,
request_counter,
)
.await?;
}
+ DeletableObjects::PrivateMessage(_) => {
+ verify_person(&activity.actor, context, request_counter).await?;
+ verify_domains_match(activity.actor.inner(), activity.object.id())?;
+ }
}
Ok(())
}
#[tracing::instrument(skip_all)]
-async fn verify_delete_activity_post_or_comment(
+async fn verify_delete_post_or_comment(
actor: &ObjectId<ApubPerson>,
object_id: &Url,
community: &ApubCommunity,
}
/// Write deletion or restoring of an object to the database, and send websocket message.
-/// TODO: we should do something similar for receive_remove_action(), but its much more complicated
-/// because of the mod log
#[tracing::instrument(skip_all)]
async fn receive_delete_action(
object: &Url,
match DeletableObjects::read_from_db(object, context).await? {
DeletableObjects::Community(community) => {
if community.local {
- let mod_ = actor
+ let mod_: Person = actor
.dereference(context, context.client(), request_counter)
- .await?;
+ .await?
+ .deref()
+ .clone();
let object = DeletableObjects::Community(community.clone());
- send_apub_delete(&mod_, &community.clone(), object, true, context).await?;
+ let c: Community = community.deref().deref().clone();
+ send_apub_delete_in_community(mod_, c, object, None, true, context).await?;
}
let community = blocking(context.pool(), move |conn| {
.await?;
}
}
+ DeletableObjects::PrivateMessage(pm) => {
+ let deleted_private_message = blocking(context.pool(), move |conn| {
+ PrivateMessage::update_deleted(conn, pm.id, deleted)
+ })
+ .await??;
+
+ send_pm_ws_message(
+ deleted_private_message.id,
+ UserOperationCrud::DeletePrivateMessage,
+ None,
+ context,
+ )
+ .await?;
+ }
}
Ok(())
}
use crate::{
activities::{
- community::{announce::GetCommunity, send_activity_in_community},
+ community::announce::GetCommunity,
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id,
verify_activity,
- verify_is_public,
},
- activity_lists::AnnouncableActivities,
- objects::{community::ApubCommunity, person::ApubPerson},
+ objects::community::ApubCommunity,
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
};
-use activitystreams_kinds::{activity::UndoType, public};
+use activitystreams_kinds::activity::UndoType;
use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
-};
-use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post};
+use lemmy_apub_lib::{data::Data, object_id::ObjectId, traits::ActivityHandler};
+use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError;
use lemmy_websocket::{
send::{send_comment_ws_message_simple, send_community_ws_message, send_post_ws_message},
context: &Data<LemmyContext>,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
- verify_is_public(&self.to, &self.cc)?;
verify_activity(&self.id, self.actor.inner(), &context.settings())?;
self.object.verify(context, request_counter).await?;
- let community = self.get_community(context, request_counter).await?;
verify_delete_activity(
- &self.object.object.id,
- &self.actor,
- &community,
+ &self.object,
self.object.summary.is_some(),
context,
request_counter,
request_counter: &mut i32,
) -> Result<(), LemmyError> {
if self.object.summary.is_some() {
- UndoDelete::receive_undo_remove_action(&self.object.object.id, context).await
+ UndoDelete::receive_undo_remove_action(self.object.object.id(), context).await
} else {
receive_delete_action(
- &self.object.object.id,
+ self.object.object.id(),
&self.actor,
false,
context,
impl UndoDelete {
#[tracing::instrument(skip_all)]
- pub(in crate::activities::deletion) async fn send(
- actor: &ApubPerson,
- community: &ApubCommunity,
+ pub(in crate::activities::deletion) fn new(
+ actor: &Person,
object: DeletableObjects,
+ to: Url,
+ community: Option<&Community>,
summary: Option<String>,
context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let object = Delete::new(actor, object, summary, context)?;
+ ) -> Result<UndoDelete, LemmyError> {
+ let object = Delete::new(actor, object, to.clone(), community, summary, context)?;
let id = generate_activity_id(
UndoType::Undo,
&context.settings().get_protocol_and_hostname(),
)?;
- let undo = UndoDelete {
- actor: ObjectId::new(actor.actor_id()),
- to: vec![public()],
+ let cc: Option<Url> = community.map(|c| c.actor_id.clone().into());
+ Ok(UndoDelete {
+ actor: ObjectId::new(actor.actor_id.clone()),
+ to: vec![to],
object,
- cc: vec![community.actor_id()],
+ cc: cc.into_iter().collect(),
kind: UndoType::Undo,
- id: id.clone(),
+ id,
unparsed: Default::default(),
- };
-
- let activity = AnnouncableActivities::UndoDelete(undo);
- send_activity_in_community(activity, &id, actor, community, vec![], context).await
+ })
}
#[tracing::instrument(skip_all)]
.await??;
send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?;
}
+ DeletableObjects::PrivateMessage(_) => unimplemented!(),
}
Ok(())
}
use uuid::Uuid;
pub mod block;
-pub mod comment;
pub mod community;
+pub mod create_or_update;
pub mod deletion;
pub mod following;
-pub mod post;
-pub mod private_message;
pub mod voting;
/// Checks that the specified Url actually identifies a Person (by fetching it), and that the person
+++ /dev/null
-pub mod create_or_update;
+++ /dev/null
-use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
- objects::{person::ApubPerson, private_message::ApubPrivateMessage},
- protocol::activities::private_message::delete::DeletePrivateMessage,
-};
-use activitystreams_kinds::activity::DeleteType;
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::verify_domains_match,
-};
-use lemmy_db_schema::{
- source::{person::Person, private_message::PrivateMessage},
- traits::Crud,
-};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
-
-impl DeletePrivateMessage {
- pub(in crate::activities::private_message) fn new(
- actor: &ApubPerson,
- pm: &PrivateMessage,
- context: &LemmyContext,
- ) -> Result<DeletePrivateMessage, LemmyError> {
- Ok(DeletePrivateMessage {
- actor: ObjectId::new(actor.actor_id()),
- to: [ObjectId::new(actor.actor_id())],
- object: ObjectId::new(pm.ap_id.clone()),
- kind: DeleteType::Delete,
- id: generate_activity_id(
- DeleteType::Delete,
- &context.settings().get_protocol_and_hostname(),
- )?,
- unparsed: Default::default(),
- })
- }
-
- #[tracing::instrument(skip_all)]
- pub async fn send(
- actor: &ApubPerson,
- pm: &ApubPrivateMessage,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let delete = DeletePrivateMessage::new(actor, pm, context)?;
- let delete_id = delete.id.clone();
-
- let recipient_id = pm.recipient_id;
- let recipient: ApubPerson =
- blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
- .await??
- .into();
- let inbox = vec![recipient.shared_inbox_or_inbox_url()];
- send_lemmy_activity(context, &delete, &delete_id, actor, inbox, true).await
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl ActivityHandler for DeletePrivateMessage {
- type DataType = LemmyContext;
-
- #[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
- verify_person(&self.actor, context, request_counter).await?;
- verify_domains_match(self.actor.inner(), self.object.inner())?;
- Ok(())
- }
-
- #[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- _request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let private_message = self.object.dereference_local(context).await?;
- let deleted_private_message = blocking(context.pool(), move |conn| {
- PrivateMessage::update_deleted(conn, private_message.id, true)
- })
- .await??;
-
- send_pm_ws_message(
- deleted_private_message.id,
- UserOperationCrud::DeletePrivateMessage,
- None,
- context,
- )
- .await?;
-
- Ok(())
- }
-}
+++ /dev/null
-pub mod create_or_update;
-pub mod delete;
-pub mod undo_delete;
+++ /dev/null
-use crate::{
- activities::{generate_activity_id, send_lemmy_activity, verify_activity, verify_person},
- objects::{person::ApubPerson, private_message::ApubPrivateMessage},
- protocol::activities::private_message::{
- delete::DeletePrivateMessage,
- undo_delete::UndoDeletePrivateMessage,
- },
-};
-use activitystreams_kinds::activity::UndoType;
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
- data::Data,
- object_id::ObjectId,
- traits::{ActivityHandler, ActorType},
- verify::{verify_domains_match, verify_urls_match},
-};
-use lemmy_db_schema::{
- source::{person::Person, private_message::PrivateMessage},
- traits::Crud,
-};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
-
-impl UndoDeletePrivateMessage {
- #[tracing::instrument(skip_all)]
- pub async fn send(
- actor: &ApubPerson,
- pm: &ApubPrivateMessage,
- context: &LemmyContext,
- ) -> Result<(), LemmyError> {
- let recipient_id = pm.recipient_id;
- let recipient: ApubPerson =
- blocking(context.pool(), move |conn| Person::read(conn, recipient_id))
- .await??
- .into();
-
- let object = DeletePrivateMessage::new(actor, pm, context)?;
- let id = generate_activity_id(
- UndoType::Undo,
- &context.settings().get_protocol_and_hostname(),
- )?;
- let undo = UndoDeletePrivateMessage {
- actor: ObjectId::new(actor.actor_id()),
- to: [ObjectId::new(recipient.actor_id())],
- object,
- kind: UndoType::Undo,
- id: id.clone(),
- unparsed: Default::default(),
- };
- let inbox = vec![recipient.shared_inbox_or_inbox_url()];
- send_lemmy_activity(context, &undo, &id, actor, inbox, true).await
- }
-}
-
-#[async_trait::async_trait(?Send)]
-impl ActivityHandler for UndoDeletePrivateMessage {
- type DataType = LemmyContext;
-
- #[tracing::instrument(skip_all)]
- async fn verify(
- &self,
- context: &Data<LemmyContext>,
- request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- verify_activity(&self.id, self.actor.inner(), &context.settings())?;
- verify_person(&self.actor, context, request_counter).await?;
- verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
- verify_domains_match(self.actor.inner(), self.object.object.inner())?;
- self.object.verify(context, request_counter).await?;
- Ok(())
- }
-
- #[tracing::instrument(skip_all)]
- async fn receive(
- self,
- context: &Data<LemmyContext>,
- _request_counter: &mut i32,
- ) -> Result<(), LemmyError> {
- let ap_id = self.object.object.clone();
- let private_message = ap_id.dereference_local(context).await?;
-
- let deleted_private_message = blocking(context.pool(), move |conn| {
- PrivateMessage::update_deleted(conn, private_message.id, false)
- })
- .await??;
-
- send_pm_ws_message(
- deleted_private_message.id,
- UserOperationCrud::EditPrivateMessage,
- None,
- context,
- )
- .await?;
-
- Ok(())
- }
-}
report::Report,
update::UpdateCommunity,
},
- create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
+ create_or_update::{
+ comment::CreateOrUpdateComment,
+ post::CreateOrUpdatePost,
+ private_message::CreateOrUpdatePrivateMessage,
+ },
deletion::{delete::Delete, undo_delete::UndoDelete},
following::{
accept::AcceptFollowCommunity,
follow::FollowCommunity,
undo_follow::UndoFollowCommunity,
},
- private_message::{
- create_or_update::CreateOrUpdatePrivateMessage,
- delete::DeletePrivateMessage,
- undo_delete::UndoDeletePrivateMessage,
- },
voting::{undo_vote::UndoVote, vote::Vote},
},
objects::page::Page,
/// Some activities can also be sent from user to user, eg a comment with mentions
AnnouncableActivities(AnnouncableActivities),
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
- DeletePrivateMessage(DeletePrivateMessage),
- UndoDeletePrivateMessage(UndoDeletePrivateMessage),
+ Delete(Delete),
+ UndoDelete(UndoDelete),
AnnounceActivity(AnnounceActivity),
}
let res = receive_activity(request, activity.clone(), activity_data, context).await?;
if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
- let community = announcable.get_community(context, &mut 0).await?;
- verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
- if community.local {
- AnnounceActivity::send(*announcable, &community, context).await?;
+ // Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
+ // community, but looks identical to Delete/Post or Delete/Comment which are in a community.
+ let community = announcable.get_community(context, &mut 0).await;
+ if let Ok(community) = community {
+ if community.local {
+ verify_person_in_community(&actor_id, &community, context, &mut 0).await?;
+ AnnounceActivity::send(*announcable, &community, context).await?;
+ }
}
}
pub mod comment;
pub mod post;
+pub mod private_message;
#[cfg(test)]
mod tests {
context::WithContext,
objects::tests::file_to_json_object,
protocol::{
- activities::create_or_update::{comment::CreateOrUpdateComment, post::CreateOrUpdatePost},
+ activities::create_or_update::{
+ comment::CreateOrUpdateComment,
+ post::CreateOrUpdatePost,
+ private_message::CreateOrUpdatePrivateMessage,
+ },
tests::test_parse_lemmy_item,
},
};
"assets/lemmy/activities/create_or_update/create_note.json",
)
.unwrap();
+ test_parse_lemmy_item::<CreateOrUpdatePrivateMessage>(
+ "assets/lemmy/activities/create_or_update/create_private_message.json",
+ )
+ .unwrap();
file_to_json_object::<WithContext<CreateOrUpdateComment>>(
"assets/pleroma/activities/create_note.json",
-use crate::{
- objects::person::ApubPerson,
- protocol::{objects::tombstone::Tombstone, Unparsed},
-};
+use crate::{objects::person::ApubPerson, protocol::Unparsed};
use activitystreams_kinds::activity::DeleteType;
use lemmy_apub_lib::object_id::ObjectId;
use serde::{Deserialize, Serialize};
pub(crate) actor: ObjectId<ApubPerson>,
#[serde(deserialize_with = "crate::deserialize_one_or_many")]
pub(crate) to: Vec<Url>,
- pub(crate) object: Tombstone,
+ pub(crate) object: IdOrNestedObject,
+ #[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Vec::is_empty")]
+ pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: DeleteType,
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
#[serde(flatten)]
pub(crate) unparsed: Unparsed,
}
+
+/// Instead of a simple ID string as object, Mastodon sends a nested tombstone for some reason,
+/// so we need to handle that as well.
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(untagged)]
+pub(crate) enum IdOrNestedObject {
+ Id(Url),
+ NestedObject(NestedObject),
+}
+
+#[derive(Clone, Debug, Deserialize, Serialize)]
+pub(crate) struct NestedObject {
+ id: Url,
+}
+
+impl IdOrNestedObject {
+ pub(crate) fn id(&self) -> &Url {
+ match self {
+ IdOrNestedObject::Id(i) => i,
+ IdOrNestedObject::NestedObject(n) => &n.id,
+ }
+ }
+}
#[cfg(test)]
mod tests {
- use crate::protocol::{
- activities::deletion::{delete::Delete, undo_delete::UndoDelete},
- tests::test_parse_lemmy_item,
+ use crate::{
+ context::WithContext,
+ objects::tests::file_to_json_object,
+ protocol::{
+ activities::deletion::{delete::Delete, undo_delete::UndoDelete},
+ tests::test_parse_lemmy_item,
+ },
};
#[actix_rt::test]
- async fn test_parse_lemmy_deletion() {
+ async fn test_parse_deletion() {
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json").unwrap();
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json").unwrap();
.unwrap();
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json")
.unwrap();
+ test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_private_message.json")
+ .unwrap();
+ test_parse_lemmy_item::<UndoDelete>(
+ "assets/lemmy/activities/deletion/undo_delete_private_message.json",
+ )
+ .unwrap();
+
+ file_to_json_object::<WithContext<Delete>>("assets/pleroma/activities/delete.json").unwrap();
+ file_to_json_object::<WithContext<Delete>>("assets/mastodon/activities/delete.json").unwrap();
}
}
pub(crate) to: Vec<Url>,
pub(crate) object: Delete,
#[serde(deserialize_with = "crate::deserialize_one_or_many")]
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Vec::is_empty")]
pub(crate) cc: Vec<Url>,
#[serde(rename = "type")]
pub(crate) kind: UndoType,
pub mod create_or_update;
pub mod deletion;
pub mod following;
-pub mod private_message;
pub mod voting;
#[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq)]
+++ /dev/null
-use crate::{
- objects::{person::ApubPerson, private_message::ApubPrivateMessage},
- protocol::Unparsed,
-};
-use activitystreams_kinds::activity::DeleteType;
-use lemmy_apub_lib::object_id::ObjectId;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct DeletePrivateMessage {
- pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one")]
- pub(crate) to: [ObjectId<ApubPerson>; 1],
- pub(crate) object: ObjectId<ApubPrivateMessage>,
- #[serde(rename = "type")]
- pub(crate) kind: DeleteType,
- pub(crate) id: Url,
- #[serde(flatten)]
- pub(crate) unparsed: Unparsed,
-}
+++ /dev/null
-pub mod create_or_update;
-pub mod delete;
-pub mod undo_delete;
-
-#[cfg(test)]
-mod tests {
- use crate::protocol::{
- activities::private_message::{
- create_or_update::CreateOrUpdatePrivateMessage,
- delete::DeletePrivateMessage,
- undo_delete::UndoDeletePrivateMessage,
- },
- tests::test_parse_lemmy_item,
- };
-
- #[actix_rt::test]
- async fn test_parse_lemmy_private_message() {
- test_parse_lemmy_item::<CreateOrUpdatePrivateMessage>(
- "assets/lemmy/activities/private_message/create.json",
- )
- .unwrap();
- test_parse_lemmy_item::<DeletePrivateMessage>(
- "assets/lemmy/activities/private_message/delete.json",
- )
- .unwrap();
- test_parse_lemmy_item::<UndoDeletePrivateMessage>(
- "assets/lemmy/activities/private_message/undo_delete.json",
- )
- .unwrap();
- }
-}
+++ /dev/null
-use crate::{
- objects::person::ApubPerson,
- protocol::{activities::private_message::delete::DeletePrivateMessage, Unparsed},
-};
-use activitystreams_kinds::activity::UndoType;
-use lemmy_apub_lib::object_id::ObjectId;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct UndoDeletePrivateMessage {
- pub(crate) actor: ObjectId<ApubPerson>,
- #[serde(deserialize_with = "crate::deserialize_one")]
- pub(crate) to: [ObjectId<ApubPerson>; 1],
- pub(crate) object: DeletePrivateMessage,
- #[serde(rename = "type")]
- pub(crate) kind: UndoType,
- pub(crate) id: Url,
- #[serde(flatten)]
- pub(crate) unparsed: Unparsed,
-}