Remove struct ActivityCommonFields, derive ActivityFields trait instead

This commit is contained in:
Felix Ableitner 2021-08-18 23:56:32 +02:00
parent ddf1db0980
commit 33a4dac675
26 changed files with 595 additions and 609 deletions

View file

@ -13,14 +13,9 @@ use crate::{
objects::{comment::Note, FromApub, ToApub}, objects::{comment::Note, FromApub, ToApub},
ActorType, ActorType,
}; };
use activitystreams::link::Mention; use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{values::PublicUrl, verify_domains_match, ActivityFields, ActivityHandler};
values::PublicUrl,
verify_domains_match,
ActivityCommonFields,
ActivityHandler,
};
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -28,17 +23,21 @@ use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CreateOrUpdateComment { pub struct CreateOrUpdateComment {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: Note, object: Note,
cc: Vec<Url>, cc: Vec<Url>,
tag: Vec<Mention>, tag: Vec<Mention>,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl CreateOrUpdateComment { impl CreateOrUpdateComment {
@ -61,17 +60,15 @@ impl CreateOrUpdateComment {
let maa = collect_non_local_mentions(comment, &community, context).await?; let maa = collect_non_local_mentions(comment, &community, context).await?;
let create_or_update = CreateOrUpdateComment { let create_or_update = CreateOrUpdateComment {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: comment.to_apub(context.pool()).await?, object: comment.to_apub(context.pool()).await?,
cc: maa.ccs, cc: maa.ccs,
tag: maa.tags, tag: maa.tags,
kind, kind,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update); let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
@ -88,15 +85,10 @@ impl ActivityHandler for CreateOrUpdateComment {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community = extract_community(&self.cc, context, request_counter).await?; let community = extract_community(&self.cc, context, request_counter).await?;
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community( verify_person_in_community(&self.actor, &community.actor_id(), context, request_counter)
&self.common.actor,
&community.actor_id(),
context,
request_counter,
)
.await?; .await?;
verify_domains_match(&self.common.actor, self.object.id_unchecked())?; 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 // TODO: should add a check that the correct community is in cc (probably needs changes to
// comment deserialization) // comment deserialization)
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
@ -108,10 +100,8 @@ impl ActivityHandler for CreateOrUpdateComment {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let comment = let comment = Comment::from_apub(&self.object, context, &self.actor, request_counter).await?;
Comment::from_apub(&self.object, context, &self.common.actor, request_counter).await?; let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
let recipients =
get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
let notif_type = match self.kind { let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreateComment, CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
CreateOrUpdateType::Update => UserOperationCrud::EditComment, CreateOrUpdateType::Update => UserOperationCrud::EditComment,
@ -122,8 +112,4 @@ impl ActivityHandler for CreateOrUpdateComment {
.await?; .await?;
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -13,9 +13,14 @@ use crate::{
generate_moderators_url, generate_moderators_url,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::AddType; use activitystreams::{
activity::kind::AddType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::{source::community::CommunityModerator_, Joinable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityModerator, CommunityModeratorForm},
@ -23,19 +28,24 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AddMod { pub struct AddMod {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: Url, object: Url,
target: Url, target: Url,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: AddType, kind: AddType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl AddMod { impl AddMod {
@ -47,17 +57,15 @@ impl AddMod {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(AddType::Add)?; let id = generate_activity_id(AddType::Add)?;
let add = AddMod { let add = AddMod {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: added_mod.actor_id(), object: added_mod.actor_id(),
target: generate_moderators_url(&community.actor_id)?.into(), target: generate_moderators_url(&community.actor_id)?.into(),
cc: [community.actor_id()], cc: [community.actor_id()],
kind: AddType::Add, kind: AddType::Add,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::AddMod(add); let activity = AnnouncableActivities::AddMod(add);
@ -73,9 +81,9 @@ impl ActivityHandler for AddMod {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?; verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
Ok(()) Ok(())
} }
@ -109,8 +117,4 @@ impl ActivityHandler for AddMod {
// TODO: send websocket notification about added mod // TODO: send websocket notification about added mod
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -24,15 +24,20 @@ use crate::{
ActorType, ActorType,
CommunityType, CommunityType,
}; };
use activitystreams::activity::kind::AnnounceType; use activitystreams::{
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; 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_db_schema::source::community::Community;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
#[serde(untagged)] #[serde(untagged)]
pub enum AnnouncableActivities { pub enum AnnouncableActivities {
CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdateComment(CreateOrUpdateComment),
@ -49,16 +54,20 @@ pub enum AnnouncableActivities {
RemoveMod(RemoveMod), RemoveMod(RemoveMod),
} }
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AnnounceActivity { pub struct AnnounceActivity {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: AnnouncableActivities, object: AnnouncableActivities,
cc: Vec<Url>, cc: Vec<Url>,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: AnnounceType, kind: AnnounceType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl AnnounceActivity { impl AnnounceActivity {
@ -69,27 +78,17 @@ impl AnnounceActivity {
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let announce = AnnounceActivity { let announce = AnnounceActivity {
actor: community.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object, object,
cc: vec![community.followers_url()], cc: vec![community.followers_url()],
kind: AnnounceType::Announce, kind: AnnounceType::Announce,
common: ActivityCommonFields {
context: lemmy_context(),
id: generate_activity_id(&AnnounceType::Announce)?, id: generate_activity_id(&AnnounceType::Announce)?,
actor: community.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?; let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
send_activity_new( send_activity_new(context, &announce, &announce.id, community, inboxes, false).await
context,
&announce,
&announce.common.id,
community,
inboxes,
false,
)
.await
} }
} }
@ -100,8 +99,8 @@ impl ActivityHandler for AnnounceActivity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_community(&self.common.actor, context, request_counter).await?; verify_community(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -111,11 +110,11 @@ impl ActivityHandler for AnnounceActivity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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(()); return Ok(());
} }
insert_activity( insert_activity(
self.object.common().id_unchecked(), self.object.id_unchecked(),
self.object.clone(), self.object.clone(),
false, false,
true, true,
@ -124,8 +123,4 @@ impl ActivityHandler for AnnounceActivity {
.await?; .await?;
self.object.receive(context, request_counter).await self.object.receive(context, request_counter).await
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -11,9 +11,14 @@ use crate::{
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType, ActorType,
}; };
use activitystreams::activity::kind::BlockType; use activitystreams::{
activity::kind::BlockType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::{Bannable, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{ community::{
@ -27,39 +32,41 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct BlockUserFromCommunity { pub struct BlockUserFromCommunity {
actor: Url,
to: PublicUrl, to: PublicUrl,
pub(in crate::activities::community) object: Url, pub(in crate::activities::community) object: Url,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: BlockType, kind: BlockType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl BlockUserFromCommunity { impl BlockUserFromCommunity {
pub(in crate::activities::community) fn new( pub(in crate::activities::community) fn new(
id: Url,
community: &Community, community: &Community,
target: &Person, target: &Person,
actor: &Person, actor: &Person,
) -> BlockUserFromCommunity { ) -> Result<BlockUserFromCommunity, LemmyError> {
BlockUserFromCommunity { Ok(BlockUserFromCommunity {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: target.actor_id(), object: target.actor_id(),
cc: [community.actor_id()], cc: [community.actor_id()],
kind: BlockType::Block, kind: BlockType::Block,
common: ActivityCommonFields { id: generate_activity_id(BlockType::Block)?,
context: lemmy_context(), context: lemmy_context(),
id,
actor: actor.actor_id(),
unparsed: Default::default(), unparsed: Default::default(),
}, })
}
} }
pub async fn send( pub async fn send(
@ -68,12 +75,12 @@ impl BlockUserFromCommunity {
actor: &Person, actor: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(BlockType::Block)?; let block = BlockUserFromCommunity::new(community, target, actor)?;
let block = BlockUserFromCommunity::new(id.clone(), community, target, actor); let block_id = block.id.clone();
let activity = AnnouncableActivities::BlockUserFromCommunity(block); let activity = AnnouncableActivities::BlockUserFromCommunity(block);
let inboxes = vec![target.get_shared_inbox_or_inbox_url()]; 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, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
Ok(()) Ok(())
} }
@ -124,8 +131,4 @@ impl ActivityHandler for BlockUserFromCommunity {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -14,9 +14,14 @@ use crate::{
generate_moderators_url, generate_moderators_url,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::RemoveType; use activitystreams::{
activity::kind::RemoveType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::Joinable;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityModerator, CommunityModeratorForm}, community::{Community, CommunityModerator, CommunityModeratorForm},
@ -24,11 +29,13 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RemoveMod { pub struct RemoveMod {
actor: Url,
to: PublicUrl, to: PublicUrl,
pub(in crate::activities) object: Url, pub(in crate::activities) object: Url,
cc: [Url; 1], cc: [Url; 1],
@ -36,8 +43,11 @@ pub struct RemoveMod {
kind: RemoveType, kind: RemoveType,
// if target is set, this is means remove mod from community // if target is set, this is means remove mod from community
pub(in crate::activities) target: Option<Url>, pub(in crate::activities) target: Option<Url>,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl RemoveMod { impl RemoveMod {
@ -49,17 +59,15 @@ impl RemoveMod {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(RemoveType::Remove)?; let id = generate_activity_id(RemoveType::Remove)?;
let remove = RemoveMod { let remove = RemoveMod {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: removed_mod.actor_id(), object: removed_mod.actor_id(),
target: Some(generate_moderators_url(&community.actor_id)?.into()), target: Some(generate_moderators_url(&community.actor_id)?.into()),
id: id.clone(),
context: lemmy_context(),
cc: [community.actor_id()], cc: [community.actor_id()],
kind: RemoveType::Remove, 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); let activity = AnnouncableActivities::RemoveMod(remove);
@ -75,16 +83,16 @@ impl ActivityHandler for RemoveMod {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
if let Some(target) = &self.target { if let Some(target) = &self.target {
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
verify_add_remove_moderator_target(target, self.cc[0].clone())?; verify_add_remove_moderator_target(target, self.cc[0].clone())?;
} else { } else {
verify_delete_activity( verify_delete_activity(
&self.object, &self.object,
self,
&self.cc[0], &self.cc[0],
self.common(),
true, true,
context, context,
request_counter, request_counter,
@ -116,18 +124,7 @@ impl ActivityHandler for RemoveMod {
// TODO: send websocket notification about removed mod // TODO: send websocket notification about removed mod
Ok(()) Ok(())
} else { } else {
receive_remove_action( receive_remove_action(&self.actor, &self.object, None, context, request_counter).await
&self.common.actor,
&self.object,
None,
context,
request_counter,
)
.await
} }
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -11,9 +11,14 @@ use crate::{
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType, 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_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_queries::Bannable;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityPersonBan, CommunityPersonBanForm}, community::{Community, CommunityPersonBan, CommunityPersonBanForm},
@ -21,18 +26,23 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoBlockUserFromCommunity { pub struct UndoBlockUserFromCommunity {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: BlockUserFromCommunity, object: BlockUserFromCommunity,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl UndoBlockUserFromCommunity { impl UndoBlockUserFromCommunity {
@ -42,25 +52,18 @@ impl UndoBlockUserFromCommunity {
actor: &Person, actor: &Person,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let block = BlockUserFromCommunity::new( let block = BlockUserFromCommunity::new(community, target, actor)?;
generate_activity_id(BlockType::Block)?,
community,
target,
actor,
);
let id = generate_activity_id(UndoType::Undo)?; let id = generate_activity_id(UndoType::Undo)?;
let undo = UndoBlockUserFromCommunity { let undo = UndoBlockUserFromCommunity {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: block, object: block,
cc: [community.actor_id()], cc: [community.actor_id()],
kind: UndoType::Undo, kind: UndoType::Undo,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo); let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo);
@ -76,9 +79,9 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -105,8 +108,4 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -11,9 +11,14 @@ use crate::{
objects::{community::Group, ToApub}, objects::{community::Group, ToApub},
ActorType, ActorType,
}; };
use activitystreams::activity::kind::UpdateType; use activitystreams::{
activity::kind::UpdateType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::{ApubObject, Crud};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityForm}, community::{Community, CommunityForm},
@ -21,21 +26,26 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
/// This activity is received from a remote community mod, and updates the description or other /// This activity is received from a remote community mod, and updates the description or other
/// fields of a local community. /// fields of a local community.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UpdateCommunity { pub struct UpdateCommunity {
actor: Url,
to: PublicUrl, to: PublicUrl,
// TODO: would be nice to use a separate struct here, which only contains the fields updated here // TODO: would be nice to use a separate struct here, which only contains the fields updated here
object: Group, object: Group,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UpdateType, kind: UpdateType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl UpdateCommunity { impl UpdateCommunity {
@ -46,16 +56,14 @@ impl UpdateCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(UpdateType::Update)?; let id = generate_activity_id(UpdateType::Update)?;
let update = UpdateCommunity { let update = UpdateCommunity {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: community.to_apub(context.pool()).await?, object: community.to_apub(context.pool()).await?,
cc: [community.actor_id()], cc: [community.actor_id()],
kind: UpdateType::Update, kind: UpdateType::Update,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::UpdateCommunity(Box::new(update)); let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
@ -70,9 +78,9 @@ impl ActivityHandler for UpdateCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?; verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
Ok(()) Ok(())
} }
@ -114,8 +122,4 @@ impl ActivityHandler for UpdateCommunity {
.await?; .await?;
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -15,10 +15,15 @@ use crate::{
fetcher::person::get_or_fetch_and_upsert_person, fetcher::person::get_or_fetch_and_upsert_person,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::DeleteType; use activitystreams::{
activity::kind::DeleteType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; 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::{ use lemmy_db_queries::{
source::{comment::Comment_, community::Community_, post::Post_}, source::{comment::Comment_, community::Community_, post::Post_},
Crud, Crud,
@ -43,6 +48,7 @@ use lemmy_websocket::{
LemmyContext, LemmyContext,
UserOperationCrud, UserOperationCrud,
}; };
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
/// This is very confusing, because there are four distinct cases to handle: /// 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 /// 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. /// 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")] #[serde(rename_all = "camelCase")]
pub struct Delete { 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) object: Url,
pub(in crate::activities::deletion) cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[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 /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
/// deleting their own content. /// deleting their own content.
pub(in crate::activities::deletion) summary: Option<String>, pub(in crate::activities::deletion) summary: Option<String>,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
pub(in crate::activities::deletion) common: ActivityCommonFields, unparsed: Unparsed,
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -75,11 +85,11 @@ impl ActivityHandler for Delete {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_delete_activity( verify_delete_activity(
&self.object, &self.object,
self,
&self.cc[0], &self.cc[0],
&self.common,
self.summary.is_some(), self.summary.is_some(),
context, context,
request_counter, request_counter,
@ -101,18 +111,11 @@ impl ActivityHandler for Delete {
} else { } else {
Some(reason) Some(reason)
}; };
receive_remove_action( receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
&self.common.actor,
&self.object,
reason,
context,
request_counter,
)
.await
} else { } else {
receive_delete_action( receive_delete_action(
&self.object, &self.object,
&self.common.actor, &self.actor,
WebsocketMessages { WebsocketMessages {
community: UserOperationCrud::DeleteCommunity, community: UserOperationCrud::DeleteCommunity,
post: UserOperationCrud::DeletePost, post: UserOperationCrud::DeletePost,
@ -125,13 +128,27 @@ impl ActivityHandler for Delete {
.await .await
} }
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }
impl Delete { impl Delete {
pub(in crate::activities::deletion) fn new(
actor: &Person,
community: &Community,
object_id: Url,
summary: Option<String>,
) -> Result<Delete, LemmyError> {
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( pub(in crate::activities::deletion) async fn send(
actor: &Person, actor: &Person,
community: &Community, community: &Community,
@ -139,23 +156,11 @@ impl Delete {
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(DeleteType::Delete)?; let delete = Delete::new(actor, community, object_id, summary)?;
let delete = Delete { let delete_id = delete.id.clone();
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 activity = AnnouncableActivities::Delete(delete); 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
} }
} }

View file

@ -8,7 +8,7 @@ use crate::{
ActorType, ActorType,
}; };
use lemmy_api_common::blocking; 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::{ use lemmy_db_queries::{
source::{comment::Comment_, community::Community_, post::Post_}, source::{comment::Comment_, community::Community_, post::Post_},
ApubObject, ApubObject,
@ -98,8 +98,8 @@ impl DeletableObjects {
pub(in crate::activities) async fn verify_delete_activity( pub(in crate::activities) async fn verify_delete_activity(
object: &Url, object: &Url,
cc: &Url, activity: &dyn ActivityFields,
common: &ActivityCommonFields, community_id: &Url,
is_mod_action: bool, is_mod_action: bool,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
@ -110,16 +110,17 @@ pub(in crate::activities) async fn verify_delete_activity(
if c.local { if c.local {
// can only do this check for local community, in remote case it would try to fetch the // can only do this check for local community, in remote case it would try to fetch the
// deleted community (which fails) // 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 // 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) => { DeletableObjects::Post(p) => {
verify_delete_activity_post_or_comment( verify_delete_activity_post_or_comment(
cc, activity,
common,
&p.ap_id.into(), &p.ap_id.into(),
community_id,
is_mod_action, is_mod_action,
context, context,
request_counter, request_counter,
@ -128,9 +129,9 @@ pub(in crate::activities) async fn verify_delete_activity(
} }
DeletableObjects::Comment(c) => { DeletableObjects::Comment(c) => {
verify_delete_activity_post_or_comment( verify_delete_activity_post_or_comment(
cc, activity,
common,
&c.ap_id.into(), &c.ap_id.into(),
community_id,
is_mod_action, is_mod_action,
context, context,
request_counter, request_counter,
@ -142,19 +143,19 @@ pub(in crate::activities) async fn verify_delete_activity(
} }
async fn verify_delete_activity_post_or_comment( async fn verify_delete_activity_post_or_comment(
cc: &Url, activity: &dyn ActivityFields,
common: &ActivityCommonFields,
object_id: &Url, object_id: &Url,
community_id: &Url,
is_mod_action: bool, is_mod_action: bool,
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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 { if is_mod_action {
verify_mod_action(&common.actor, cc.clone(), context).await?; verify_mod_action(activity.actor(), community_id.clone(), context).await?;
} else { } else {
// domain of post ap_id and post.creator ap_id are identical, so we just check the former // 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(()) Ok(())
} }

View file

@ -15,10 +15,15 @@ use crate::{
extensions::context::lemmy_context, extensions::context::lemmy_context,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::{DeleteType, UndoType}; use activitystreams::{
activity::kind::UndoType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; 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_queries::source::{comment::Comment_, community::Community_, post::Post_};
use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
@ -27,18 +32,23 @@ use lemmy_websocket::{
LemmyContext, LemmyContext,
UserOperationCrud, UserOperationCrud,
}; };
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoDelete { pub struct UndoDelete {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: Delete, object: Delete,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -48,12 +58,12 @@ impl ActivityHandler for UndoDelete {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
verify_delete_activity( verify_delete_activity(
&self.object.object, &self.object.object,
self,
&self.cc[0], &self.cc[0],
&self.common,
self.object.summary.is_some(), self.object.summary.is_some(),
context, context,
request_counter, request_counter,
@ -72,7 +82,7 @@ impl ActivityHandler for UndoDelete {
} else { } else {
receive_delete_action( receive_delete_action(
&self.object.object, &self.object.object,
&self.common.actor, &self.actor,
WebsocketMessages { WebsocketMessages {
community: UserOperationCrud::EditCommunity, community: UserOperationCrud::EditCommunity,
post: UserOperationCrud::EditPost, post: UserOperationCrud::EditPost,
@ -85,10 +95,6 @@ impl ActivityHandler for UndoDelete {
.await .await
} }
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }
impl UndoDelete { impl UndoDelete {
@ -99,32 +105,18 @@ impl UndoDelete {
summary: Option<String>, summary: Option<String>,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let delete = Delete { let object = Delete::new(actor, community, object_id, summary)?;
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 id = generate_activity_id(UndoType::Undo)?; let id = generate_activity_id(UndoType::Undo)?;
let undo = UndoDelete { let undo = UndoDelete {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: delete, object,
cc: [community.actor_id()], cc: [community.actor_id()],
kind: UndoType::Undo, kind: UndoType::Undo,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::UndoDelete(undo); let activity = AnnouncableActivities::UndoDelete(undo);

View file

@ -10,9 +10,14 @@ use crate::{
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType, ActorType,
}; };
use activitystreams::activity::kind::AcceptType; use activitystreams::{
activity::kind::AcceptType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::{ApubObject, Followable};
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower}, community::{Community, CommunityFollower},
@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct AcceptFollowCommunity { pub struct AcceptFollowCommunity {
actor: Url,
to: Url, to: Url,
object: FollowCommunity, object: FollowCommunity,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: AcceptType, kind: AcceptType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl AcceptFollowCommunity { impl AcceptFollowCommunity {
@ -40,26 +50,23 @@ impl AcceptFollowCommunity {
Community::read_from_apub_id(conn, &community_id.into()) Community::read_from_apub_id(conn, &community_id.into())
}) })
.await??; .await??;
let person_id = follow.common.actor.clone(); let person_id = follow.actor().clone();
let person = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
Person::read_from_apub_id(conn, &person_id.into()) Person::read_from_apub_id(conn, &person_id.into())
}) })
.await??; .await??;
let id = generate_activity_id(AcceptType::Accept)?;
let accept = AcceptFollowCommunity { let accept = AcceptFollowCommunity {
actor: community.actor_id(),
to: person.actor_id(), to: person.actor_id(),
object: follow, object: follow,
kind: AcceptType::Accept, kind: AcceptType::Accept,
common: ActivityCommonFields { id: generate_activity_id(AcceptType::Accept)?,
context: lemmy_context(), context: lemmy_context(),
id: id.clone(),
actor: community.actor_id(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let inbox = vec![person.inbox_url.into()]; 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 /// Handle accepted follows
@ -70,10 +77,10 @@ impl ActivityHandler for AcceptFollowCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_urls_match(&self.to, &self.object.common.actor)?; verify_urls_match(&self.to, self.object.actor())?;
verify_urls_match(&self.common.actor, &self.object.to)?; verify_urls_match(&self.actor, &self.object.to)?;
verify_community(&self.common.actor, context, request_counter).await?; verify_community(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -83,8 +90,7 @@ impl ActivityHandler for AcceptFollowCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_community(&self.common.actor, context, request_counter).await?;
let to = get_or_fetch_and_upsert_person(&self.to, 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 // This will throw an error if no follow was requested
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
@ -94,8 +100,4 @@ impl ActivityHandler for AcceptFollowCommunity {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -10,9 +10,14 @@ use crate::{
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType, ActorType,
}; };
use activitystreams::activity::kind::FollowType; use activitystreams::{
activity::kind::FollowType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::Followable;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm}, community::{Community, CommunityFollower, CommunityFollowerForm},
@ -20,20 +25,39 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct FollowCommunity { pub struct FollowCommunity {
actor: Url,
pub(in crate::activities::following) to: Url, pub(in crate::activities::following) to: Url,
pub(in crate::activities::following) object: Url, pub(in crate::activities::following) object: Url,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(in crate::activities::following) kind: FollowType, kind: FollowType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
pub(in crate::activities::following) common: ActivityCommonFields, unparsed: Unparsed,
} }
impl FollowCommunity { impl FollowCommunity {
pub(in crate::activities::following) fn new(
actor: &Person,
community: &Community,
) -> Result<FollowCommunity, LemmyError> {
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( pub async fn send(
actor: &Person, actor: &Person,
community: &Community, community: &Community,
@ -49,20 +73,9 @@ impl FollowCommunity {
}) })
.await?; .await?;
let id = generate_activity_id(FollowType::Follow)?; let follow = FollowCommunity::new(actor, community)?;
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 inbox = vec![community.inbox_url.clone().into()]; 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, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_urls_match(&self.to, &self.object)?; 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(()) Ok(())
} }
@ -84,8 +97,7 @@ impl ActivityHandler for FollowCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
let community = let community =
get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?; get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -102,8 +114,4 @@ impl ActivityHandler for FollowCommunity {
AcceptFollowCommunity::send(self, context).await AcceptFollowCommunity::send(self, context).await
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -10,9 +10,14 @@ use crate::{
fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person}, fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
ActorType, 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_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_queries::Followable;
use lemmy_db_schema::source::{ use lemmy_db_schema::source::{
community::{Community, CommunityFollower, CommunityFollowerForm}, community::{Community, CommunityFollower, CommunityFollowerForm},
@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoFollowCommunity { pub struct UndoFollowCommunity {
actor: Url,
to: Url, to: Url,
object: FollowCommunity, object: FollowCommunity,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl UndoFollowCommunity { impl UndoFollowCommunity {
@ -39,30 +49,18 @@ impl UndoFollowCommunity {
community: &Community, community: &Community,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let id = generate_activity_id(UndoType::Undo)?; let object = FollowCommunity::new(actor, community)?;
let undo = UndoFollowCommunity { let undo = UndoFollowCommunity {
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(), actor: actor.actor_id(),
unparsed: Default::default(), to: community.actor_id(),
}, object,
},
kind: UndoType::Undo, kind: UndoType::Undo,
common: ActivityCommonFields { id: generate_activity_id(UndoType::Undo)?,
context: lemmy_context(), context: lemmy_context(),
id: id.clone(),
actor: actor.actor_id(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let inbox = vec![community.get_shared_inbox_or_inbox_url()]; 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, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_urls_match(&self.to, &self.object.object)?; verify_urls_match(&self.to, &self.object.object)?;
verify_urls_match(&self.common.actor, &self.object.common.actor)?; verify_urls_match(&self.actor, self.object.actor())?;
verify_person(&self.common.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -86,8 +84,7 @@ impl ActivityHandler for UndoFollowCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?; let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
let community_follower_form = CommunityFollowerForm { let community_follower_form = CommunityFollowerForm {
@ -103,8 +100,4 @@ impl ActivityHandler for UndoFollowCommunity {
.await?; .await?;
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -6,7 +6,7 @@ use crate::{
}; };
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; 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_queries::ApubObject;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, person::Person}, source::{community::Community, person::Person},
@ -90,9 +90,9 @@ async fn verify_community(
Ok(()) Ok(())
} }
fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> { fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> {
check_is_apub_id_valid(&common.actor, false)?; check_is_apub_id_valid(activity.actor(), false)?;
verify_domains_match(common.id_unchecked(), &common.actor)?; verify_domains_match(activity.id_unchecked(), activity.actor())?;
Ok(()) Ok(())
} }

View file

@ -14,31 +14,37 @@ use crate::{
objects::{post::Page, FromApub, ToApub}, objects::{post::Page, FromApub, ToApub},
ActorType, ActorType,
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; use lemmy_api_common::blocking;
use lemmy_apub_lib::{ use lemmy_apub_lib::{
values::PublicUrl, values::PublicUrl,
verify_domains_match, verify_domains_match,
verify_urls_match, verify_urls_match,
ActivityCommonFields, ActivityFields,
ActivityHandler, ActivityHandler,
}; };
use lemmy_db_queries::Crud; use lemmy_db_queries::Crud;
use lemmy_db_schema::source::{community::Community, person::Person, post::Post}; use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePost { pub struct CreateOrUpdatePost {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: Page, object: Page,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl CreateOrUpdatePost { impl CreateOrUpdatePost {
@ -56,16 +62,14 @@ impl CreateOrUpdatePost {
let id = generate_activity_id(kind.clone())?; let id = generate_activity_id(kind.clone())?;
let create_or_update = CreateOrUpdatePost { let create_or_update = CreateOrUpdatePost {
actor: actor.actor_id(),
to: PublicUrl::Public, to: PublicUrl::Public,
object: post.to_apub(context.pool()).await?, object: post.to_apub(context.pool()).await?,
cc: [community.actor_id()], cc: [community.actor_id()],
kind, kind,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update)); let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
@ -80,14 +84,14 @@ impl ActivityHandler for CreateOrUpdatePost {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
let community = extract_community(&self.cc, context, request_counter).await?; let community = extract_community(&self.cc, context, request_counter).await?;
let community_id = community.actor_id(); 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 { match self.kind {
CreateOrUpdateType::Create => { CreateOrUpdateType::Create => {
verify_domains_match(&self.common.actor, self.object.id_unchecked())?; verify_domains_match(&self.actor, self.object.id_unchecked())?;
verify_urls_match(&self.common.actor, &self.object.attributed_to)?; 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. // 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 // 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, // 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 => { CreateOrUpdateType::Update => {
let is_mod_action = self.object.is_mod_action(context.pool()).await?; let is_mod_action = self.object.is_mod_action(context.pool()).await?;
if is_mod_action { if is_mod_action {
verify_mod_action(&self.common.actor, community_id, context).await?; verify_mod_action(&self.actor, community_id, context).await?;
} else { } else {
verify_domains_match(&self.common.actor, self.object.id_unchecked())?; verify_domains_match(&self.actor, self.object.id_unchecked())?;
verify_urls_match(&self.common.actor, &self.object.attributed_to)?; verify_urls_match(&self.actor, &self.object.attributed_to)?;
} }
} }
} }
@ -117,8 +121,7 @@ impl ActivityHandler for CreateOrUpdatePost {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?; let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
let notif_type = match self.kind { 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?; send_post_ws_message(post.id, notif_type, None, None, context).await?;
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -5,23 +5,30 @@ use crate::{
objects::{private_message::Note, FromApub, ToApub}, objects::{private_message::Note, FromApub, ToApub},
ActorType, ActorType,
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use lemmy_api_common::blocking; 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_queries::Crud;
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct CreateOrUpdatePrivateMessage { pub struct CreateOrUpdatePrivateMessage {
#[serde(rename = "@context")]
pub context: OneOrMany<AnyBase>,
id: Url,
actor: Url,
to: Url, to: Url,
cc: [Url; 0],
object: Note, object: Note,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: CreateOrUpdateType, kind: CreateOrUpdateType,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, pub unparsed: Unparsed,
} }
impl CreateOrUpdatePrivateMessage { impl CreateOrUpdatePrivateMessage {
@ -37,15 +44,14 @@ impl CreateOrUpdatePrivateMessage {
let id = generate_activity_id(kind.clone())?; let id = generate_activity_id(kind.clone())?;
let create_or_update = CreateOrUpdatePrivateMessage { let create_or_update = CreateOrUpdatePrivateMessage {
to: recipient.actor_id(),
object: private_message.to_apub(context.pool()).await?,
kind,
common: ActivityCommonFields {
context: lemmy_context(), context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), actor: actor.actor_id(),
to: recipient.actor_id(),
cc: [],
object: private_message.to_apub(context.pool()).await?,
kind,
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
send_activity_new(context, &create_or_update, &id, actor, inbox, true).await send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
@ -58,9 +64,9 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person(&self.common.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_domains_match(&self.common.actor, self.object.id_unchecked())?; verify_domains_match(&self.actor, self.object.id_unchecked())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -71,7 +77,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let private_message = 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 { let notif_type = match self.kind {
CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage, CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
@ -81,8 +87,4 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -4,50 +4,64 @@ use crate::{
extensions::context::lemmy_context, extensions::context::lemmy_context,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::DeleteType; use activitystreams::{
activity::kind::DeleteType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DeletePrivateMessage { pub struct DeletePrivateMessage {
pub(in crate::activities::private_message) to: Url, actor: Url,
to: Url,
pub(in crate::activities::private_message) object: Url, pub(in crate::activities::private_message) object: Url,
#[serde(rename = "type")] #[serde(rename = "type")]
pub(in crate::activities::private_message) kind: DeleteType, kind: DeleteType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
pub(in crate::activities::private_message) common: ActivityCommonFields, unparsed: Unparsed,
} }
impl DeletePrivateMessage { impl DeletePrivateMessage {
pub(in crate::activities::private_message) fn new(
actor: &Person,
pm: &PrivateMessage,
) -> Result<DeletePrivateMessage, LemmyError> {
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( pub async fn send(
actor: &Person, actor: &Person,
pm: &PrivateMessage, pm: &PrivateMessage,
context: &LemmyContext, context: &LemmyContext,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let delete = DeletePrivateMessage::new(actor, pm)?;
let delete_id = delete.id.clone();
let recipient_id = pm.recipient_id; let recipient_id = pm.recipient_id;
let recipient = let recipient =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; 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()]; 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, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person(&self.common.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_domains_match(&self.common.actor, &self.object)?; verify_domains_match(&self.actor, &self.object)?;
Ok(()) Ok(())
} }
@ -89,8 +103,4 @@ impl ActivityHandler for DeletePrivateMessage {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -9,29 +9,34 @@ use crate::{
extensions::context::lemmy_context, extensions::context::lemmy_context,
ActorType, ActorType,
}; };
use activitystreams::activity::kind::{DeleteType, UndoType}; use activitystreams::{
use lemmy_api_common::blocking; activity::kind::UndoType,
use lemmy_apub_lib::{ base::AnyBase,
verify_domains_match, primitives::OneOrMany,
verify_urls_match, unparsed::Unparsed,
ActivityCommonFields,
ActivityHandler,
}; };
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_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage}; use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoDeletePrivateMessage { pub struct UndoDeletePrivateMessage {
actor: Url,
to: Url, to: Url,
object: DeletePrivateMessage, object: DeletePrivateMessage,
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl UndoDeletePrivateMessage { impl UndoDeletePrivateMessage {
@ -44,29 +49,16 @@ impl UndoDeletePrivateMessage {
let recipient = let recipient =
blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??; blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
let object = DeletePrivateMessage { let object = DeletePrivateMessage::new(actor, pm)?;
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 id = generate_activity_id(UndoType::Undo)?; let id = generate_activity_id(UndoType::Undo)?;
let undo = UndoDeletePrivateMessage { let undo = UndoDeletePrivateMessage {
actor: actor.actor_id(),
to: recipient.actor_id(), to: recipient.actor_id(),
object, object,
kind: UndoType::Undo, kind: UndoType::Undo,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let inbox = vec![recipient.get_shared_inbox_or_inbox_url()]; let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
send_activity_new(context, &undo, &id, actor, inbox, true).await send_activity_new(context, &undo, &id, actor, inbox, true).await
@ -80,10 +72,10 @@ impl ActivityHandler for UndoDeletePrivateMessage {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person(&self.common.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_urls_match(&self.common.actor, &self.object.common.actor)?; verify_urls_match(&self.actor, self.object.actor())?;
verify_domains_match(&self.common.actor, &self.object.object)?; verify_domains_match(&self.actor, &self.object.object)?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -114,8 +106,4 @@ impl ActivityHandler for UndoDeletePrivateMessage {
Ok(()) Ok(())
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -3,23 +3,33 @@ use crate::activities::{
deletion::{undo_delete::UndoDelete, verify_delete_activity}, deletion::{undo_delete::UndoDelete, verify_delete_activity},
verify_activity, verify_activity,
}; };
use activitystreams::activity::kind::UndoType; use activitystreams::{
use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; activity::kind::UndoType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoRemovePostCommentOrCommunity { pub struct UndoRemovePostCommentOrCommunity {
actor: Url,
to: PublicUrl, to: PublicUrl,
// Note, there is no such thing as Undo/Remove/Mod, so we ignore that // Note, there is no such thing as Undo/Remove/Mod, so we ignore that
object: RemoveMod, object: RemoveMod,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -29,13 +39,13 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
verify_delete_activity( verify_delete_activity(
&self.object.object, &self.object.object,
self,
&self.cc[0], &self.cc[0],
self.common(),
true, true,
context, context,
request_counter, request_counter,
@ -51,8 +61,4 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
UndoDelete::receive_undo_remove_action(&self.object.object, context).await UndoDelete::receive_undo_remove_action(&self.object.object, context).await
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -19,9 +19,14 @@ use crate::{
ActorType, ActorType,
PostOrComment, PostOrComment,
}; };
use activitystreams::activity::kind::UndoType; use activitystreams::{
activity::kind::UndoType,
base::AnyBase,
primitives::OneOrMany,
unparsed::Unparsed,
};
use lemmy_api_common::blocking; 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_queries::Crud;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, person::Person}, source::{community::Community, person::Person},
@ -29,19 +34,24 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
use std::ops::Deref; use std::ops::Deref;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct UndoVote { pub struct UndoVote {
actor: Url,
to: PublicUrl, to: PublicUrl,
object: Vote, object: Vote,
cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
kind: UndoType, kind: UndoType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
common: ActivityCommonFields, unparsed: Unparsed,
} }
impl UndoVote { impl UndoVote {
@ -56,30 +66,18 @@ impl UndoVote {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .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 { let undo_vote = UndoVote {
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(), actor: actor.actor_id(),
unparsed: Default::default(), to: PublicUrl::Public,
}, object,
},
cc: [community.actor_id()], cc: [community.actor_id()],
kind: UndoType::Undo, kind: UndoType::Undo,
common: ActivityCommonFields {
context: lemmy_context(),
id: id.clone(), id: id.clone(),
actor: actor.actor_id(), context: lemmy_context(),
unparsed: Default::default(), unparsed: Default::default(),
},
}; };
let activity = AnnouncableActivities::UndoVote(undo_vote); let activity = AnnouncableActivities::UndoVote(undo_vote);
send_to_community_new(activity, &id, actor, &community, vec![], context).await send_to_community_new(activity, &id, actor, &community, vec![], context).await
@ -93,9 +91,9 @@ impl ActivityHandler for UndoVote {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
verify_urls_match(&self.common.actor, &self.object.common().actor)?; verify_urls_match(&self.actor, self.object.actor())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())
} }
@ -105,8 +103,7 @@ impl ActivityHandler for UndoVote {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
let object = let object =
get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter) get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
.await?; .await?;
@ -115,8 +112,4 @@ impl ActivityHandler for UndoVote {
PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await, PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
} }
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -15,9 +15,10 @@ use crate::{
ActorType, ActorType,
PostOrComment, PostOrComment,
}; };
use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::blocking; 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_queries::Crud;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, person::Person}, 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")] #[serde(rename_all = "camelCase")]
pub struct Vote { 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) object: Url,
pub(in crate::activities::voting) cc: [Url; 1], cc: [Url; 1],
#[serde(rename = "type")] #[serde(rename = "type")]
pub(in crate::activities::voting) kind: VoteType, pub(in crate::activities::voting) kind: VoteType,
id: Url,
#[serde(rename = "@context")]
context: OneOrMany<AnyBase>,
#[serde(flatten)] #[serde(flatten)]
pub(in crate::activities::voting) common: ActivityCommonFields, unparsed: Unparsed,
} }
impl Vote { impl Vote {
pub(in crate::activities::voting) fn new(
object: &PostOrComment,
actor: &Person,
community: &Community,
kind: VoteType,
) -> Result<Vote, LemmyError> {
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( pub async fn send(
object: &PostOrComment, object: &PostOrComment,
actor: &Person, actor: &Person,
@ -81,22 +104,11 @@ impl Vote {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .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); 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, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_activity(self.common())?; verify_activity(self)?;
verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
Ok(()) Ok(())
} }
@ -117,8 +129,7 @@ impl ActivityHandler for Vote {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let actor = let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
let object = let object =
get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?; get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
match object { match object {
@ -126,8 +137,4 @@ impl ActivityHandler for Vote {
PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await, PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
} }
} }
fn common(&self) -> &ActivityCommonFields {
&self.common
}
} }

View file

@ -21,7 +21,7 @@ use activitystreams::{
}; };
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking; 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_queries::source::{activity::Activity_, community::Community_};
use lemmy_db_schema::source::{activity::Activity, community::Community}; use lemmy_db_schema::source::{activity::Activity, community::Community};
use lemmy_db_views_actor::{ 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)] #[serde(untagged)]
pub enum GroupInboxActivities { pub enum GroupInboxActivities {
FollowCommunity(FollowCommunity), FollowCommunity(FollowCommunity),

View file

@ -20,7 +20,7 @@ use anyhow::{anyhow, Context};
use futures::StreamExt; use futures::StreamExt;
use http::StatusCode; use http::StatusCode;
use lemmy_api_common::blocking; 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_queries::{source::activity::Activity_, DbPool};
use lemmy_db_schema::source::activity::Activity; use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
@ -36,7 +36,7 @@ mod person;
mod post; mod post;
pub mod routes; pub mod routes;
#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)] #[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
#[serde(untagged)] #[serde(untagged)]
pub enum SharedInboxActivities { pub enum SharedInboxActivities {
GroupInboxActivities(GroupInboxActivities), GroupInboxActivities(GroupInboxActivities),
@ -80,30 +80,32 @@ async fn receive_activity<'a, T>(
context: &LemmyContext, context: &LemmyContext,
) -> Result<HttpResponse, LemmyError> ) -> Result<HttpResponse, LemmyError>
where 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 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!())?)?; verify_signature(&request, &actor.public_key().context(location_info!())?)?;
// Do nothing if we received the same activity before // 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()); return Ok(HttpResponse::Ok().finish());
} }
check_is_apub_id_valid(&activity_data.actor, false)?; check_is_apub_id_valid(activity.actor(), false)?;
info!( info!("Verifying activity {}", activity.id_unchecked().to_string());
"Verifying activity {}",
activity_data.id_unchecked().to_string()
);
activity.verify(context, request_counter).await?; activity.verify(context, request_counter).await?;
assert_activity_not_local(&activity)?; assert_activity_not_local(&activity)?;
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen // 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. // if we receive the same activity twice in very quick succession.
insert_activity( insert_activity(
activity_data.id_unchecked(), activity.id_unchecked(),
activity.clone(), activity.clone(),
false, false,
true, true,
@ -111,10 +113,7 @@ where
) )
.await?; .await?;
info!( info!("Receiving activity {}", activity.id_unchecked().to_string());
"Receiving activity {}",
activity_data.id_unchecked().to_string()
);
activity.receive(context, request_counter).await?; activity.receive(context, request_counter).await?;
Ok(HttpResponse::Ok().finish()) Ok(HttpResponse::Ok().finish())
} }
@ -187,12 +186,8 @@ pub(crate) async fn is_activity_already_known(
} }
} }
fn assert_activity_not_local<T: Debug + ActivityHandler>(activity: &T) -> Result<(), LemmyError> { fn assert_activity_not_local<T: Debug + ActivityFields>(activity: &T) -> Result<(), LemmyError> {
let activity_domain = activity let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
.common()
.id_unchecked()
.domain()
.context(location_info!())?;
if activity_domain == Settings::get().hostname { if activity_domain == Settings::get().hostname {
return Err( return Err(

View file

@ -1,6 +1,6 @@
use crate::{ use crate::{
activities::{ activities::{
community::announce::AnnounceActivity, community::announce::{AnnouncableActivities, AnnounceActivity},
following::accept::AcceptFollowCommunity, following::accept::AcceptFollowCommunity,
private_message::{ private_message::{
create_or_update::CreateOrUpdatePrivateMessage, create_or_update::CreateOrUpdatePrivateMessage,
@ -24,7 +24,7 @@ use activitystreams::{
}; };
use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse}; use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
use lemmy_api_common::blocking; 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_queries::source::person::Person_;
use lemmy_db_schema::source::person::Person; use lemmy_db_schema::source::person::Person;
use lemmy_utils::LemmyError; 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)] #[serde(untagged)]
pub enum PersonInboxActivities { pub enum PersonInboxActivities {
AcceptFollowCommunity(AcceptFollowCommunity), AcceptFollowCommunity(AcceptFollowCommunity),
/// Some activities can also be sent from user to user, eg a comment with mentions
AnnouncableActivities(AnnouncableActivities),
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage), CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
DeletePrivateMessage(DeletePrivateMessage), DeletePrivateMessage(DeletePrivateMessage),
UndoDeletePrivateMessage(UndoDeletePrivateMessage), UndoDeletePrivateMessage(UndoDeletePrivateMessage),

View file

@ -1,33 +1,15 @@
pub mod values; pub mod values;
use activitystreams::{ use activitystreams::error::DomainError;
base::AnyBase,
error::DomainError,
primitives::OneOrMany,
unparsed::Unparsed,
};
pub use lemmy_apub_lib_derive::*; pub use lemmy_apub_lib_derive::*;
use lemmy_utils::LemmyError; use lemmy_utils::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] pub trait ActivityFields {
#[serde(rename_all = "camelCase")] fn id_unchecked(&self) -> &Url;
pub struct ActivityCommonFields { fn actor(&self) -> &Url;
#[serde(rename = "@context")] fn cc(&self) -> Vec<Url>;
pub context: OneOrMany<AnyBase>,
pub id: Url,
pub actor: Url,
// unparsed fields
#[serde(flatten)]
pub unparsed: Unparsed,
}
impl ActivityCommonFields {
pub fn id_unchecked(&self) -> &Url {
&self.id
}
} }
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
@ -43,7 +25,6 @@ pub trait ActivityHandler {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError>; ) -> Result<(), LemmyError>;
fn common(&self) -> &ActivityCommonFields;
} }
pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> { pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {

View file

@ -1,6 +1,6 @@
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::quote; 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 /// Generates implementation ActivityHandler for an enum, which looks like the following (handling
/// all enum variants). /// 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)] #[proc_macro_derive(ActivityHandler)]
pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 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); let input = parse_macro_input!(input as DeriveInput);
// Used in the quasi-quotation below as `#name`. let enum_name = input.ident;
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let input_enum = if let Data::Enum(d) = input.data { let enum_variants = if let Data::Enum(d) = input.data {
d d.variants
} else { } else {
unimplemented!() unimplemented!()
}; };
let impl_verify = input_enum let body_verify = quote! {a.verify(context, request_counter).await};
.variants let impl_verify = enum_variants
.iter() .iter()
.map(|variant| variant_impl_verify(&name, variant)); .map(|v| generate_match_arm(&enum_name, v, &body_verify));
let impl_receive = input_enum let body_receive = quote! {a.receive(context, request_counter).await};
.variants let impl_receive = enum_variants
.iter() .iter()
.map(|variant| variant_impl_receive(&name, variant)); .map(|v| generate_match_arm(&enum_name, v, &body_receive));
let impl_common = input_enum
.variants
.iter()
.map(|variant| variant_impl_common(&name, variant));
// The generated impl.
let expanded = quote! { let expanded = quote! {
#[async_trait::async_trait(?Send)] #[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( async fn verify(
&self, &self,
context: &LemmyContext, context: &lemmy_websocket::LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), lemmy_utils::LemmyError> {
match self { match self {
#(#impl_verify)* #(#impl_verify)*
} }
} }
async fn receive( async fn receive(
self, self,
context: &LemmyContext, context: &lemmy_websocket::LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), lemmy_utils::LemmyError> {
match self { match self {
#(#impl_receive)* #(#impl_receive)*
} }
} }
fn common(&self) -> &ActivityCommonFields {
match self {
#(#impl_common)*
}
}
} }
}; };
expanded.into()
// Hand the output tokens back to the compiler.
proc_macro::TokenStream::from(expanded)
} }
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; let id = &variant.ident;
match &variant.fields { match &variant.fields {
syn::Fields::Unnamed(_) => { Unnamed(_) => {
quote! { quote! {
#name::#id(a) => a.common(), #enum_name::#id(a) => #body,
} }
} }
_ => unimplemented!(), _ => unimplemented!(),
} }
} }
fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream { #[proc_macro_derive(ActivityFields)]
let id = &variant.ident; pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match &variant.fields { let input = parse_macro_input!(input as DeriveInput);
syn::Fields::Unnamed(_) => {
quote! {
#name::#id(a) => a.verify(context, request_counter).await,
}
}
_ => unimplemented!(),
}
}
fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream { let name = input.ident;
let id = &variant.ident;
match &variant.fields { let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
syn::Fields::Unnamed(_) => {
let expanded = match input.data {
Data::Enum(e) => {
let variants = e.variants;
let impl_id = variants
.iter()
.map(|v| generate_match_arm(&name, v, &quote! {a.id_unchecked()}));
let impl_actor = variants
.iter()
.map(|v| generate_match_arm(&name, v, &quote! {a.actor()}));
let impl_cc = variants
.iter()
.map(|v| generate_match_arm(&name, v, &quote! {a.cc()}));
quote! { 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<url::Url> { 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<url::Url> { #cc_impl }
}
} }
} }
_ => unimplemented!(), _ => unimplemented!(),
} };
expanded.into()
} }