From 33a4dac675116f7e411cd2537882ac3c2f30c59d Mon Sep 17 00:00:00 2001 From: Felix Ableitner Date: Wed, 18 Aug 2021 23:56:32 +0200 Subject: [PATCH] Remove struct ActivityCommonFields, derive ActivityFields trait instead --- .../activities/comment/create_or_update.rs | 50 +++----- .../apub/src/activities/community/add_mod.rs | 38 +++--- .../apub/src/activities/community/announce.rs | 51 ++++---- .../src/activities/community/block_user.rs | 51 ++++---- .../src/activities/community/remove_mod.rs | 49 ++++--- .../activities/community/undo_block_user.rs | 45 ++++--- .../apub/src/activities/community/update.rs | 38 +++--- crates/apub/src/activities/deletion/delete.rs | 79 ++++++------ crates/apub/src/activities/deletion/mod.rs | 29 +++-- .../src/activities/deletion/undo_delete.rs | 54 ++++---- .../apub/src/activities/following/accept.rs | 48 +++---- .../apub/src/activities/following/follow.rs | 60 +++++---- crates/apub/src/activities/following/undo.rs | 57 ++++----- crates/apub/src/activities/mod.rs | 8 +- .../src/activities/post/create_or_update.rs | 43 +++---- .../private_message/create_or_update.rs | 36 +++--- .../src/activities/private_message/delete.rs | 64 ++++++---- .../activities/private_message/undo_delete.rs | 58 ++++----- crates/apub/src/activities/undo_remove.rs | 26 ++-- .../apub/src/activities/voting/undo_vote.rs | 57 ++++----- crates/apub/src/activities/voting/vote.rs | 61 +++++---- crates/apub/src/http/community.rs | 4 +- crates/apub/src/http/mod.rs | 41 +++--- crates/apub/src/http/person.rs | 8 +- crates/apub_lib/src/lib.rs | 29 +---- crates/apub_lib_derive/src/lib.rs | 120 ++++++++++-------- 26 files changed, 595 insertions(+), 609 deletions(-) diff --git a/crates/apub/src/activities/comment/create_or_update.rs b/crates/apub/src/activities/comment/create_or_update.rs index cb9b23497..7f83d78fa 100644 --- a/crates/apub/src/activities/comment/create_or_update.rs +++ b/crates/apub/src/activities/comment/create_or_update.rs @@ -13,14 +13,9 @@ use crate::{ objects::{comment::Note, FromApub, ToApub}, ActorType, }; -use activitystreams::link::Mention; +use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - values::PublicUrl, - verify_domains_match, - ActivityCommonFields, - ActivityHandler, -}; +use lemmy_apub_lib::{values::PublicUrl, verify_domains_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::Crud; use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; @@ -28,17 +23,21 @@ use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct CreateOrUpdateComment { + actor: Url, to: PublicUrl, object: Note, cc: Vec, tag: Vec, #[serde(rename = "type")] kind: CreateOrUpdateType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl CreateOrUpdateComment { @@ -61,17 +60,15 @@ impl CreateOrUpdateComment { let maa = collect_non_local_mentions(comment, &community, context).await?; let create_or_update = CreateOrUpdateComment { + actor: actor.actor_id(), to: PublicUrl::Public, object: comment.to_apub(context.pool()).await?, cc: maa.ccs, tag: maa.tags, kind, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update); @@ -88,15 +85,10 @@ impl ActivityHandler for CreateOrUpdateComment { ) -> Result<(), LemmyError> { let community = extract_community(&self.cc, context, request_counter).await?; - verify_activity(self.common())?; - verify_person_in_community( - &self.common.actor, - &community.actor_id(), - context, - request_counter, - ) - .await?; - verify_domains_match(&self.common.actor, self.object.id_unchecked())?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &community.actor_id(), context, request_counter) + .await?; + verify_domains_match(&self.actor, self.object.id_unchecked())?; // TODO: should add a check that the correct community is in cc (probably needs changes to // comment deserialization) self.object.verify(context, request_counter).await?; @@ -108,10 +100,8 @@ impl ActivityHandler for CreateOrUpdateComment { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let comment = - Comment::from_apub(&self.object, context, &self.common.actor, request_counter).await?; - let recipients = - get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?; + let comment = Comment::from_apub(&self.object, context, &self.actor, request_counter).await?; + let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreateComment, CreateOrUpdateType::Update => UserOperationCrud::EditComment, @@ -122,8 +112,4 @@ impl ActivityHandler for CreateOrUpdateComment { .await?; Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index bd7cfbadd..f83f14630 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -13,9 +13,14 @@ use crate::{ generate_moderators_url, ActorType, }; -use activitystreams::activity::kind::AddType; +use activitystreams::{ + activity::kind::AddType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::{source::community::CommunityModerator_, Joinable}; use lemmy_db_schema::source::{ community::{Community, CommunityModerator, CommunityModeratorForm}, @@ -23,19 +28,24 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct AddMod { + actor: Url, to: PublicUrl, object: Url, target: Url, cc: [Url; 1], #[serde(rename = "type")] kind: AddType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl AddMod { @@ -47,17 +57,15 @@ impl AddMod { ) -> Result<(), LemmyError> { let id = generate_activity_id(AddType::Add)?; let add = AddMod { + actor: actor.actor_id(), to: PublicUrl::Public, object: added_mod.actor_id(), target: generate_moderators_url(&community.actor_id)?.into(), cc: [community.actor_id()], kind: AddType::Add, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::AddMod(add); @@ -73,9 +81,9 @@ impl ActivityHandler for AddMod { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?; Ok(()) } @@ -109,8 +117,4 @@ impl ActivityHandler for AddMod { // TODO: send websocket notification about added mod Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index 06c895a05..e7d917b72 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -24,15 +24,20 @@ use crate::{ ActorType, CommunityType, }; -use activitystreams::activity::kind::AnnounceType; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use activitystreams::{ + activity::kind::AnnounceType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_schema::source::community::Community; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] #[serde(untagged)] pub enum AnnouncableActivities { CreateOrUpdateComment(CreateOrUpdateComment), @@ -49,16 +54,20 @@ pub enum AnnouncableActivities { RemoveMod(RemoveMod), } -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct AnnounceActivity { + actor: Url, to: PublicUrl, object: AnnouncableActivities, cc: Vec, #[serde(rename = "type")] kind: AnnounceType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl AnnounceActivity { @@ -69,27 +78,17 @@ impl AnnounceActivity { context: &LemmyContext, ) -> Result<(), LemmyError> { let announce = AnnounceActivity { + actor: community.actor_id(), to: PublicUrl::Public, object, cc: vec![community.followers_url()], kind: AnnounceType::Announce, - common: ActivityCommonFields { - context: lemmy_context(), - id: generate_activity_id(&AnnounceType::Announce)?, - actor: community.actor_id(), - unparsed: Default::default(), - }, + id: generate_activity_id(&AnnounceType::Announce)?, + context: lemmy_context(), + unparsed: Default::default(), }; let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?; - send_activity_new( - context, - &announce, - &announce.common.id, - community, - inboxes, - false, - ) - .await + send_activity_new(context, &announce, &announce.id, community, inboxes, false).await } } @@ -100,8 +99,8 @@ impl ActivityHandler for AnnounceActivity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_community(&self.common.actor, context, request_counter).await?; + verify_activity(self)?; + verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -111,11 +110,11 @@ impl ActivityHandler for AnnounceActivity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - if is_activity_already_known(context.pool(), self.object.common().id_unchecked()).await? { + if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? { return Ok(()); } insert_activity( - self.object.common().id_unchecked(), + self.object.id_unchecked(), self.object.clone(), false, true, @@ -124,8 +123,4 @@ impl ActivityHandler for AnnounceActivity { .await?; self.object.receive(context, request_counter).await } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/block_user.rs b/crates/apub/src/activities/community/block_user.rs index d725c35da..d31077dfa 100644 --- a/crates/apub/src/activities/community/block_user.rs +++ b/crates/apub/src/activities/community/block_user.rs @@ -11,9 +11,14 @@ use crate::{ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, ActorType, }; -use activitystreams::activity::kind::BlockType; +use activitystreams::{ + activity::kind::BlockType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::{Bannable, Followable}; use lemmy_db_schema::source::{ community::{ @@ -27,39 +32,41 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct BlockUserFromCommunity { + actor: Url, to: PublicUrl, pub(in crate::activities::community) object: Url, cc: [Url; 1], #[serde(rename = "type")] kind: BlockType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl BlockUserFromCommunity { pub(in crate::activities::community) fn new( - id: Url, community: &Community, target: &Person, actor: &Person, - ) -> BlockUserFromCommunity { - BlockUserFromCommunity { + ) -> Result { + Ok(BlockUserFromCommunity { + actor: actor.actor_id(), to: PublicUrl::Public, object: target.actor_id(), cc: [community.actor_id()], kind: BlockType::Block, - common: ActivityCommonFields { - context: lemmy_context(), - id, - actor: actor.actor_id(), - unparsed: Default::default(), - }, - } + id: generate_activity_id(BlockType::Block)?, + context: lemmy_context(), + unparsed: Default::default(), + }) } pub async fn send( @@ -68,12 +75,12 @@ impl BlockUserFromCommunity { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(BlockType::Block)?; - let block = BlockUserFromCommunity::new(id.clone(), community, target, actor); + let block = BlockUserFromCommunity::new(community, target, actor)?; + let block_id = block.id.clone(); let activity = AnnouncableActivities::BlockUserFromCommunity(block); let inboxes = vec![target.get_shared_inbox_or_inbox_url()]; - send_to_community_new(activity, &id, actor, community, inboxes, context).await + send_to_community_new(activity, &block_id, actor, community, inboxes, context).await } } @@ -84,9 +91,9 @@ impl ActivityHandler for BlockUserFromCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; Ok(()) } @@ -124,8 +131,4 @@ impl ActivityHandler for BlockUserFromCommunity { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index 9ca0082fd..2d4eba56a 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -14,9 +14,14 @@ use crate::{ generate_moderators_url, ActorType, }; -use activitystreams::activity::kind::RemoveType; +use activitystreams::{ + activity::kind::RemoveType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::Joinable; use lemmy_db_schema::source::{ community::{Community, CommunityModerator, CommunityModeratorForm}, @@ -24,11 +29,13 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct RemoveMod { + actor: Url, to: PublicUrl, pub(in crate::activities) object: Url, cc: [Url; 1], @@ -36,8 +43,11 @@ pub struct RemoveMod { kind: RemoveType, // if target is set, this is means remove mod from community pub(in crate::activities) target: Option, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl RemoveMod { @@ -49,17 +59,15 @@ impl RemoveMod { ) -> Result<(), LemmyError> { let id = generate_activity_id(RemoveType::Remove)?; let remove = RemoveMod { + actor: actor.actor_id(), to: PublicUrl::Public, object: removed_mod.actor_id(), target: Some(generate_moderators_url(&community.actor_id)?.into()), + id: id.clone(), + context: lemmy_context(), cc: [community.actor_id()], kind: RemoveType::Remove, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + unparsed: Default::default(), }; let activity = AnnouncableActivities::RemoveMod(remove); @@ -75,16 +83,16 @@ impl ActivityHandler for RemoveMod { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; if let Some(target) = &self.target { - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; verify_add_remove_moderator_target(target, self.cc[0].clone())?; } else { verify_delete_activity( &self.object, + self, &self.cc[0], - self.common(), true, context, request_counter, @@ -116,18 +124,7 @@ impl ActivityHandler for RemoveMod { // TODO: send websocket notification about removed mod Ok(()) } else { - receive_remove_action( - &self.common.actor, - &self.object, - None, - context, - request_counter, - ) - .await + receive_remove_action(&self.actor, &self.object, None, context, request_counter).await } } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/undo_block_user.rs b/crates/apub/src/activities/community/undo_block_user.rs index 0afe77c36..0a9665af5 100644 --- a/crates/apub/src/activities/community/undo_block_user.rs +++ b/crates/apub/src/activities/community/undo_block_user.rs @@ -11,9 +11,14 @@ use crate::{ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, ActorType, }; -use activitystreams::activity::kind::{BlockType, UndoType}; +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::Bannable; use lemmy_db_schema::source::{ community::{Community, CommunityPersonBan, CommunityPersonBanForm}, @@ -21,18 +26,23 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoBlockUserFromCommunity { + actor: Url, to: PublicUrl, object: BlockUserFromCommunity, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl UndoBlockUserFromCommunity { @@ -42,25 +52,18 @@ impl UndoBlockUserFromCommunity { actor: &Person, context: &LemmyContext, ) -> Result<(), LemmyError> { - let block = BlockUserFromCommunity::new( - generate_activity_id(BlockType::Block)?, - community, - target, - actor, - ); + let block = BlockUserFromCommunity::new(community, target, actor)?; let id = generate_activity_id(UndoType::Undo)?; let undo = UndoBlockUserFromCommunity { + actor: actor.actor_id(), to: PublicUrl::Public, object: block, cc: [community.actor_id()], kind: UndoType::Undo, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo); @@ -76,9 +79,9 @@ impl ActivityHandler for UndoBlockUserFromCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -105,8 +108,4 @@ impl ActivityHandler for UndoBlockUserFromCommunity { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index db4b141ba..7539464da 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -11,9 +11,14 @@ use crate::{ objects::{community::Group, ToApub}, ActorType, }; -use activitystreams::activity::kind::UpdateType; +use activitystreams::{ + activity::kind::UpdateType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::{ApubObject, Crud}; use lemmy_db_schema::source::{ community::{Community, CommunityForm}, @@ -21,21 +26,26 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; +use serde::{Deserialize, Serialize}; use url::Url; /// This activity is received from a remote community mod, and updates the description or other /// fields of a local community. -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UpdateCommunity { + actor: Url, to: PublicUrl, // TODO: would be nice to use a separate struct here, which only contains the fields updated here object: Group, cc: [Url; 1], #[serde(rename = "type")] kind: UpdateType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl UpdateCommunity { @@ -46,16 +56,14 @@ impl UpdateCommunity { ) -> Result<(), LemmyError> { let id = generate_activity_id(UpdateType::Update)?; let update = UpdateCommunity { + actor: actor.actor_id(), to: PublicUrl::Public, object: community.to_apub(context.pool()).await?, cc: [community.actor_id()], kind: UpdateType::Update, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::UpdateCommunity(Box::new(update)); @@ -70,9 +78,9 @@ impl ActivityHandler for UpdateCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_mod_action(&self.actor, self.cc[0].clone(), context).await?; Ok(()) } @@ -114,8 +122,4 @@ impl ActivityHandler for UpdateCommunity { .await?; Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index 093919b88..d94bb21e5 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -15,10 +15,15 @@ use crate::{ fetcher::person::get_or_fetch_and_upsert_person, ActorType, }; -use activitystreams::activity::kind::DeleteType; +use activitystreams::{ + activity::kind::DeleteType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::{ source::{comment::Comment_, community::Community_, post::Post_}, Crud, @@ -43,6 +48,7 @@ use lemmy_websocket::{ LemmyContext, UserOperationCrud, }; +use serde::{Deserialize, Serialize}; use url::Url; /// This is very confusing, because there are four distinct cases to handle: @@ -53,19 +59,23 @@ use url::Url; /// /// TODO: we should probably change how community deletions work to simplify this. Probably by /// wrapping it in an announce just like other activities, instead of having the community send it. -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct Delete { - pub(in crate::activities::deletion) to: PublicUrl, + actor: Url, + to: PublicUrl, pub(in crate::activities::deletion) object: Url, - pub(in crate::activities::deletion) cc: [Url; 1], + cc: [Url; 1], #[serde(rename = "type")] - pub(in crate::activities::deletion) kind: DeleteType, + kind: DeleteType, /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user /// deleting their own content. pub(in crate::activities::deletion) summary: Option, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - pub(in crate::activities::deletion) common: ActivityCommonFields, + unparsed: Unparsed, } #[async_trait::async_trait(?Send)] @@ -75,11 +85,11 @@ impl ActivityHandler for Delete { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; verify_delete_activity( &self.object, + self, &self.cc[0], - &self.common, self.summary.is_some(), context, request_counter, @@ -101,18 +111,11 @@ impl ActivityHandler for Delete { } else { Some(reason) }; - receive_remove_action( - &self.common.actor, - &self.object, - reason, - context, - request_counter, - ) - .await + receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await } else { receive_delete_action( &self.object, - &self.common.actor, + &self.actor, WebsocketMessages { community: UserOperationCrud::DeleteCommunity, post: UserOperationCrud::DeletePost, @@ -125,13 +128,27 @@ impl ActivityHandler for Delete { .await } } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } impl Delete { + pub(in crate::activities::deletion) fn new( + actor: &Person, + community: &Community, + object_id: Url, + summary: Option, + ) -> Result { + Ok(Delete { + actor: actor.actor_id(), + to: PublicUrl::Public, + object: object_id, + cc: [community.actor_id()], + kind: DeleteType::Delete, + summary, + id: generate_activity_id(DeleteType::Delete)?, + context: lemmy_context(), + unparsed: Default::default(), + }) + } pub(in crate::activities::deletion) async fn send( actor: &Person, community: &Community, @@ -139,23 +156,11 @@ impl Delete { summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(DeleteType::Delete)?; - let delete = Delete { - to: PublicUrl::Public, - object: object_id, - cc: [community.actor_id()], - kind: DeleteType::Delete, - summary, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; + let delete = Delete::new(actor, community, object_id, summary)?; + let delete_id = delete.id.clone(); let activity = AnnouncableActivities::Delete(delete); - send_to_community_new(activity, &id, actor, community, vec![], context).await + send_to_community_new(activity, &delete_id, actor, community, vec![], context).await } } diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index cf73be097..350773f42 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -8,7 +8,7 @@ use crate::{ ActorType, }; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields}; +use lemmy_apub_lib::{verify_domains_match, ActivityFields}; use lemmy_db_queries::{ source::{comment::Comment_, community::Community_, post::Post_}, ApubObject, @@ -98,8 +98,8 @@ impl DeletableObjects { pub(in crate::activities) async fn verify_delete_activity( object: &Url, - cc: &Url, - common: &ActivityCommonFields, + activity: &dyn ActivityFields, + community_id: &Url, is_mod_action: bool, context: &LemmyContext, request_counter: &mut i32, @@ -110,16 +110,17 @@ pub(in crate::activities) async fn verify_delete_activity( if c.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(&common.actor, cc, context, request_counter).await?; + verify_person_in_community(activity.actor(), community_id, context, request_counter) + .await?; } // community deletion is always a mod (or admin) action - verify_mod_action(&common.actor, c.actor_id(), context).await?; + verify_mod_action(activity.actor(), c.actor_id(), context).await?; } DeletableObjects::Post(p) => { verify_delete_activity_post_or_comment( - cc, - common, + activity, &p.ap_id.into(), + community_id, is_mod_action, context, request_counter, @@ -128,9 +129,9 @@ pub(in crate::activities) async fn verify_delete_activity( } DeletableObjects::Comment(c) => { verify_delete_activity_post_or_comment( - cc, - common, + activity, &c.ap_id.into(), + community_id, is_mod_action, context, request_counter, @@ -142,19 +143,19 @@ pub(in crate::activities) async fn verify_delete_activity( } async fn verify_delete_activity_post_or_comment( - cc: &Url, - common: &ActivityCommonFields, + activity: &dyn ActivityFields, object_id: &Url, + community_id: &Url, is_mod_action: bool, context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_person_in_community(&common.actor, cc, context, request_counter).await?; + verify_person_in_community(activity.actor(), community_id, context, request_counter).await?; if is_mod_action { - verify_mod_action(&common.actor, cc.clone(), context).await?; + verify_mod_action(activity.actor(), community_id.clone(), context).await?; } else { // domain of post ap_id and post.creator ap_id are identical, so we just check the former - verify_domains_match(&common.actor, object_id)?; + verify_domains_match(activity.actor(), object_id)?; } Ok(()) } diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index 0114acac8..35369d441 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -15,10 +15,15 @@ use crate::{ extensions::context::lemmy_context, ActorType, }; -use activitystreams::activity::kind::{DeleteType, UndoType}; +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_}; use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; @@ -27,18 +32,23 @@ use lemmy_websocket::{ LemmyContext, UserOperationCrud, }; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoDelete { + actor: Url, to: PublicUrl, object: Delete, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } #[async_trait::async_trait(?Send)] @@ -48,12 +58,12 @@ impl ActivityHandler for UndoDelete { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; self.object.verify(context, request_counter).await?; verify_delete_activity( &self.object.object, + self, &self.cc[0], - &self.common, self.object.summary.is_some(), context, request_counter, @@ -72,7 +82,7 @@ impl ActivityHandler for UndoDelete { } else { receive_delete_action( &self.object.object, - &self.common.actor, + &self.actor, WebsocketMessages { community: UserOperationCrud::EditCommunity, post: UserOperationCrud::EditPost, @@ -85,10 +95,6 @@ impl ActivityHandler for UndoDelete { .await } } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } impl UndoDelete { @@ -99,32 +105,18 @@ impl UndoDelete { summary: Option, context: &LemmyContext, ) -> Result<(), LemmyError> { - let delete = Delete { - to: PublicUrl::Public, - object: object_id, - cc: [community.actor_id()], - kind: DeleteType::Delete, - summary, - common: ActivityCommonFields { - context: lemmy_context(), - id: generate_activity_id(DeleteType::Delete)?, - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; + let object = Delete::new(actor, community, object_id, summary)?; let id = generate_activity_id(UndoType::Undo)?; let undo = UndoDelete { + actor: actor.actor_id(), to: PublicUrl::Public, - object: delete, + object, cc: [community.actor_id()], kind: UndoType::Undo, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoDelete(undo); diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index bc6895899..c76263cc0 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -10,9 +10,14 @@ use crate::{ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, ActorType, }; -use activitystreams::activity::kind::AcceptType; +use activitystreams::{ + activity::kind::AcceptType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::{ApubObject, Followable}; use lemmy_db_schema::source::{ community::{Community, CommunityFollower}, @@ -20,17 +25,22 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct AcceptFollowCommunity { + actor: Url, to: Url, object: FollowCommunity, #[serde(rename = "type")] kind: AcceptType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl AcceptFollowCommunity { @@ -40,26 +50,23 @@ impl AcceptFollowCommunity { Community::read_from_apub_id(conn, &community_id.into()) }) .await??; - let person_id = follow.common.actor.clone(); + let person_id = follow.actor().clone(); let person = blocking(context.pool(), move |conn| { Person::read_from_apub_id(conn, &person_id.into()) }) .await??; - let id = generate_activity_id(AcceptType::Accept)?; let accept = AcceptFollowCommunity { + actor: community.actor_id(), to: person.actor_id(), object: follow, kind: AcceptType::Accept, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: community.actor_id(), - unparsed: Default::default(), - }, + id: generate_activity_id(AcceptType::Accept)?, + context: lemmy_context(), + unparsed: Default::default(), }; let inbox = vec![person.inbox_url.into()]; - send_activity_new(context, &accept, &id, &community, inbox, true).await + send_activity_new(context, &accept, &accept.id, &community, inbox, true).await } } /// Handle accepted follows @@ -70,10 +77,10 @@ impl ActivityHandler for AcceptFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_urls_match(&self.to, &self.object.common.actor)?; - verify_urls_match(&self.common.actor, &self.object.to)?; - verify_community(&self.common.actor, context, request_counter).await?; + verify_activity(self)?; + verify_urls_match(&self.to, self.object.actor())?; + verify_urls_match(&self.actor, &self.object.to)?; + verify_community(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -83,8 +90,7 @@ impl ActivityHandler for AcceptFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_community(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?; let to = get_or_fetch_and_upsert_person(&self.to, context, request_counter).await?; // This will throw an error if no follow was requested blocking(context.pool(), move |conn| { @@ -94,8 +100,4 @@ impl ActivityHandler for AcceptFollowCommunity { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index aa96fb39f..e6ca747a0 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -10,9 +10,14 @@ use crate::{ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, ActorType, }; -use activitystreams::activity::kind::FollowType; +use activitystreams::{ + activity::kind::FollowType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::Followable; use lemmy_db_schema::source::{ community::{Community, CommunityFollower, CommunityFollowerForm}, @@ -20,20 +25,39 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct FollowCommunity { + actor: Url, pub(in crate::activities::following) to: Url, pub(in crate::activities::following) object: Url, #[serde(rename = "type")] - pub(in crate::activities::following) kind: FollowType, + kind: FollowType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - pub(in crate::activities::following) common: ActivityCommonFields, + unparsed: Unparsed, } impl FollowCommunity { + pub(in crate::activities::following) fn new( + actor: &Person, + community: &Community, + ) -> Result { + Ok(FollowCommunity { + actor: actor.actor_id(), + to: community.actor_id(), + object: community.actor_id(), + kind: FollowType::Follow, + id: generate_activity_id(FollowType::Follow)?, + context: lemmy_context(), + unparsed: Default::default(), + }) + } pub async fn send( actor: &Person, community: &Community, @@ -49,20 +73,9 @@ impl FollowCommunity { }) .await?; - let id = generate_activity_id(FollowType::Follow)?; - let follow = FollowCommunity { - to: community.actor_id(), - object: community.actor_id(), - kind: FollowType::Follow, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; + let follow = FollowCommunity::new(actor, community)?; let inbox = vec![community.inbox_url.clone().into()]; - send_activity_new(context, &follow, &id, actor, inbox, true).await + send_activity_new(context, &follow, &follow.id, actor, inbox, true).await } } @@ -73,9 +86,9 @@ impl ActivityHandler for FollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; verify_urls_match(&self.to, &self.object)?; - verify_person(&self.common.actor, context, request_counter).await?; + verify_person(&self.actor, context, request_counter).await?; Ok(()) } @@ -84,8 +97,7 @@ impl ActivityHandler for FollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?; let community = get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?; let community_follower_form = CommunityFollowerForm { @@ -102,8 +114,4 @@ impl ActivityHandler for FollowCommunity { AcceptFollowCommunity::send(self, context).await } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/following/undo.rs b/crates/apub/src/activities/following/undo.rs index 7fbc7be5b..092036bb4 100644 --- a/crates/apub/src/activities/following/undo.rs +++ b/crates/apub/src/activities/following/undo.rs @@ -10,9 +10,14 @@ use crate::{ fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, ActorType, }; -use activitystreams::activity::kind::{FollowType, UndoType}; +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::Followable; use lemmy_db_schema::source::{ community::{Community, CommunityFollower, CommunityFollowerForm}, @@ -20,17 +25,22 @@ use lemmy_db_schema::source::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoFollowCommunity { + actor: Url, to: Url, object: FollowCommunity, #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl UndoFollowCommunity { @@ -39,30 +49,18 @@ impl UndoFollowCommunity { community: &Community, context: &LemmyContext, ) -> Result<(), LemmyError> { - let id = generate_activity_id(UndoType::Undo)?; + let object = FollowCommunity::new(actor, community)?; let undo = UndoFollowCommunity { + actor: actor.actor_id(), to: community.actor_id(), - object: FollowCommunity { - to: community.actor_id(), - object: community.actor_id(), - kind: FollowType::Follow, - common: ActivityCommonFields { - context: lemmy_context(), - id: generate_activity_id(FollowType::Follow)?, - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }, + object, kind: UndoType::Undo, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: generate_activity_id(UndoType::Undo)?, + context: lemmy_context(), + unparsed: Default::default(), }; let inbox = vec![community.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &undo, &id, actor, inbox, true).await + send_activity_new(context, &undo, &undo.id, actor, inbox, true).await } } @@ -73,10 +71,10 @@ impl ActivityHandler for UndoFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; verify_urls_match(&self.to, &self.object.object)?; - verify_urls_match(&self.common.actor, &self.object.common.actor)?; - verify_person(&self.common.actor, context, request_counter).await?; + verify_urls_match(&self.actor, self.object.actor())?; + verify_person(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -86,8 +84,7 @@ impl ActivityHandler for UndoFollowCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?; let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?; let community_follower_form = CommunityFollowerForm { @@ -103,8 +100,4 @@ impl ActivityHandler for UndoFollowCommunity { .await?; Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 8d44bcc11..a846a0e70 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -6,7 +6,7 @@ use crate::{ }; use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields}; +use lemmy_apub_lib::{verify_domains_match, ActivityFields}; use lemmy_db_queries::ApubObject; use lemmy_db_schema::{ source::{community::Community, person::Person}, @@ -90,9 +90,9 @@ async fn verify_community( Ok(()) } -fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> { - check_is_apub_id_valid(&common.actor, false)?; - verify_domains_match(common.id_unchecked(), &common.actor)?; +fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> { + check_is_apub_id_valid(activity.actor(), false)?; + verify_domains_match(activity.id_unchecked(), activity.actor())?; Ok(()) } diff --git a/crates/apub/src/activities/post/create_or_update.rs b/crates/apub/src/activities/post/create_or_update.rs index 1a9c77689..c1b0703db 100644 --- a/crates/apub/src/activities/post/create_or_update.rs +++ b/crates/apub/src/activities/post/create_or_update.rs @@ -14,31 +14,37 @@ use crate::{ objects::{post::Page, FromApub, ToApub}, ActorType, }; +use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; use lemmy_apub_lib::{ values::PublicUrl, verify_domains_match, verify_urls_match, - ActivityCommonFields, + ActivityFields, ActivityHandler, }; use lemmy_db_queries::Crud; use lemmy_db_schema::source::{community::Community, person::Person, post::Post}; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct CreateOrUpdatePost { + actor: Url, to: PublicUrl, object: Page, cc: [Url; 1], #[serde(rename = "type")] kind: CreateOrUpdateType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl CreateOrUpdatePost { @@ -56,16 +62,14 @@ impl CreateOrUpdatePost { let id = generate_activity_id(kind.clone())?; let create_or_update = CreateOrUpdatePost { + actor: actor.actor_id(), to: PublicUrl::Public, object: post.to_apub(context.pool()).await?, cc: [community.actor_id()], kind, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update)); @@ -80,14 +84,14 @@ impl ActivityHandler for CreateOrUpdatePost { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; let community = extract_community(&self.cc, context, request_counter).await?; let community_id = community.actor_id(); - verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?; + verify_person_in_community(&self.actor, &community_id, context, request_counter).await?; match self.kind { CreateOrUpdateType::Create => { - verify_domains_match(&self.common.actor, self.object.id_unchecked())?; - verify_urls_match(&self.common.actor, &self.object.attributed_to)?; + verify_domains_match(&self.actor, self.object.id_unchecked())?; + verify_urls_match(&self.actor, &self.object.attributed_to)?; // Check that the post isnt locked or stickied, as that isnt possible for newly created posts. // However, when fetching a remote post we generate a new create activity with the current // locked/stickied value, so this check may fail. So only check if its a local community, @@ -101,10 +105,10 @@ impl ActivityHandler for CreateOrUpdatePost { CreateOrUpdateType::Update => { let is_mod_action = self.object.is_mod_action(context.pool()).await?; if is_mod_action { - verify_mod_action(&self.common.actor, community_id, context).await?; + verify_mod_action(&self.actor, community_id, context).await?; } else { - verify_domains_match(&self.common.actor, self.object.id_unchecked())?; - verify_urls_match(&self.common.actor, &self.object.attributed_to)?; + verify_domains_match(&self.actor, self.object.id_unchecked())?; + verify_urls_match(&self.actor, &self.object.attributed_to)?; } } } @@ -117,8 +121,7 @@ impl ActivityHandler for CreateOrUpdatePost { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?; let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?; let notif_type = match self.kind { @@ -128,8 +131,4 @@ impl ActivityHandler for CreateOrUpdatePost { send_post_ws_message(post.id, notif_type, None, None, context).await?; Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/private_message/create_or_update.rs b/crates/apub/src/activities/private_message/create_or_update.rs index cd3c65744..98a26d806 100644 --- a/crates/apub/src/activities/private_message/create_or_update.rs +++ b/crates/apub/src/activities/private_message/create_or_update.rs @@ -5,23 +5,30 @@ use crate::{ objects::{private_message::Note, FromApub, ToApub}, ActorType, }; +use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::Crud; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct CreateOrUpdatePrivateMessage { + #[serde(rename = "@context")] + pub context: OneOrMany, + id: Url, + actor: Url, to: Url, + cc: [Url; 0], object: Note, #[serde(rename = "type")] kind: CreateOrUpdateType, #[serde(flatten)] - common: ActivityCommonFields, + pub unparsed: Unparsed, } impl CreateOrUpdatePrivateMessage { @@ -37,15 +44,14 @@ impl CreateOrUpdatePrivateMessage { let id = generate_activity_id(kind.clone())?; let create_or_update = CreateOrUpdatePrivateMessage { + context: lemmy_context(), + id: id.clone(), + actor: actor.actor_id(), to: recipient.actor_id(), + cc: [], object: private_message.to_apub(context.pool()).await?, kind, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + unparsed: Default::default(), }; let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; send_activity_new(context, &create_or_update, &id, actor, inbox, true).await @@ -58,9 +64,9 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person(&self.common.actor, context, request_counter).await?; - verify_domains_match(&self.common.actor, self.object.id_unchecked())?; + verify_activity(self)?; + verify_person(&self.actor, context, request_counter).await?; + verify_domains_match(&self.actor, self.object.id_unchecked())?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -71,7 +77,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { request_counter: &mut i32, ) -> Result<(), LemmyError> { let private_message = - PrivateMessage::from_apub(&self.object, context, &self.common.actor, request_counter).await?; + PrivateMessage::from_apub(&self.object, context, &self.actor, request_counter).await?; let notif_type = match self.kind { CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, @@ -81,8 +87,4 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/private_message/delete.rs b/crates/apub/src/activities/private_message/delete.rs index e6c03093b..47e1a71a1 100644 --- a/crates/apub/src/activities/private_message/delete.rs +++ b/crates/apub/src/activities/private_message/delete.rs @@ -4,50 +4,64 @@ use crate::{ extensions::context::lemmy_context, ActorType, }; -use activitystreams::activity::kind::DeleteType; +use activitystreams::{ + activity::kind::DeleteType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct DeletePrivateMessage { - pub(in crate::activities::private_message) to: Url, + actor: Url, + to: Url, pub(in crate::activities::private_message) object: Url, #[serde(rename = "type")] - pub(in crate::activities::private_message) kind: DeleteType, + kind: DeleteType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - pub(in crate::activities::private_message) common: ActivityCommonFields, + unparsed: Unparsed, } impl DeletePrivateMessage { + pub(in crate::activities::private_message) fn new( + actor: &Person, + pm: &PrivateMessage, + ) -> Result { + Ok(DeletePrivateMessage { + actor: actor.actor_id(), + to: actor.actor_id(), + object: pm.ap_id.clone().into(), + kind: DeleteType::Delete, + id: generate_activity_id(DeleteType::Delete)?, + context: lemmy_context(), + unparsed: Default::default(), + }) + } pub async fn send( actor: &Person, pm: &PrivateMessage, context: &LemmyContext, ) -> Result<(), LemmyError> { + let delete = DeletePrivateMessage::new(actor, pm)?; + let delete_id = delete.id.clone(); + let recipient_id = pm.recipient_id; let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - - let id = generate_activity_id(DeleteType::Delete)?; - let delete = DeletePrivateMessage { - to: actor.actor_id(), - object: pm.ap_id.clone().into(), - kind: DeleteType::Delete, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; - send_activity_new(context, &delete, &id, actor, inbox, true).await + send_activity_new(context, &delete, &delete_id, actor, inbox, true).await } } @@ -58,9 +72,9 @@ impl ActivityHandler for DeletePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person(&self.common.actor, context, request_counter).await?; - verify_domains_match(&self.common.actor, &self.object)?; + verify_activity(self)?; + verify_person(&self.actor, context, request_counter).await?; + verify_domains_match(&self.actor, &self.object)?; Ok(()) } @@ -89,8 +103,4 @@ impl ActivityHandler for DeletePrivateMessage { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/private_message/undo_delete.rs b/crates/apub/src/activities/private_message/undo_delete.rs index 4cd2a139d..911a17c79 100644 --- a/crates/apub/src/activities/private_message/undo_delete.rs +++ b/crates/apub/src/activities/private_message/undo_delete.rs @@ -9,29 +9,34 @@ use crate::{ extensions::context::lemmy_context, ActorType, }; -use activitystreams::activity::kind::{DeleteType, UndoType}; -use lemmy_api_common::blocking; -use lemmy_apub_lib::{ - verify_domains_match, - verify_urls_match, - ActivityCommonFields, - ActivityHandler, +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, }; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{verify_domains_match, verify_urls_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_utils::LemmyError; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoDeletePrivateMessage { + actor: Url, to: Url, object: DeletePrivateMessage, #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl UndoDeletePrivateMessage { @@ -44,29 +49,16 @@ impl UndoDeletePrivateMessage { let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; - let object = DeletePrivateMessage { - to: recipient.actor_id(), - object: pm.ap_id.clone().into(), - kind: DeleteType::Delete, - common: ActivityCommonFields { - context: lemmy_context(), - id: generate_activity_id(DeleteType::Delete)?, - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; - + let object = DeletePrivateMessage::new(actor, pm)?; let id = generate_activity_id(UndoType::Undo)?; let undo = UndoDeletePrivateMessage { + actor: actor.actor_id(), to: recipient.actor_id(), object, kind: UndoType::Undo, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; send_activity_new(context, &undo, &id, actor, inbox, true).await @@ -80,10 +72,10 @@ impl ActivityHandler for UndoDeletePrivateMessage { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person(&self.common.actor, context, request_counter).await?; - verify_urls_match(&self.common.actor, &self.object.common.actor)?; - verify_domains_match(&self.common.actor, &self.object.object)?; + verify_activity(self)?; + verify_person(&self.actor, context, request_counter).await?; + verify_urls_match(&self.actor, self.object.actor())?; + verify_domains_match(&self.actor, &self.object.object)?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -114,8 +106,4 @@ impl ActivityHandler for UndoDeletePrivateMessage { Ok(()) } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/undo_remove.rs b/crates/apub/src/activities/undo_remove.rs index 6518cd5f6..03b2f888e 100644 --- a/crates/apub/src/activities/undo_remove.rs +++ b/crates/apub/src/activities/undo_remove.rs @@ -3,23 +3,33 @@ use crate::activities::{ deletion::{undo_delete::UndoDelete, verify_delete_activity}, verify_activity, }; -use activitystreams::activity::kind::UndoType; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoRemovePostCommentOrCommunity { + actor: Url, to: PublicUrl, // Note, there is no such thing as Undo/Remove/Mod, so we ignore that object: RemoveMod, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } #[async_trait::async_trait(?Send)] @@ -29,13 +39,13 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; + verify_activity(self)?; self.object.verify(context, request_counter).await?; verify_delete_activity( &self.object.object, + self, &self.cc[0], - self.common(), true, context, request_counter, @@ -51,8 +61,4 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity { ) -> Result<(), LemmyError> { UndoDelete::receive_undo_remove_action(&self.object.object, context).await } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index 5b5feac9d..6e18b3cb6 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -19,9 +19,14 @@ use crate::{ ActorType, PostOrComment, }; -use activitystreams::activity::kind::UndoType; +use activitystreams::{ + activity::kind::UndoType, + base::AnyBase, + primitives::OneOrMany, + unparsed::Unparsed, +}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityFields, ActivityHandler}; use lemmy_db_queries::Crud; use lemmy_db_schema::{ source::{community::Community, person::Person}, @@ -29,19 +34,24 @@ use lemmy_db_schema::{ }; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; use std::ops::Deref; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct UndoVote { + actor: Url, to: PublicUrl, object: Vote, cc: [Url; 1], #[serde(rename = "type")] kind: UndoType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - common: ActivityCommonFields, + unparsed: Unparsed, } impl UndoVote { @@ -56,30 +66,18 @@ impl UndoVote { Community::read(conn, community_id) }) .await??; - let id = generate_activity_id(UndoType::Undo)?; + let object = Vote::new(object, actor, &community, kind.clone())?; + let id = generate_activity_id(UndoType::Undo)?; let undo_vote = UndoVote { + actor: actor.actor_id(), to: PublicUrl::Public, - object: Vote { - to: PublicUrl::Public, - object: object.ap_id(), - cc: [community.actor_id()], - kind: kind.clone(), - common: ActivityCommonFields { - context: lemmy_context(), - id: generate_activity_id(kind)?, - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }, + object, cc: [community.actor_id()], kind: UndoType::Undo, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, + id: id.clone(), + context: lemmy_context(), + unparsed: Default::default(), }; let activity = AnnouncableActivities::UndoVote(undo_vote); send_to_community_new(activity, &id, actor, &community, vec![], context).await @@ -93,9 +91,9 @@ impl ActivityHandler for UndoVote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_urls_match(&self.common.actor, &self.object.common().actor)?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; + verify_urls_match(&self.actor, self.object.actor())?; self.object.verify(context, request_counter).await?; Ok(()) } @@ -105,8 +103,7 @@ impl ActivityHandler for UndoVote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?; let object = get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter) .await?; @@ -115,8 +112,4 @@ impl ActivityHandler for UndoVote { PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await, } } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index 4183c2ad0..cd7d04c54 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -15,9 +15,10 @@ use crate::{ ActorType, PostOrComment, }; +use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed}; use anyhow::anyhow; use lemmy_api_common::blocking; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler}; use lemmy_db_queries::Crud; use lemmy_db_schema::{ source::{community::Community, person::Person}, @@ -57,19 +58,41 @@ impl From<&VoteType> for i16 { } } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)] #[serde(rename_all = "camelCase")] pub struct Vote { - pub(in crate::activities::voting) to: PublicUrl, + actor: Url, + to: PublicUrl, pub(in crate::activities::voting) object: Url, - pub(in crate::activities::voting) cc: [Url; 1], + cc: [Url; 1], #[serde(rename = "type")] pub(in crate::activities::voting) kind: VoteType, + id: Url, + #[serde(rename = "@context")] + context: OneOrMany, #[serde(flatten)] - pub(in crate::activities::voting) common: ActivityCommonFields, + unparsed: Unparsed, } impl Vote { + pub(in crate::activities::voting) fn new( + object: &PostOrComment, + actor: &Person, + community: &Community, + kind: VoteType, + ) -> Result { + Ok(Vote { + actor: actor.actor_id(), + to: PublicUrl::Public, + object: object.ap_id(), + cc: [community.actor_id()], + kind: kind.clone(), + id: generate_activity_id(kind)?, + context: lemmy_context(), + unparsed: Default::default(), + }) + } + pub async fn send( object: &PostOrComment, actor: &Person, @@ -81,22 +104,11 @@ impl Vote { Community::read(conn, community_id) }) .await??; - let id = generate_activity_id(kind.clone())?; + let vote = Vote::new(object, actor, &community, kind)?; + let vote_id = vote.id.clone(); - let vote = Vote { - to: PublicUrl::Public, - object: object.ap_id(), - cc: [community.actor_id()], - kind, - common: ActivityCommonFields { - context: lemmy_context(), - id: id.clone(), - actor: actor.actor_id(), - unparsed: Default::default(), - }, - }; let activity = AnnouncableActivities::Vote(vote); - send_to_community_new(activity, &id, actor, &community, vec![], context).await + send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await } } @@ -107,8 +119,8 @@ impl ActivityHandler for Vote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; + verify_activity(self)?; + verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?; Ok(()) } @@ -117,8 +129,7 @@ impl ActivityHandler for Vote { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = - get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?; let object = get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?; match object { @@ -126,8 +137,4 @@ impl ActivityHandler for Vote { PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await, } } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } } diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 6c8bcb658..38b2158df 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -21,7 +21,7 @@ use activitystreams::{ }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{ActivityFields, ActivityHandler}; use lemmy_db_queries::source::{activity::Activity_, community::Community_}; use lemmy_db_schema::source::{activity::Activity, community::Community}; use lemmy_db_views_actor::{ @@ -57,7 +57,7 @@ pub(crate) async fn get_apub_community_http( } } -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] #[serde(untagged)] pub enum GroupInboxActivities { FollowCommunity(FollowCommunity), diff --git a/crates/apub/src/http/mod.rs b/crates/apub/src/http/mod.rs index 7a372f94c..1014b00a2 100644 --- a/crates/apub/src/http/mod.rs +++ b/crates/apub/src/http/mod.rs @@ -20,7 +20,7 @@ use anyhow::{anyhow, Context}; use futures::StreamExt; use http::StatusCode; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{ActivityFields, ActivityHandler}; use lemmy_db_queries::{source::activity::Activity_, DbPool}; use lemmy_db_schema::source::activity::Activity; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; @@ -36,7 +36,7 @@ mod person; mod post; pub mod routes; -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] #[serde(untagged)] pub enum SharedInboxActivities { GroupInboxActivities(GroupInboxActivities), @@ -80,30 +80,32 @@ async fn receive_activity<'a, T>( context: &LemmyContext, ) -> Result where - T: ActivityHandler + Clone + Deserialize<'a> + Serialize + std::fmt::Debug + Send + 'static, + T: ActivityHandler + + ActivityFields + + Clone + + Deserialize<'a> + + Serialize + + std::fmt::Debug + + Send + + 'static, { - let activity_data = activity.common(); - let request_counter = &mut 0; - let actor = get_or_fetch_and_upsert_actor(&activity_data.actor, context, request_counter).await?; + let actor = get_or_fetch_and_upsert_actor(activity.actor(), context, request_counter).await?; verify_signature(&request, &actor.public_key().context(location_info!())?)?; // Do nothing if we received the same activity before - if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? { + if is_activity_already_known(context.pool(), activity.id_unchecked()).await? { return Ok(HttpResponse::Ok().finish()); } - check_is_apub_id_valid(&activity_data.actor, false)?; - info!( - "Verifying activity {}", - activity_data.id_unchecked().to_string() - ); + check_is_apub_id_valid(activity.actor(), false)?; + info!("Verifying activity {}", activity.id_unchecked().to_string()); activity.verify(context, request_counter).await?; assert_activity_not_local(&activity)?; // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen // if we receive the same activity twice in very quick succession. insert_activity( - activity_data.id_unchecked(), + activity.id_unchecked(), activity.clone(), false, true, @@ -111,10 +113,7 @@ where ) .await?; - info!( - "Receiving activity {}", - activity_data.id_unchecked().to_string() - ); + info!("Receiving activity {}", activity.id_unchecked().to_string()); activity.receive(context, request_counter).await?; Ok(HttpResponse::Ok().finish()) } @@ -187,12 +186,8 @@ pub(crate) async fn is_activity_already_known( } } -fn assert_activity_not_local(activity: &T) -> Result<(), LemmyError> { - let activity_domain = activity - .common() - .id_unchecked() - .domain() - .context(location_info!())?; +fn assert_activity_not_local(activity: &T) -> Result<(), LemmyError> { + let activity_domain = activity.id_unchecked().domain().context(location_info!())?; if activity_domain == Settings::get().hostname { return Err( diff --git a/crates/apub/src/http/person.rs b/crates/apub/src/http/person.rs index 539f05b75..e7b8dfed0 100644 --- a/crates/apub/src/http/person.rs +++ b/crates/apub/src/http/person.rs @@ -1,6 +1,6 @@ use crate::{ activities::{ - community::announce::AnnounceActivity, + community::announce::{AnnouncableActivities, AnnounceActivity}, following::accept::AcceptFollowCommunity, private_message::{ create_or_update::CreateOrUpdatePrivateMessage, @@ -24,7 +24,7 @@ use activitystreams::{ }; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use lemmy_api_common::blocking; -use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler}; +use lemmy_apub_lib::{ActivityFields, ActivityHandler}; use lemmy_db_queries::source::person::Person_; use lemmy_db_schema::source::person::Person; use lemmy_utils::LemmyError; @@ -59,10 +59,12 @@ pub(crate) async fn get_apub_person_http( } } -#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] +#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)] #[serde(untagged)] pub enum PersonInboxActivities { AcceptFollowCommunity(AcceptFollowCommunity), + /// Some activities can also be sent from user to user, eg a comment with mentions + AnnouncableActivities(AnnouncableActivities), CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), DeletePrivateMessage(DeletePrivateMessage), UndoDeletePrivateMessage(UndoDeletePrivateMessage), diff --git a/crates/apub_lib/src/lib.rs b/crates/apub_lib/src/lib.rs index 15267db71..cc88b79c9 100644 --- a/crates/apub_lib/src/lib.rs +++ b/crates/apub_lib/src/lib.rs @@ -1,33 +1,15 @@ pub mod values; -use activitystreams::{ - base::AnyBase, - error::DomainError, - primitives::OneOrMany, - unparsed::Unparsed, -}; +use activitystreams::error::DomainError; pub use lemmy_apub_lib_derive::*; use lemmy_utils::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ActivityCommonFields { - #[serde(rename = "@context")] - pub context: OneOrMany, - pub id: Url, - pub actor: Url, - - // unparsed fields - #[serde(flatten)] - pub unparsed: Unparsed, -} - -impl ActivityCommonFields { - pub fn id_unchecked(&self) -> &Url { - &self.id - } +pub trait ActivityFields { + fn id_unchecked(&self) -> &Url; + fn actor(&self) -> &Url; + fn cc(&self) -> Vec; } #[async_trait::async_trait(?Send)] @@ -43,7 +25,6 @@ pub trait ActivityHandler { context: &LemmyContext, request_counter: &mut i32, ) -> Result<(), LemmyError>; - fn common(&self) -> &ActivityCommonFields; } pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> { diff --git a/crates/apub_lib_derive/src/lib.rs b/crates/apub_lib_derive/src/lib.rs index d35454a37..e7a1912cf 100644 --- a/crates/apub_lib_derive/src/lib.rs +++ b/crates/apub_lib_derive/src/lib.rs @@ -1,6 +1,6 @@ use proc_macro2::TokenStream; use quote::quote; -use syn::{parse_macro_input, Data, DeriveInput}; +use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant}; /// Generates implementation ActivityHandler for an enum, which looks like the following (handling /// all enum variants). @@ -46,104 +46,118 @@ use syn::{parse_macro_input, Data, DeriveInput}; /// } /// /// ``` -/// -/// TODO: consider replacing this macro with https://crates.io/crates/typetag crate, though it -/// doesnt support untagged enums which we need for apub. #[proc_macro_derive(ActivityHandler)] pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Parse the input tokens into a syntax tree. let input = parse_macro_input!(input as DeriveInput); - // Used in the quasi-quotation below as `#name`. - let name = input.ident; + let enum_name = input.ident; let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); - let input_enum = if let Data::Enum(d) = input.data { - d + let enum_variants = if let Data::Enum(d) = input.data { + d.variants } else { unimplemented!() }; - let impl_verify = input_enum - .variants + let body_verify = quote! {a.verify(context, request_counter).await}; + let impl_verify = enum_variants .iter() - .map(|variant| variant_impl_verify(&name, variant)); - let impl_receive = input_enum - .variants + .map(|v| generate_match_arm(&enum_name, v, &body_verify)); + let body_receive = quote! {a.receive(context, request_counter).await}; + let impl_receive = enum_variants .iter() - .map(|variant| variant_impl_receive(&name, variant)); - let impl_common = input_enum - .variants - .iter() - .map(|variant| variant_impl_common(&name, variant)); + .map(|v| generate_match_arm(&enum_name, v, &body_receive)); - // The generated impl. let expanded = quote! { #[async_trait::async_trait(?Send)] - impl #impl_generics lemmy_apub_lib::ActivityHandler for #name #ty_generics #where_clause { + impl #impl_generics lemmy_apub_lib::ActivityHandler for #enum_name #ty_generics #where_clause { async fn verify( &self, - context: &LemmyContext, + context: &lemmy_websocket::LemmyContext, request_counter: &mut i32, - ) -> Result<(), LemmyError> { + ) -> Result<(), lemmy_utils::LemmyError> { match self { #(#impl_verify)* } } async fn receive( self, - context: &LemmyContext, + context: &lemmy_websocket::LemmyContext, request_counter: &mut i32, - ) -> Result<(), LemmyError> { + ) -> Result<(), lemmy_utils::LemmyError> { match self { #(#impl_receive)* } } - fn common(&self) -> &ActivityCommonFields { - match self { - #(#impl_common)* - } - } } }; - - // Hand the output tokens back to the compiler. - proc_macro::TokenStream::from(expanded) + expanded.into() } -fn variant_impl_common(name: &syn::Ident, variant: &syn::Variant) -> TokenStream { +fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream { let id = &variant.ident; match &variant.fields { - syn::Fields::Unnamed(_) => { + Unnamed(_) => { quote! { - #name::#id(a) => a.common(), + #enum_name::#id(a) => #body, } } _ => unimplemented!(), } } -fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream { - let id = &variant.ident; - match &variant.fields { - syn::Fields::Unnamed(_) => { - quote! { - #name::#id(a) => a.verify(context, request_counter).await, - } - } - _ => unimplemented!(), - } -} +#[proc_macro_derive(ActivityFields)] +pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); -fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream { - let id = &variant.ident; - match &variant.fields { - syn::Fields::Unnamed(_) => { + let name = input.ident; + + let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); + + let expanded = match input.data { + Data::Enum(e) => { + let variants = e.variants; + let impl_id = variants + .iter() + .map(|v| generate_match_arm(&name, v, "e! {a.id_unchecked()})); + let impl_actor = variants + .iter() + .map(|v| generate_match_arm(&name, v, "e! {a.actor()})); + let impl_cc = variants + .iter() + .map(|v| generate_match_arm(&name, v, "e! {a.cc()})); quote! { - #name::#id(a) => a.receive(context, request_counter).await, + impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause { + fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } } + fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } } + fn cc(&self) -> Vec { match self { #(#impl_cc)* } } + } + } + } + Data::Struct(s) => { + // check if the struct has a field "cc", and generate impl for cc() function depending on that + let has_cc = if let syn::Fields::Named(n) = s.fields { + n.named + .iter() + .any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc") + } else { + unimplemented!() + }; + let cc_impl = if has_cc { + quote! {self.cc.clone().into()} + } else { + quote! {vec![]} + }; + quote! { + impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause { + fn id_unchecked(&self) -> &url::Url { &self.id } + fn actor(&self) -> &url::Url { &self.actor } + fn cc(&self) -> Vec { #cc_impl } + } } } _ => unimplemented!(), - } + }; + expanded.into() }