]> Untitled Git - lemmy.git/commitdiff
Rewrite remaining activities (#1712)
authorNutomic <me@nutomic.com>
Thu, 19 Aug 2021 21:24:33 +0000 (21:24 +0000)
committerGitHub <noreply@github.com>
Thu, 19 Aug 2021 21:24:33 +0000 (17:24 -0400)
* Limit type/method visibility in apub code

* Simplify db_queries traits by removing generics

* Simplify delete activity implementation

* Rewrite delete activities

* Implement helper functions for websocket message sending

* When receiving delete reason as empty string, change to none

* Rewrite remaining activities

* Simplify inbox

* Remove struct ActivityCommonFields, derive ActivityFields trait instead

* Community should announce received activities to followers

35 files changed:
crates/api/src/community.rs
crates/api_crud/src/community/update.rs
crates/apub/src/activities/comment/create_or_update.rs
crates/apub/src/activities/community/add_mod.rs
crates/apub/src/activities/community/announce.rs
crates/apub/src/activities/community/block_user.rs
crates/apub/src/activities/community/mod.rs
crates/apub/src/activities/community/remove_mod.rs [new file with mode: 0644]
crates/apub/src/activities/community/undo_block_user.rs
crates/apub/src/activities/community/update.rs
crates/apub/src/activities/deletion/delete.rs
crates/apub/src/activities/deletion/mod.rs
crates/apub/src/activities/deletion/undo_delete.rs
crates/apub/src/activities/following/accept.rs
crates/apub/src/activities/following/follow.rs
crates/apub/src/activities/following/undo.rs
crates/apub/src/activities/mod.rs
crates/apub/src/activities/post/create_or_update.rs
crates/apub/src/activities/private_message/create_or_update.rs
crates/apub/src/activities/private_message/delete.rs
crates/apub/src/activities/private_message/undo_delete.rs
crates/apub/src/activities/removal/mod.rs [deleted file]
crates/apub/src/activities/removal/remove.rs [deleted file]
crates/apub/src/activities/send/community.rs
crates/apub/src/activities/undo_remove.rs [moved from crates/apub/src/activities/removal/undo_remove.rs with 69% similarity]
crates/apub/src/activities/voting/undo_vote.rs
crates/apub/src/activities/voting/vote.rs
crates/apub/src/activity_queue.rs
crates/apub/src/http/community.rs
crates/apub/src/http/inbox_enums.rs [deleted file]
crates/apub/src/http/mod.rs
crates/apub/src/http/person.rs
crates/apub/src/lib.rs
crates/apub_lib/src/lib.rs
crates/apub_lib_derive/src/lib.rs

index 5aefd1443a071f48c520f0419615a973f29a8c1f..d68d27c2f7a2234aa872cdd50106509961b8cdc4 100644 (file)
@@ -8,12 +8,14 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   is_mod_or_admin,
 };
-use lemmy_apub::{
-  activities::following::{
-    follow::FollowCommunity as FollowCommunityApub,
-    undo::UndoFollowCommunity,
+use lemmy_apub::activities::{
+  community::{
+    add_mod::AddMod,
+    block_user::BlockUserFromCommunity,
+    remove_mod::RemoveMod,
+    undo_block_user::UndoBlockUserFromCommunity,
   },
-  CommunityType,
+  following::{follow::FollowCommunity as FollowCommunityApub, undo::UndoFollowCommunity},
 };
 use lemmy_db_queries::{
   source::{comment::Comment_, community::CommunityModerator_, post::Post_},
@@ -219,17 +221,20 @@ impl Perform for BanFromCommunity {
       .await?
       .ok();
 
-      community
-        .send_block_user(&local_user_view.person, banned_person, context)
+      BlockUserFromCommunity::send(&community, &banned_person, &local_user_view.person, context)
         .await?;
     } else {
       let unban = move |conn: &'_ _| CommunityPersonBan::unban(conn, &community_user_ban_form);
       if blocking(context.pool(), unban).await?.is_err() {
         return Err(ApiError::err("community_user_already_banned").into());
       }
-      community
-        .send_undo_block_user(&local_user_view.person, banned_person, context)
-        .await?;
+      UndoBlockUserFromCommunity::send(
+        &community,
+        &banned_person,
+        &local_user_view.person,
+        context,
+      )
+      .await?;
     }
 
     // Remove/Restore their data if that's desired
@@ -356,13 +361,9 @@ impl Perform for AddModToCommunity {
     })
     .await??;
     if data.added {
-      community
-        .send_add_mod(&local_user_view.person, updated_mod, context)
-        .await?;
+      AddMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
     } else {
-      community
-        .send_remove_mod(&local_user_view.person, updated_mod, context)
-        .await?;
+      RemoveMod::send(&community, &updated_mod, &local_user_view.person, context).await?;
     }
 
     // Note: in case a remote mod is added, this returns the old moderators list, it will only get
index 2a99c76db5e6fc46760987283eba97c1c51a352a..2196c57b77e51bf316b31cb8a4897496a5b71898 100644 (file)
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   community::{CommunityResponse, EditCommunity},
   get_local_user_view_from_jwt,
 };
-use lemmy_apub::CommunityType;
+use lemmy_apub::activities::community::update::UpdateCommunity;
 use lemmy_db_queries::{diesel_option_overwrite_to_url, Crud};
 use lemmy_db_schema::{
   naive_now,
@@ -69,9 +69,7 @@ impl PerformCrud for EditCommunity {
     .await?
     .map_err(|_| ApiError::err("couldnt_update_community"))?;
 
-    updated_community
-      .send_update(local_user_view.person.to_owned(), context)
-      .await?;
+    UpdateCommunity::send(&updated_community, &local_user_view.person, context).await?;
 
     let op = UserOperationCrud::EditCommunity;
     send_community_ws_message(data.community_id, op, websocket_id, None, context).await
index cb9b2349781f43f26642d1ed7ba65299fc1affac..7f83d78faae01b9cf20ba0241331d304c32d016a 100644 (file)
@@ -13,14 +13,9 @@ use crate::{
   objects::{comment::Note, FromApub, ToApub},
   ActorType,
 };
-use activitystreams::link::Mention;
+use activitystreams::{base::AnyBase, link::Mention, primitives::OneOrMany, unparsed::Unparsed};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  values::PublicUrl,
-  verify_domains_match,
-  ActivityCommonFields,
-  ActivityHandler,
-};
+use lemmy_apub_lib::{values::PublicUrl, verify_domains_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Crud;
 use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
 use lemmy_utils::LemmyError;
@@ -28,17 +23,21 @@ use lemmy_websocket::{send::send_comment_ws_message, LemmyContext, UserOperation
 use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct CreateOrUpdateComment {
+  actor: Url,
   to: PublicUrl,
   object: Note,
   cc: Vec<Url>,
   tag: Vec<Mention>,
   #[serde(rename = "type")]
   kind: CreateOrUpdateType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl CreateOrUpdateComment {
@@ -61,17 +60,15 @@ impl CreateOrUpdateComment {
     let maa = collect_non_local_mentions(comment, &community, context).await?;
 
     let create_or_update = CreateOrUpdateComment {
+      actor: actor.actor_id(),
       to: PublicUrl::Public,
       object: comment.to_apub(context.pool()).await?,
       cc: maa.ccs,
       tag: maa.tags,
       kind,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
 
     let activity = AnnouncableActivities::CreateOrUpdateComment(create_or_update);
@@ -88,15 +85,10 @@ impl ActivityHandler for CreateOrUpdateComment {
   ) -> Result<(), LemmyError> {
     let community = extract_community(&self.cc, context, request_counter).await?;
 
-    verify_activity(self.common())?;
-    verify_person_in_community(
-      &self.common.actor,
-      &community.actor_id(),
-      context,
-      request_counter,
-    )
-    .await?;
-    verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &community.actor_id(), context, request_counter)
+      .await?;
+    verify_domains_match(&self.actor, self.object.id_unchecked())?;
     // TODO: should add a check that the correct community is in cc (probably needs changes to
     //       comment deserialization)
     self.object.verify(context, request_counter).await?;
@@ -108,10 +100,8 @@ impl ActivityHandler for CreateOrUpdateComment {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let comment =
-      Comment::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
-    let recipients =
-      get_notif_recipients(&self.common.actor, &comment, context, request_counter).await?;
+    let comment = Comment::from_apub(&self.object, context, &self.actor, request_counter).await?;
+    let recipients = get_notif_recipients(&self.actor, &comment, context, request_counter).await?;
     let notif_type = match self.kind {
       CreateOrUpdateType::Create => UserOperationCrud::CreateComment,
       CreateOrUpdateType::Update => UserOperationCrud::EditComment,
@@ -122,8 +112,4 @@ impl ActivityHandler for CreateOrUpdateComment {
     .await?;
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index db6d369a192350ba64fed267c1c9f3e2875fa31e..f83f14630d3f34fcc57f8d8db651ac4b357e796c 100644 (file)
@@ -1,33 +1,77 @@
 use crate::{
   activities::{
+    community::announce::AnnouncableActivities,
+    generate_activity_id,
     verify_activity,
     verify_add_remove_moderator_target,
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_queue::send_to_community_new,
+  extensions::context::lemmy_context,
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
-  CommunityType,
+  generate_moderators_url,
+  ActorType,
+};
+use activitystreams::{
+  activity::kind::AddType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams::{activity::kind::AddType, base::AnyBase};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{source::community::CommunityModerator_, Joinable};
-use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityModerator, CommunityModeratorForm},
+  person::Person,
+};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct AddMod {
+  actor: Url,
   to: PublicUrl,
   object: Url,
   target: Url,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: AddType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
+}
+
+impl AddMod {
+  pub async fn send(
+    community: &Community,
+    added_mod: &Person,
+    actor: &Person,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let id = generate_activity_id(AddType::Add)?;
+    let add = AddMod {
+      actor: actor.actor_id(),
+      to: PublicUrl::Public,
+      object: added_mod.actor_id(),
+      target: generate_moderators_url(&community.actor_id)?.into(),
+      cc: [community.actor_id()],
+      kind: AddType::Add,
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
+    };
+
+    let activity = AnnouncableActivities::AddMod(add);
+    let inboxes = vec![added_mod.get_shared_inbox_or_inbox_url()];
+    send_to_community_new(activity, &id, actor, community, inboxes, context).await
+  }
 }
 
 #[async_trait::async_trait(?Send)]
@@ -37,9 +81,9 @@ impl ActivityHandler for AddMod {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-    verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+    verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
     verify_add_remove_moderator_target(&self.target, self.cc[0].clone())?;
     Ok(())
   }
@@ -70,17 +114,7 @@ impl ActivityHandler for AddMod {
       })
       .await??;
     }
-    if community.local {
-      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
-      community
-        .send_announce(anybase, Some(self.object.clone()), context)
-        .await?;
-    }
     // TODO: send websocket notification about added mod
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 734fed00e61ba1f8d7b45efa1802ef06720a12d7..e7d917b72d9524827088e9a761f48b7ab42aeba8 100644 (file)
@@ -5,12 +5,14 @@ use crate::{
       add_mod::AddMod,
       block_user::BlockUserFromCommunity,
       list_community_follower_inboxes,
+      remove_mod::RemoveMod,
       undo_block_user::UndoBlockUserFromCommunity,
+      update::UpdateCommunity,
     },
     deletion::{delete::Delete, undo_delete::UndoDelete},
     generate_activity_id,
     post::create_or_update::CreateOrUpdatePost,
-    removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
+    undo_remove::UndoRemovePostCommentOrCommunity,
     verify_activity,
     verify_community,
     voting::{undo_vote::UndoVote, vote::Vote},
@@ -22,15 +24,20 @@ use crate::{
   ActorType,
   CommunityType,
 };
-use activitystreams::activity::kind::AnnounceType;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use activitystreams::{
+  activity::kind::AnnounceType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_schema::source::community::Community;
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
 #[serde(untagged)]
 pub enum AnnouncableActivities {
   CreateOrUpdateComment(CreateOrUpdateComment),
@@ -40,22 +47,27 @@ pub enum AnnouncableActivities {
   Delete(Delete),
   UndoDelete(UndoDelete),
   UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
+  UpdateCommunity(Box<UpdateCommunity>),
   BlockUserFromCommunity(BlockUserFromCommunity),
   UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
   AddMod(AddMod),
   RemoveMod(RemoveMod),
 }
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct AnnounceActivity {
+  actor: Url,
   to: PublicUrl,
   object: AnnouncableActivities,
   cc: Vec<Url>,
   #[serde(rename = "type")]
   kind: AnnounceType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl AnnounceActivity {
@@ -66,27 +78,17 @@ impl AnnounceActivity {
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
     let announce = AnnounceActivity {
+      actor: community.actor_id(),
       to: PublicUrl::Public,
       object,
       cc: vec![community.followers_url()],
       kind: AnnounceType::Announce,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: generate_activity_id(&AnnounceType::Announce)?,
-        actor: community.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: generate_activity_id(&AnnounceType::Announce)?,
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
     let inboxes = list_community_follower_inboxes(community, additional_inboxes, context).await?;
-    send_activity_new(
-      context,
-      &announce,
-      &announce.common.id,
-      community,
-      inboxes,
-      false,
-    )
-    .await
+    send_activity_new(context, &announce, &announce.id, community, inboxes, false).await
   }
 }
 
@@ -97,8 +99,8 @@ impl ActivityHandler for AnnounceActivity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_community(&self.common.actor, context, request_counter).await?;
+    verify_activity(self)?;
+    verify_community(&self.actor, context, request_counter).await?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -108,11 +110,11 @@ impl ActivityHandler for AnnounceActivity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    if is_activity_already_known(context.pool(), self.object.common().id_unchecked()).await? {
+    if is_activity_already_known(context.pool(), self.object.id_unchecked()).await? {
       return Ok(());
     }
     insert_activity(
-      self.object.common().id_unchecked(),
+      self.object.id_unchecked(),
       self.object.clone(),
       false,
       true,
@@ -121,8 +123,4 @@ impl ActivityHandler for AnnounceActivity {
     .await?;
     self.object.receive(context, request_counter).await
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index bf362c4827a45a9a0445c54a72f6998ab3ea76c1..d31077dfa28e5e6216b42304acd984ddbdfa9a1f 100644 (file)
@@ -1,31 +1,87 @@
 use crate::{
-  activities::{verify_activity, verify_mod_action, verify_person_in_community},
+  activities::{
+    community::announce::AnnouncableActivities,
+    generate_activity_id,
+    verify_activity,
+    verify_mod_action,
+    verify_person_in_community,
+  },
+  activity_queue::send_to_community_new,
+  extensions::context::lemmy_context,
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+  ActorType,
+};
+use activitystreams::{
+  activity::kind::BlockType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams::activity::kind::BlockType;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{Bannable, Followable};
-use lemmy_db_schema::source::community::{
-  CommunityFollower,
-  CommunityFollowerForm,
-  CommunityPersonBan,
-  CommunityPersonBanForm,
+use lemmy_db_schema::source::{
+  community::{
+    Community,
+    CommunityFollower,
+    CommunityFollowerForm,
+    CommunityPersonBan,
+    CommunityPersonBanForm,
+  },
+  person::Person,
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct BlockUserFromCommunity {
+  actor: Url,
   to: PublicUrl,
   pub(in crate::activities::community) object: Url,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: BlockType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
+}
+
+impl BlockUserFromCommunity {
+  pub(in crate::activities::community) fn new(
+    community: &Community,
+    target: &Person,
+    actor: &Person,
+  ) -> Result<BlockUserFromCommunity, LemmyError> {
+    Ok(BlockUserFromCommunity {
+      actor: actor.actor_id(),
+      to: PublicUrl::Public,
+      object: target.actor_id(),
+      cc: [community.actor_id()],
+      kind: BlockType::Block,
+      id: generate_activity_id(BlockType::Block)?,
+      context: lemmy_context(),
+      unparsed: Default::default(),
+    })
+  }
+
+  pub async fn send(
+    community: &Community,
+    target: &Person,
+    actor: &Person,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let block = BlockUserFromCommunity::new(community, target, actor)?;
+    let block_id = block.id.clone();
+
+    let activity = AnnouncableActivities::BlockUserFromCommunity(block);
+    let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
+    send_to_community_new(activity, &block_id, actor, community, inboxes, context).await
+  }
 }
 
 #[async_trait::async_trait(?Send)]
@@ -35,9 +91,9 @@ impl ActivityHandler for BlockUserFromCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-    verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+    verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
     Ok(())
   }
 
@@ -75,8 +131,4 @@ impl ActivityHandler for BlockUserFromCommunity {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 49cf9615f527c3b45c416619b5d5caf9342c6988..ba80ff4ad080f89cde0f7c597e753ddb8aa0face 100644 (file)
@@ -8,6 +8,7 @@ use url::Url;
 pub mod add_mod;
 pub mod announce;
 pub mod block_user;
+pub mod remove_mod;
 pub mod undo_block_user;
 pub mod update;
 
diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs
new file mode 100644 (file)
index 0000000..2d4eba5
--- /dev/null
@@ -0,0 +1,130 @@
+use crate::{
+  activities::{
+    community::announce::AnnouncableActivities,
+    deletion::{delete::receive_remove_action, verify_delete_activity},
+    generate_activity_id,
+    verify_activity,
+    verify_add_remove_moderator_target,
+    verify_mod_action,
+    verify_person_in_community,
+  },
+  activity_queue::send_to_community_new,
+  extensions::context::lemmy_context,
+  fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+  generate_moderators_url,
+  ActorType,
+};
+use activitystreams::{
+  activity::kind::RemoveType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
+use lemmy_db_queries::Joinable;
+use lemmy_db_schema::source::{
+  community::{Community, CommunityModerator, CommunityModeratorForm},
+  person::Person,
+};
+use lemmy_utils::LemmyError;
+use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
+use url::Url;
+
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
+#[serde(rename_all = "camelCase")]
+pub struct RemoveMod {
+  actor: Url,
+  to: PublicUrl,
+  pub(in crate::activities) object: Url,
+  cc: [Url; 1],
+  #[serde(rename = "type")]
+  kind: RemoveType,
+  // if target is set, this is means remove mod from community
+  pub(in crate::activities) target: Option<Url>,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
+  #[serde(flatten)]
+  unparsed: Unparsed,
+}
+
+impl RemoveMod {
+  pub async fn send(
+    community: &Community,
+    removed_mod: &Person,
+    actor: &Person,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let id = generate_activity_id(RemoveType::Remove)?;
+    let remove = RemoveMod {
+      actor: actor.actor_id(),
+      to: PublicUrl::Public,
+      object: removed_mod.actor_id(),
+      target: Some(generate_moderators_url(&community.actor_id)?.into()),
+      id: id.clone(),
+      context: lemmy_context(),
+      cc: [community.actor_id()],
+      kind: RemoveType::Remove,
+      unparsed: Default::default(),
+    };
+
+    let activity = AnnouncableActivities::RemoveMod(remove);
+    let inboxes = vec![removed_mod.get_shared_inbox_or_inbox_url()];
+    send_to_community_new(activity, &id, actor, community, inboxes, context).await
+  }
+}
+
+#[async_trait::async_trait(?Send)]
+impl ActivityHandler for RemoveMod {
+  async fn verify(
+    &self,
+    context: &LemmyContext,
+    request_counter: &mut i32,
+  ) -> Result<(), LemmyError> {
+    verify_activity(self)?;
+    if let Some(target) = &self.target {
+      verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+      verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
+      verify_add_remove_moderator_target(target, self.cc[0].clone())?;
+    } else {
+      verify_delete_activity(
+        &self.object,
+        self,
+        &self.cc[0],
+        true,
+        context,
+        request_counter,
+      )
+      .await?;
+    }
+    Ok(())
+  }
+
+  async fn receive(
+    self,
+    context: &LemmyContext,
+    request_counter: &mut i32,
+  ) -> Result<(), LemmyError> {
+    if self.target.is_some() {
+      let community =
+        get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
+      let remove_mod =
+        get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
+
+      let form = CommunityModeratorForm {
+        community_id: community.id,
+        person_id: remove_mod.id,
+      };
+      blocking(context.pool(), move |conn| {
+        CommunityModerator::leave(conn, &form)
+      })
+      .await??;
+      // TODO: send websocket notification about removed mod
+      Ok(())
+    } else {
+      receive_remove_action(&self.actor, &self.object, None, context, request_counter).await
+    }
+  }
+}
index 514c90ccd046fb66a95aafce869ad0b9f3e605f6..0a9665af5e0e0805a2529509d46f28a0fad4f575 100644 (file)
@@ -1,31 +1,75 @@
 use crate::{
   activities::{
-    community::block_user::BlockUserFromCommunity,
+    community::{announce::AnnouncableActivities, block_user::BlockUserFromCommunity},
+    generate_activity_id,
     verify_activity,
     verify_mod_action,
     verify_person_in_community,
   },
+  activity_queue::send_to_community_new,
+  extensions::context::lemmy_context,
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
+  ActorType,
+};
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams::activity::kind::UndoType;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Bannable;
-use lemmy_db_schema::source::community::{CommunityPersonBan, CommunityPersonBanForm};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityPersonBan, CommunityPersonBanForm},
+  person::Person,
+};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoBlockUserFromCommunity {
+  actor: Url,
   to: PublicUrl,
   object: BlockUserFromCommunity,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
+}
+
+impl UndoBlockUserFromCommunity {
+  pub async fn send(
+    community: &Community,
+    target: &Person,
+    actor: &Person,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let block = BlockUserFromCommunity::new(community, target, actor)?;
+
+    let id = generate_activity_id(UndoType::Undo)?;
+    let undo = UndoBlockUserFromCommunity {
+      actor: actor.actor_id(),
+      to: PublicUrl::Public,
+      object: block,
+      cc: [community.actor_id()],
+      kind: UndoType::Undo,
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
+    };
+
+    let activity = AnnouncableActivities::UndoBlockUserFromCommunity(undo);
+    let inboxes = vec![target.get_shared_inbox_or_inbox_url()];
+    send_to_community_new(activity, &id, actor, community, inboxes, context).await
+  }
 }
 
 #[async_trait::async_trait(?Send)]
@@ -35,9 +79,9 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-    verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+    verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -64,8 +108,4 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index d38d722bfc6a8fc45f8c6bfeb6e4db14f766ff53..7539464dac706429f59b616aa40a90c15d3cbcca 100644 (file)
@@ -1,28 +1,74 @@
 use crate::{
-  activities::{verify_activity, verify_mod_action, verify_person_in_community},
-  objects::community::Group,
+  activities::{
+    community::announce::AnnouncableActivities,
+    generate_activity_id,
+    verify_activity,
+    verify_mod_action,
+    verify_person_in_community,
+  },
+  activity_queue::send_to_community_new,
+  extensions::context::lemmy_context,
+  objects::{community::Group, ToApub},
+  ActorType,
+};
+use activitystreams::{
+  activity::kind::UpdateType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams::activity::kind::UpdateType;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{ApubObject, Crud};
-use lemmy_db_schema::source::community::{Community, CommunityForm};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityForm},
+  person::Person,
+};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
 /// This activity is received from a remote community mod, and updates the description or other
 /// fields of a local community.
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UpdateCommunity {
+  actor: Url,
   to: PublicUrl,
+  // TODO: would be nice to use a separate struct here, which only contains the fields updated here
   object: Group,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UpdateType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
+}
+
+impl UpdateCommunity {
+  pub async fn send(
+    community: &Community,
+    actor: &Person,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let id = generate_activity_id(UpdateType::Update)?;
+    let update = UpdateCommunity {
+      actor: actor.actor_id(),
+      to: PublicUrl::Public,
+      object: community.to_apub(context.pool()).await?,
+      cc: [community.actor_id()],
+      kind: UpdateType::Update,
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
+    };
+
+    let activity = AnnouncableActivities::UpdateCommunity(Box::new(update));
+    send_to_community_new(activity, &id, actor, community, vec![], context).await
+  }
 }
 
 #[async_trait::async_trait(?Send)]
@@ -32,9 +78,9 @@ impl ActivityHandler for UpdateCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-    verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+    verify_mod_action(&self.actor, self.cc[0].clone(), context).await?;
     Ok(())
   }
 
@@ -76,8 +122,4 @@ impl ActivityHandler for UpdateCommunity {
     .await?;
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 093919b88e5ce68692afa554396e87dbd4756ea2..45567288255a3239fcb49be49f27b6c1eb6b2451 100644 (file)
@@ -15,10 +15,15 @@ use crate::{
   fetcher::person::get_or_fetch_and_upsert_person,
   ActorType,
 };
-use activitystreams::activity::kind::DeleteType;
+use activitystreams::{
+  activity::kind::DeleteType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use anyhow::anyhow;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{
   source::{comment::Comment_, community::Community_, post::Post_},
   Crud,
@@ -43,6 +48,7 @@ use lemmy_websocket::{
   LemmyContext,
   UserOperationCrud,
 };
+use serde::{Deserialize, Serialize};
 use url::Url;
 
 /// This is very confusing, because there are four distinct cases to handle:
@@ -53,19 +59,23 @@ use url::Url;
 ///
 /// TODO: we should probably change how community deletions work to simplify this. Probably by
 /// wrapping it in an announce just like other activities, instead of having the community send it.
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct Delete {
-  pub(in crate::activities::deletion) to: PublicUrl,
+  actor: Url,
+  to: PublicUrl,
   pub(in crate::activities::deletion) object: Url,
   pub(in crate::activities::deletion) cc: [Url; 1],
   #[serde(rename = "type")]
-  pub(in crate::activities::deletion) kind: DeleteType,
+  kind: DeleteType,
   /// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
   /// deleting their own content.
   pub(in crate::activities::deletion) summary: Option<String>,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  pub(in crate::activities::deletion) common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 #[async_trait::async_trait(?Send)]
@@ -75,11 +85,11 @@ impl ActivityHandler for Delete {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     verify_delete_activity(
       &self.object,
+      self,
       &self.cc[0],
-      &self.common,
       self.summary.is_some(),
       context,
       request_counter,
@@ -101,18 +111,11 @@ impl ActivityHandler for Delete {
       } else {
         Some(reason)
       };
-      receive_remove_action(
-        &self.common.actor,
-        &self.object,
-        reason,
-        context,
-        request_counter,
-      )
-      .await
+      receive_remove_action(&self.actor, &self.object, reason, context, request_counter).await
     } else {
       receive_delete_action(
         &self.object,
-        &self.common.actor,
+        &self.actor,
         WebsocketMessages {
           community: UserOperationCrud::DeleteCommunity,
           post: UserOperationCrud::DeletePost,
@@ -125,37 +128,39 @@ impl ActivityHandler for Delete {
       .await
     }
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
 
 impl Delete {
-  pub(in crate::activities::deletion) async fn send(
+  pub(in crate::activities::deletion) fn new(
     actor: &Person,
     community: &Community,
     object_id: Url,
     summary: Option<String>,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let id = generate_activity_id(DeleteType::Delete)?;
-    let delete = Delete {
+  ) -> Result<Delete, LemmyError> {
+    Ok(Delete {
+      actor: actor.actor_id(),
       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(),
-      },
-    };
+      id: generate_activity_id(DeleteType::Delete)?,
+      context: lemmy_context(),
+      unparsed: Default::default(),
+    })
+  }
+  pub(in crate::activities::deletion) async fn send(
+    actor: &Person,
+    community: &Community,
+    object_id: Url,
+    summary: Option<String>,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let delete = Delete::new(actor, community, object_id, summary)?;
+    let delete_id = delete.id.clone();
 
     let activity = AnnouncableActivities::Delete(delete);
-    send_to_community_new(activity, &id, actor, community, vec![], context).await
+    send_to_community_new(activity, &delete_id, actor, community, vec![], context).await
   }
 }
 
index cf73be097654ea98e675e3164535530d56efef29..350773f42b356fbf3658a2bfe8c1aee8550a06e6 100644 (file)
@@ -8,7 +8,7 @@ use crate::{
   ActorType,
 };
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields};
 use lemmy_db_queries::{
   source::{comment::Comment_, community::Community_, post::Post_},
   ApubObject,
@@ -98,8 +98,8 @@ impl DeletableObjects {
 
 pub(in crate::activities) async fn verify_delete_activity(
   object: &Url,
-  cc: &Url,
-  common: &ActivityCommonFields,
+  activity: &dyn ActivityFields,
+  community_id: &Url,
   is_mod_action: bool,
   context: &LemmyContext,
   request_counter: &mut i32,
@@ -110,16 +110,17 @@ pub(in crate::activities) async fn verify_delete_activity(
       if c.local {
         // can only do this check for local community, in remote case it would try to fetch the
         // deleted community (which fails)
-        verify_person_in_community(&common.actor, cc, context, request_counter).await?;
+        verify_person_in_community(activity.actor(), community_id, context, request_counter)
+          .await?;
       }
       // community deletion is always a mod (or admin) action
-      verify_mod_action(&common.actor, c.actor_id(), context).await?;
+      verify_mod_action(activity.actor(), c.actor_id(), context).await?;
     }
     DeletableObjects::Post(p) => {
       verify_delete_activity_post_or_comment(
-        cc,
-        common,
+        activity,
         &p.ap_id.into(),
+        community_id,
         is_mod_action,
         context,
         request_counter,
@@ -128,9 +129,9 @@ pub(in crate::activities) async fn verify_delete_activity(
     }
     DeletableObjects::Comment(c) => {
       verify_delete_activity_post_or_comment(
-        cc,
-        common,
+        activity,
         &c.ap_id.into(),
+        community_id,
         is_mod_action,
         context,
         request_counter,
@@ -142,19 +143,19 @@ pub(in crate::activities) async fn verify_delete_activity(
 }
 
 async fn verify_delete_activity_post_or_comment(
-  cc: &Url,
-  common: &ActivityCommonFields,
+  activity: &dyn ActivityFields,
   object_id: &Url,
+  community_id: &Url,
   is_mod_action: bool,
   context: &LemmyContext,
   request_counter: &mut i32,
 ) -> Result<(), LemmyError> {
-  verify_person_in_community(&common.actor, cc, context, request_counter).await?;
+  verify_person_in_community(activity.actor(), community_id, context, request_counter).await?;
   if is_mod_action {
-    verify_mod_action(&common.actor, cc.clone(), context).await?;
+    verify_mod_action(activity.actor(), community_id.clone(), context).await?;
   } else {
     // domain of post ap_id and post.creator ap_id are identical, so we just check the former
-    verify_domains_match(&common.actor, object_id)?;
+    verify_domains_match(activity.actor(), object_id)?;
   }
   Ok(())
 }
index 0114acac81aa2c387190c8fcf3b8ea31e58dc42c..35369d4413d4324aca5cbe4b7ad770a3e6c87fb3 100644 (file)
@@ -15,10 +15,15 @@ use crate::{
   extensions::context::lemmy_context,
   ActorType,
 };
-use activitystreams::activity::kind::{DeleteType, UndoType};
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use anyhow::anyhow;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::source::{comment::Comment_, community::Community_, post::Post_};
 use lemmy_db_schema::source::{comment::Comment, community::Community, person::Person, post::Post};
 use lemmy_utils::LemmyError;
@@ -27,18 +32,23 @@ use lemmy_websocket::{
   LemmyContext,
   UserOperationCrud,
 };
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoDelete {
+  actor: Url,
   to: PublicUrl,
   object: Delete,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 #[async_trait::async_trait(?Send)]
@@ -48,12 +58,12 @@ impl ActivityHandler for UndoDelete {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     self.object.verify(context, request_counter).await?;
     verify_delete_activity(
       &self.object.object,
+      self,
       &self.cc[0],
-      &self.common,
       self.object.summary.is_some(),
       context,
       request_counter,
@@ -72,7 +82,7 @@ impl ActivityHandler for UndoDelete {
     } else {
       receive_delete_action(
         &self.object.object,
-        &self.common.actor,
+        &self.actor,
         WebsocketMessages {
           community: UserOperationCrud::EditCommunity,
           post: UserOperationCrud::EditPost,
@@ -85,10 +95,6 @@ impl ActivityHandler for UndoDelete {
       .await
     }
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
 
 impl UndoDelete {
@@ -99,32 +105,18 @@ impl UndoDelete {
     summary: Option<String>,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
-    let delete = Delete {
-      to: PublicUrl::Public,
-      object: object_id,
-      cc: [community.actor_id()],
-      kind: DeleteType::Delete,
-      summary,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: generate_activity_id(DeleteType::Delete)?,
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
-    };
+    let object = Delete::new(actor, community, object_id, summary)?;
 
     let id = generate_activity_id(UndoType::Undo)?;
     let undo = UndoDelete {
+      actor: actor.actor_id(),
       to: PublicUrl::Public,
-      object: delete,
+      object,
       cc: [community.actor_id()],
       kind: UndoType::Undo,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
 
     let activity = AnnouncableActivities::UndoDelete(undo);
index bc6895899d1437f67b5bd074481d60de413acb4c..c76263cc0f93ccc29e229e4c9431ce68eea153f6 100644 (file)
@@ -10,9 +10,14 @@ use crate::{
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
   ActorType,
 };
-use activitystreams::activity::kind::AcceptType;
+use activitystreams::{
+  activity::kind::AcceptType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{ApubObject, Followable};
 use lemmy_db_schema::source::{
   community::{Community, CommunityFollower},
@@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct AcceptFollowCommunity {
+  actor: Url,
   to: Url,
   object: FollowCommunity,
   #[serde(rename = "type")]
   kind: AcceptType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl AcceptFollowCommunity {
@@ -40,26 +50,23 @@ impl AcceptFollowCommunity {
       Community::read_from_apub_id(conn, &community_id.into())
     })
     .await??;
-    let person_id = follow.common.actor.clone();
+    let person_id = follow.actor().clone();
     let person = blocking(context.pool(), move |conn| {
       Person::read_from_apub_id(conn, &person_id.into())
     })
     .await??;
 
-    let id = generate_activity_id(AcceptType::Accept)?;
     let accept = AcceptFollowCommunity {
+      actor: community.actor_id(),
       to: person.actor_id(),
       object: follow,
       kind: AcceptType::Accept,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: community.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: generate_activity_id(AcceptType::Accept)?,
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
     let inbox = vec![person.inbox_url.into()];
-    send_activity_new(context, &accept, &id, &community, inbox, true).await
+    send_activity_new(context, &accept, &accept.id, &community, inbox, true).await
   }
 }
 /// Handle accepted follows
@@ -70,10 +77,10 @@ impl ActivityHandler for AcceptFollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_urls_match(&self.to, &self.object.common.actor)?;
-    verify_urls_match(&self.common.actor, &self.object.to)?;
-    verify_community(&self.common.actor, context, request_counter).await?;
+    verify_activity(self)?;
+    verify_urls_match(&self.to, self.object.actor())?;
+    verify_urls_match(&self.actor, &self.object.to)?;
+    verify_community(&self.actor, context, request_counter).await?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -83,8 +90,7 @@ impl ActivityHandler for AcceptFollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_community(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_community(&self.actor, context, request_counter).await?;
     let to = get_or_fetch_and_upsert_person(&self.to, context, request_counter).await?;
     // This will throw an error if no follow was requested
     blocking(context.pool(), move |conn| {
@@ -94,8 +100,4 @@ impl ActivityHandler for AcceptFollowCommunity {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index aa96fb39f453f5e91642512088af39e58c2f9287..e6ca747a09dc96b9913f6d6c57185a234d9dc86e 100644 (file)
@@ -10,9 +10,14 @@ use crate::{
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
   ActorType,
 };
-use activitystreams::activity::kind::FollowType;
+use activitystreams::{
+  activity::kind::FollowType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Followable;
 use lemmy_db_schema::source::{
   community::{Community, CommunityFollower, CommunityFollowerForm},
@@ -20,20 +25,39 @@ use lemmy_db_schema::source::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct FollowCommunity {
+  actor: Url,
   pub(in crate::activities::following) to: Url,
   pub(in crate::activities::following) object: Url,
   #[serde(rename = "type")]
-  pub(in crate::activities::following) kind: FollowType,
+  kind: FollowType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  pub(in crate::activities::following) common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl FollowCommunity {
+  pub(in crate::activities::following) fn new(
+    actor: &Person,
+    community: &Community,
+  ) -> Result<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(
     actor: &Person,
     community: &Community,
@@ -49,20 +73,9 @@ impl FollowCommunity {
     })
     .await?;
 
-    let id = generate_activity_id(FollowType::Follow)?;
-    let follow = FollowCommunity {
-      to: community.actor_id(),
-      object: community.actor_id(),
-      kind: FollowType::Follow,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
-    };
+    let follow = FollowCommunity::new(actor, community)?;
     let inbox = vec![community.inbox_url.clone().into()];
-    send_activity_new(context, &follow, &id, actor, inbox, true).await
+    send_activity_new(context, &follow, &follow.id, actor, inbox, true).await
   }
 }
 
@@ -73,9 +86,9 @@ impl ActivityHandler for FollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     verify_urls_match(&self.to, &self.object)?;
-    verify_person(&self.common.actor, context, request_counter).await?;
+    verify_person(&self.actor, context, request_counter).await?;
     Ok(())
   }
 
@@ -84,8 +97,7 @@ impl ActivityHandler for FollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
     let community =
       get_or_fetch_and_upsert_community(&self.object, context, request_counter).await?;
     let community_follower_form = CommunityFollowerForm {
@@ -102,8 +114,4 @@ impl ActivityHandler for FollowCommunity {
 
     AcceptFollowCommunity::send(self, context).await
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 7fbc7be5b33ab4b6d9b458b8b6d65de415d1d7d6..092036bb4932a941f472b271707043e62eb152fd 100644 (file)
@@ -10,9 +10,14 @@ use crate::{
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
   ActorType,
 };
-use activitystreams::activity::kind::{FollowType, UndoType};
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_urls_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Followable;
 use lemmy_db_schema::source::{
   community::{Community, CommunityFollower, CommunityFollowerForm},
@@ -20,17 +25,22 @@ use lemmy_db_schema::source::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoFollowCommunity {
+  actor: Url,
   to: Url,
   object: FollowCommunity,
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl UndoFollowCommunity {
@@ -39,30 +49,18 @@ impl UndoFollowCommunity {
     community: &Community,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
-    let id = generate_activity_id(UndoType::Undo)?;
+    let object = FollowCommunity::new(actor, community)?;
     let undo = UndoFollowCommunity {
+      actor: actor.actor_id(),
       to: community.actor_id(),
-      object: FollowCommunity {
-        to: community.actor_id(),
-        object: community.actor_id(),
-        kind: FollowType::Follow,
-        common: ActivityCommonFields {
-          context: lemmy_context(),
-          id: generate_activity_id(FollowType::Follow)?,
-          actor: actor.actor_id(),
-          unparsed: Default::default(),
-        },
-      },
+      object,
       kind: UndoType::Undo,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: generate_activity_id(UndoType::Undo)?,
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
     let inbox = vec![community.get_shared_inbox_or_inbox_url()];
-    send_activity_new(context, &undo, &id, actor, inbox, true).await
+    send_activity_new(context, &undo, &undo.id, actor, inbox, true).await
   }
 }
 
@@ -73,10 +71,10 @@ impl ActivityHandler for UndoFollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     verify_urls_match(&self.to, &self.object.object)?;
-    verify_urls_match(&self.common.actor, &self.object.common.actor)?;
-    verify_person(&self.common.actor, context, request_counter).await?;
+    verify_urls_match(&self.actor, self.object.actor())?;
+    verify_person(&self.actor, context, request_counter).await?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -86,8 +84,7 @@ impl ActivityHandler for UndoFollowCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
     let community = get_or_fetch_and_upsert_community(&self.to, context, request_counter).await?;
 
     let community_follower_form = CommunityFollowerForm {
@@ -103,8 +100,4 @@ impl ActivityHandler for UndoFollowCommunity {
     .await?;
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index afbad6c8b9cad907717a5f1a2077dd886e0a011d..a846a0e7052cfc529fc9ff9e2a41f07b25f058b2 100644 (file)
@@ -6,7 +6,7 @@ use crate::{
 };
 use anyhow::anyhow;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields};
 use lemmy_db_queries::ApubObject;
 use lemmy_db_schema::{
   source::{community::Community, person::Person},
@@ -26,8 +26,8 @@ pub mod deletion;
 pub mod following;
 pub mod post;
 pub mod private_message;
-pub mod removal;
 pub mod send;
+pub mod undo_remove;
 pub mod voting;
 
 #[derive(Clone, Debug, ToString, Deserialize, Serialize)]
@@ -90,9 +90,9 @@ async fn verify_community(
   Ok(())
 }
 
-fn verify_activity(common: &ActivityCommonFields) -> Result<(), LemmyError> {
-  check_is_apub_id_valid(&common.actor, false)?;
-  verify_domains_match(common.id_unchecked(), &common.actor)?;
+fn verify_activity(activity: &dyn ActivityFields) -> Result<(), LemmyError> {
+  check_is_apub_id_valid(activity.actor(), false)?;
+  verify_domains_match(activity.id_unchecked(), activity.actor())?;
   Ok(())
 }
 
index 1a9c776896a61c73a67df7e97c6f9d5450bca19c..c1b0703db534a99d68d4e44486a9b4f4dd3bc424 100644 (file)
@@ -14,31 +14,37 @@ use crate::{
   objects::{post::Page, FromApub, ToApub},
   ActorType,
 };
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
 use anyhow::anyhow;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   values::PublicUrl,
   verify_domains_match,
   verify_urls_match,
-  ActivityCommonFields,
+  ActivityFields,
   ActivityHandler,
 };
 use lemmy_db_queries::Crud;
 use lemmy_db_schema::source::{community::Community, person::Person, post::Post};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct CreateOrUpdatePost {
+  actor: Url,
   to: PublicUrl,
   object: Page,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: CreateOrUpdateType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl CreateOrUpdatePost {
@@ -56,16 +62,14 @@ impl CreateOrUpdatePost {
 
     let id = generate_activity_id(kind.clone())?;
     let create_or_update = CreateOrUpdatePost {
+      actor: actor.actor_id(),
       to: PublicUrl::Public,
       object: post.to_apub(context.pool()).await?,
       cc: [community.actor_id()],
       kind,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
 
     let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
@@ -80,14 +84,14 @@ impl ActivityHandler for CreateOrUpdatePost {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     let community = extract_community(&self.cc, context, request_counter).await?;
     let community_id = community.actor_id();
-    verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
+    verify_person_in_community(&self.actor, &community_id, context, request_counter).await?;
     match self.kind {
       CreateOrUpdateType::Create => {
-        verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
-        verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
+        verify_domains_match(&self.actor, self.object.id_unchecked())?;
+        verify_urls_match(&self.actor, &self.object.attributed_to)?;
         // Check that the post isnt locked or stickied, as that isnt possible for newly created posts.
         // However, when fetching a remote post we generate a new create activity with the current
         // locked/stickied value, so this check may fail. So only check if its a local community,
@@ -101,10 +105,10 @@ impl ActivityHandler for CreateOrUpdatePost {
       CreateOrUpdateType::Update => {
         let is_mod_action = self.object.is_mod_action(context.pool()).await?;
         if is_mod_action {
-          verify_mod_action(&self.common.actor, community_id, context).await?;
+          verify_mod_action(&self.actor, community_id, context).await?;
         } else {
-          verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
-          verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
+          verify_domains_match(&self.actor, self.object.id_unchecked())?;
+          verify_urls_match(&self.actor, &self.object.attributed_to)?;
         }
       }
     }
@@ -117,8 +121,7 @@ impl ActivityHandler for CreateOrUpdatePost {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
     let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
 
     let notif_type = match self.kind {
@@ -128,8 +131,4 @@ impl ActivityHandler for CreateOrUpdatePost {
     send_post_ws_message(post.id, notif_type, None, None, context).await?;
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index cd3c657444a32bd6fb9c94b11da53207addb5ac8..98a26d806d2bac6cb2514df0e307dca282633c1a 100644 (file)
@@ -5,23 +5,30 @@ use crate::{
   objects::{private_message::Note, FromApub, ToApub},
   ActorType,
 };
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Crud;
 use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct CreateOrUpdatePrivateMessage {
+  #[serde(rename = "@context")]
+  pub context: OneOrMany<AnyBase>,
+  id: Url,
+  actor: Url,
   to: Url,
+  cc: [Url; 0],
   object: Note,
   #[serde(rename = "type")]
   kind: CreateOrUpdateType,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  pub unparsed: Unparsed,
 }
 
 impl CreateOrUpdatePrivateMessage {
@@ -37,15 +44,14 @@ impl CreateOrUpdatePrivateMessage {
 
     let id = generate_activity_id(kind.clone())?;
     let create_or_update = CreateOrUpdatePrivateMessage {
+      context: lemmy_context(),
+      id: id.clone(),
+      actor: actor.actor_id(),
       to: recipient.actor_id(),
+      cc: [],
       object: private_message.to_apub(context.pool()).await?,
       kind,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      unparsed: Default::default(),
     };
     let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
     send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
@@ -58,9 +64,9 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person(&self.common.actor, context, request_counter).await?;
-    verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
+    verify_activity(self)?;
+    verify_person(&self.actor, context, request_counter).await?;
+    verify_domains_match(&self.actor, self.object.id_unchecked())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -71,7 +77,7 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
     let private_message =
-      PrivateMessage::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
+      PrivateMessage::from_apub(&self.object, context, &self.actor, request_counter).await?;
 
     let notif_type = match self.kind {
       CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
@@ -81,8 +87,4 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index e6c03093b057c95dab9f8af18304cb6771b030f5..47e1a71a158169a1a637d3472e35cff30141017a 100644 (file)
@@ -4,50 +4,64 @@ use crate::{
   extensions::context::lemmy_context,
   ActorType,
 };
-use activitystreams::activity::kind::DeleteType;
+use activitystreams::{
+  activity::kind::DeleteType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{verify_domains_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
 use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct DeletePrivateMessage {
-  pub(in crate::activities::private_message) to: Url,
+  actor: Url,
+  to: Url,
   pub(in crate::activities::private_message) object: Url,
   #[serde(rename = "type")]
-  pub(in crate::activities::private_message) kind: DeleteType,
+  kind: DeleteType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  pub(in crate::activities::private_message) common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl DeletePrivateMessage {
+  pub(in crate::activities::private_message) fn new(
+    actor: &Person,
+    pm: &PrivateMessage,
+  ) -> Result<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(
     actor: &Person,
     pm: &PrivateMessage,
     context: &LemmyContext,
   ) -> Result<(), LemmyError> {
+    let delete = DeletePrivateMessage::new(actor, pm)?;
+    let delete_id = delete.id.clone();
+
     let recipient_id = pm.recipient_id;
     let recipient =
       blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
-
-    let id = generate_activity_id(DeleteType::Delete)?;
-    let delete = DeletePrivateMessage {
-      to: actor.actor_id(),
-      object: pm.ap_id.clone().into(),
-      kind: DeleteType::Delete,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
-    };
     let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
-    send_activity_new(context, &delete, &id, actor, inbox, true).await
+    send_activity_new(context, &delete, &delete_id, actor, inbox, true).await
   }
 }
 
@@ -58,9 +72,9 @@ impl ActivityHandler for DeletePrivateMessage {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person(&self.common.actor, context, request_counter).await?;
-    verify_domains_match(&self.common.actor, &self.object)?;
+    verify_activity(self)?;
+    verify_person(&self.actor, context, request_counter).await?;
+    verify_domains_match(&self.actor, &self.object)?;
     Ok(())
   }
 
@@ -89,8 +103,4 @@ impl ActivityHandler for DeletePrivateMessage {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 4cd2a139d0ec16c09caf385cab00a316e137353c..911a17c7941789a4a073d2d68aa35da9492f38d0 100644 (file)
@@ -9,29 +9,34 @@ use crate::{
   extensions::context::lemmy_context,
   ActorType,
 };
-use activitystreams::activity::kind::{DeleteType, UndoType};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{
-  verify_domains_match,
-  verify_urls_match,
-  ActivityCommonFields,
-  ActivityHandler,
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
+use lemmy_api_common::blocking;
+use lemmy_apub_lib::{verify_domains_match, verify_urls_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject, Crud};
 use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoDeletePrivateMessage {
+  actor: Url,
   to: Url,
   object: DeletePrivateMessage,
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl UndoDeletePrivateMessage {
@@ -44,29 +49,16 @@ impl UndoDeletePrivateMessage {
     let recipient =
       blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
 
-    let object = DeletePrivateMessage {
-      to: recipient.actor_id(),
-      object: pm.ap_id.clone().into(),
-      kind: DeleteType::Delete,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: generate_activity_id(DeleteType::Delete)?,
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
-    };
-
+    let object = DeletePrivateMessage::new(actor, pm)?;
     let id = generate_activity_id(UndoType::Undo)?;
     let undo = UndoDeletePrivateMessage {
+      actor: actor.actor_id(),
       to: recipient.actor_id(),
       object,
       kind: UndoType::Undo,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
     let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
     send_activity_new(context, &undo, &id, actor, inbox, true).await
@@ -80,10 +72,10 @@ impl ActivityHandler for UndoDeletePrivateMessage {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person(&self.common.actor, context, request_counter).await?;
-    verify_urls_match(&self.common.actor, &self.object.common.actor)?;
-    verify_domains_match(&self.common.actor, &self.object.object)?;
+    verify_activity(self)?;
+    verify_person(&self.actor, context, request_counter).await?;
+    verify_urls_match(&self.actor, self.object.actor())?;
+    verify_domains_match(&self.actor, &self.object.object)?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -114,8 +106,4 @@ impl ActivityHandler for UndoDeletePrivateMessage {
 
     Ok(())
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
diff --git a/crates/apub/src/activities/removal/mod.rs b/crates/apub/src/activities/removal/mod.rs
deleted file mode 100644 (file)
index 01c031d..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod remove;
-pub mod undo_remove;
diff --git a/crates/apub/src/activities/removal/remove.rs b/crates/apub/src/activities/removal/remove.rs
deleted file mode 100644 (file)
index 1bd6e0a..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-use crate::{
-  activities::{
-    deletion::{delete::receive_remove_action, verify_delete_activity},
-    verify_activity,
-    verify_add_remove_moderator_target,
-    verify_mod_action,
-    verify_person_in_community,
-  },
-  fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
-  CommunityType,
-};
-use activitystreams::{activity::kind::RemoveType, base::AnyBase};
-use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
-use lemmy_db_queries::Joinable;
-use lemmy_db_schema::source::community::{CommunityModerator, CommunityModeratorForm};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use url::Url;
-
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct RemoveMod {
-  to: PublicUrl,
-  pub(in crate::activities::removal) object: Url,
-  cc: [Url; 1],
-  #[serde(rename = "type")]
-  kind: RemoveType,
-  // if target is set, this is means remove mod from community
-  pub(in crate::activities::removal) target: Option<Url>,
-  #[serde(flatten)]
-  common: ActivityCommonFields,
-}
-
-#[async_trait::async_trait(?Send)]
-impl ActivityHandler for RemoveMod {
-  async fn verify(
-    &self,
-    context: &LemmyContext,
-    request_counter: &mut i32,
-  ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    if let Some(target) = &self.target {
-      verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-      verify_mod_action(&self.common.actor, self.cc[0].clone(), context).await?;
-      verify_add_remove_moderator_target(target, self.cc[0].clone())?;
-    } else {
-      verify_delete_activity(
-        &self.object,
-        &self.cc[0],
-        self.common(),
-        true,
-        context,
-        request_counter,
-      )
-      .await?;
-    }
-    Ok(())
-  }
-
-  async fn receive(
-    self,
-    context: &LemmyContext,
-    request_counter: &mut i32,
-  ) -> Result<(), LemmyError> {
-    if self.target.is_some() {
-      let community =
-        get_or_fetch_and_upsert_community(&self.cc[0], context, request_counter).await?;
-      let remove_mod =
-        get_or_fetch_and_upsert_person(&self.object, context, request_counter).await?;
-
-      let form = CommunityModeratorForm {
-        community_id: community.id,
-        person_id: remove_mod.id,
-      };
-      blocking(context.pool(), move |conn| {
-        CommunityModerator::leave(conn, &form)
-      })
-      .await??;
-      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
-      community
-        .send_announce(anybase, Some(self.object.clone()), context)
-        .await?;
-      // TODO: send websocket notification about removed mod
-      Ok(())
-    } else {
-      receive_remove_action(
-        &self.common.actor,
-        &self.object,
-        None,
-        context,
-        request_counter,
-      )
-      .await
-    }
-  }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
-}
index a103f95c6951322724a18b31d4f90b38d59ad0b7..29aa9dba8c62c43ae2c15115d20dfae85ddc4206 100644 (file)
@@ -1,38 +1,10 @@
-use crate::{
-  activities::generate_activity_id,
-  activity_queue::{send_to_community, send_to_community_followers},
-  check_is_apub_id_valid,
-  extensions::context::lemmy_context,
-  fetcher::get_or_fetch_and_upsert_actor,
-  generate_moderators_url,
-  insert_activity,
-  objects::ToApub,
-  ActorType,
-  CommunityType,
-};
-use activitystreams::{
-  activity::{
-    kind::{AddType, AnnounceType, BlockType, RemoveType, UndoType, UpdateType},
-    Add,
-    Announce,
-    Block,
-    OptTargetRefExt,
-    Remove,
-    Undo,
-    Update,
-  },
-  base::{AnyBase, BaseExt, ExtendsExt},
-  object::ObjectExt,
-  public,
-};
-use anyhow::Context;
+use crate::{check_is_apub_id_valid, ActorType, CommunityType};
 use itertools::Itertools;
 use lemmy_api_common::blocking;
 use lemmy_db_queries::DbPool;
-use lemmy_db_schema::source::{community::Community, person::Person};
+use lemmy_db_schema::source::community::Community;
 use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
-use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
-use lemmy_websocket::LemmyContext;
+use lemmy_utils::LemmyError;
 use url::Url;
 
 impl ActorType for Community {
@@ -67,71 +39,6 @@ impl CommunityType for Community {
     self.followers_url.clone().into()
   }
 
-  /// If a remote community is updated by a local mod, send the updated info to the community's
-  /// instance.
-  async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError> {
-    if self.local {
-      // Do nothing, other instances will automatically refetch the community
-    } else {
-      let mut update = Update::new(
-        mod_.actor_id(),
-        AnyBase::from_arbitrary_json(self.to_apub(context.pool()).await?)?,
-      );
-      update
-        .set_many_contexts(lemmy_context())
-        .set_id(generate_activity_id(UpdateType::Update)?)
-        .set_to(public())
-        .set_many_ccs(vec![self.actor_id()]);
-      send_to_community(update, &mod_, self, None, context).await?;
-    }
-    Ok(())
-  }
-
-  /// Wraps an activity sent to the community in an announce, and then sends the announce to all
-  /// community followers.
-  ///
-  /// If we are announcing a local activity, it hasn't been stored in the database yet, and we need
-  /// to do it here, so that it can be fetched by ID. Remote activities are inserted into DB in the
-  /// inbox.
-  ///
-  /// If the `object` of the announced activity is an actor, the actor ID needs to be passed as
-  /// `object_actor`, so that the announce can be delivered to that user.
-  async fn send_announce(
-    &self,
-    activity: AnyBase,
-    object_actor: Option<Url>,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let inner_id = activity.id().context(location_info!())?;
-    if inner_id.domain() == Some(&Settings::get().get_hostname_without_port()?) {
-      insert_activity(inner_id, activity.clone(), true, false, context.pool()).await?;
-    }
-
-    let mut ccs = vec![self.followers_url()];
-    let mut object_actor_inbox: Option<Url> = None;
-    if let Some(actor_id) = object_actor {
-      // Ignore errors, maybe its not actually an actor
-      // TODO: should pass the actual request counter in, but that seems complicated
-      let actor = get_or_fetch_and_upsert_actor(&actor_id, context, &mut 0)
-        .await
-        .ok();
-      if let Some(actor) = actor {
-        ccs.push(actor_id);
-        object_actor_inbox = Some(actor.get_shared_inbox_or_inbox_url());
-      }
-    }
-    let mut announce = Announce::new(self.actor_id(), activity);
-    announce
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(AnnounceType::Announce)?)
-      .set_to(public())
-      .set_many_ccs(ccs);
-
-    send_to_community_followers(announce, self, object_actor_inbox, context).await?;
-
-    Ok(())
-  }
-
   /// For a given community, returns the inboxes of all followers.
   async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
     let id = self.id;
@@ -152,82 +59,4 @@ impl CommunityType for Community {
 
     Ok(inboxes)
   }
-
-  async fn send_add_mod(
-    &self,
-    actor: &Person,
-    added_mod: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let mut add = Add::new(actor.actor_id(), added_mod.actor_id());
-    add
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(AddType::Add)?)
-      .set_to(public())
-      .set_many_ccs(vec![self.actor_id()])
-      .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
-
-    send_to_community(add, actor, self, Some(added_mod.actor_id()), context).await?;
-    Ok(())
-  }
-
-  async fn send_remove_mod(
-    &self,
-    actor: &Person,
-    removed_mod: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let mut remove = Remove::new(actor.actor_id(), removed_mod.actor_id());
-    remove
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(RemoveType::Remove)?)
-      .set_to(public())
-      .set_many_ccs(vec![self.actor_id()])
-      .set_target(generate_moderators_url(&self.actor_id)?.into_inner());
-
-    send_to_community(remove, actor, self, Some(removed_mod.actor_id()), context).await?;
-    Ok(())
-  }
-
-  async fn send_block_user(
-    &self,
-    actor: &Person,
-    blocked_user: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let mut block = Block::new(actor.actor_id(), blocked_user.actor_id());
-    block
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(BlockType::Block)?)
-      .set_to(public())
-      .set_many_ccs(vec![self.actor_id()]);
-
-    send_to_community(block, actor, self, Some(blocked_user.actor_id()), context).await?;
-    Ok(())
-  }
-
-  async fn send_undo_block_user(
-    &self,
-    actor: &Person,
-    unblocked_user: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let mut block = Block::new(actor.actor_id(), unblocked_user.actor_id());
-    block
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(BlockType::Block)?)
-      .set_to(public())
-      .set_many_ccs(vec![self.actor_id()]);
-
-    // Undo that fake activity
-    let mut undo = Undo::new(actor.actor_id(), block.into_any_base()?);
-    undo
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(UndoType::Undo)?)
-      .set_to(public())
-      .set_many_ccs(vec![self.actor_id()]);
-
-    send_to_community(undo, actor, self, Some(unblocked_user.actor_id()), context).await?;
-    Ok(())
-  }
 }
similarity index 69%
rename from crates/apub/src/activities/removal/undo_remove.rs
rename to crates/apub/src/activities/undo_remove.rs
index e2c7ef76f34a64dcecd214c64a9d739b412ba700..03b2f888e16b71728227f90837088d5affba2e49 100644 (file)
@@ -1,25 +1,35 @@
 use crate::activities::{
+  community::remove_mod::RemoveMod,
   deletion::{undo_delete::UndoDelete, verify_delete_activity},
-  removal::remove::RemoveMod,
   verify_activity,
 };
-use activitystreams::activity::kind::UndoType;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoRemovePostCommentOrCommunity {
+  actor: Url,
   to: PublicUrl,
   // Note, there is no such thing as Undo/Remove/Mod, so we ignore that
   object: RemoveMod,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 #[async_trait::async_trait(?Send)]
@@ -29,13 +39,13 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
+    verify_activity(self)?;
     self.object.verify(context, request_counter).await?;
 
     verify_delete_activity(
       &self.object.object,
+      self,
       &self.cc[0],
-      self.common(),
       true,
       context,
       request_counter,
@@ -51,8 +61,4 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
   ) -> Result<(), LemmyError> {
     UndoDelete::receive_undo_remove_action(&self.object.object, context).await
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 5b5feac9d7246c6391f5056efa5463a24145773f..6e18b3cb6be1831c145bb61e68a395f35a547f43 100644 (file)
@@ -19,9 +19,14 @@ use crate::{
   ActorType,
   PostOrComment,
 };
-use activitystreams::activity::kind::UndoType;
+use activitystreams::{
+  activity::kind::UndoType,
+  base::AnyBase,
+  primitives::OneOrMany,
+  unparsed::Unparsed,
+};
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Crud;
 use lemmy_db_schema::{
   source::{community::Community, person::Person},
@@ -29,19 +34,24 @@ use lemmy_db_schema::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
 use std::ops::Deref;
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct UndoVote {
+  actor: Url,
   to: PublicUrl,
   object: Vote,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UndoType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl UndoVote {
@@ -56,30 +66,18 @@ impl UndoVote {
       Community::read(conn, community_id)
     })
     .await??;
-    let id = generate_activity_id(UndoType::Undo)?;
 
+    let object = Vote::new(object, actor, &community, kind.clone())?;
+    let id = generate_activity_id(UndoType::Undo)?;
     let undo_vote = UndoVote {
+      actor: actor.actor_id(),
       to: PublicUrl::Public,
-      object: Vote {
-        to: PublicUrl::Public,
-        object: object.ap_id(),
-        cc: [community.actor_id()],
-        kind: kind.clone(),
-        common: ActivityCommonFields {
-          context: lemmy_context(),
-          id: generate_activity_id(kind)?,
-          actor: actor.actor_id(),
-          unparsed: Default::default(),
-        },
-      },
+      object,
       cc: [community.actor_id()],
       kind: UndoType::Undo,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
+      id: id.clone(),
+      context: lemmy_context(),
+      unparsed: Default::default(),
     };
     let activity = AnnouncableActivities::UndoVote(undo_vote);
     send_to_community_new(activity, &id, actor, &community, vec![], context).await
@@ -93,9 +91,9 @@ impl ActivityHandler for UndoVote {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
-    verify_urls_match(&self.common.actor, &self.object.common().actor)?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
+    verify_urls_match(&self.actor, self.object.actor())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
@@ -105,8 +103,7 @@ impl ActivityHandler for UndoVote {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
     let object =
       get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter)
         .await?;
@@ -115,8 +112,4 @@ impl ActivityHandler for UndoVote {
       PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await,
     }
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 4183c2ad0a6005b0d80313ba38fc95722273fbca..cd7d04c54906de2d24f49575b1e0a9468b0c608c 100644 (file)
@@ -15,9 +15,10 @@ use crate::{
   ActorType,
   PostOrComment,
 };
+use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
 use anyhow::anyhow;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler};
+use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
 use lemmy_db_queries::Crud;
 use lemmy_db_schema::{
   source::{community::Community, person::Person},
@@ -57,19 +58,41 @@ impl From<&VoteType> for i16 {
   }
 }
 
-#[derive(Clone, Debug, Deserialize, Serialize)]
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
 #[serde(rename_all = "camelCase")]
 pub struct Vote {
-  pub(in crate::activities::voting) to: PublicUrl,
+  actor: Url,
+  to: PublicUrl,
   pub(in crate::activities::voting) object: Url,
-  pub(in crate::activities::voting) cc: [Url; 1],
+  cc: [Url; 1],
   #[serde(rename = "type")]
   pub(in crate::activities::voting) kind: VoteType,
+  id: Url,
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
   #[serde(flatten)]
-  pub(in crate::activities::voting) common: ActivityCommonFields,
+  unparsed: Unparsed,
 }
 
 impl Vote {
+  pub(in crate::activities::voting) fn new(
+    object: &PostOrComment,
+    actor: &Person,
+    community: &Community,
+    kind: VoteType,
+  ) -> Result<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(
     object: &PostOrComment,
     actor: &Person,
@@ -81,22 +104,11 @@ impl Vote {
       Community::read(conn, community_id)
     })
     .await??;
-    let id = generate_activity_id(kind.clone())?;
+    let vote = Vote::new(object, actor, &community, kind)?;
+    let vote_id = vote.id.clone();
 
-    let vote = Vote {
-      to: PublicUrl::Public,
-      object: object.ap_id(),
-      cc: [community.actor_id()],
-      kind,
-      common: ActivityCommonFields {
-        context: lemmy_context(),
-        id: id.clone(),
-        actor: actor.actor_id(),
-        unparsed: Default::default(),
-      },
-    };
     let activity = AnnouncableActivities::Vote(vote);
-    send_to_community_new(activity, &id, actor, &community, vec![], context).await
+    send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await
   }
 }
 
@@ -107,8 +119,8 @@ impl ActivityHandler for Vote {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_activity(self.common())?;
-    verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?;
+    verify_activity(self)?;
+    verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
     Ok(())
   }
 
@@ -117,8 +129,7 @@ impl ActivityHandler for Vote {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let actor =
-      get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
+    let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
     let object =
       get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
     match object {
@@ -126,8 +137,4 @@ impl ActivityHandler for Vote {
       PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
     }
   }
-
-  fn common(&self) -> &ActivityCommonFields {
-    &self.common
-  }
 }
index 48cccc612d9862ff4b40a556ba9c42483f7637e3..01959c7d72ba50f2faf4bf68efe54ebcf8de1427 100644 (file)
@@ -1,16 +1,10 @@
 use crate::{
   activities::community::announce::{AnnouncableActivities, AnnounceActivity},
-  check_is_apub_id_valid,
   extensions::signatures::sign_and_send,
   insert_activity,
   ActorType,
-  CommunityType,
   APUB_JSON_CONTENT_TYPE,
 };
-use activitystreams::{
-  base::{BaseExt, Extends, ExtendsExt},
-  object::AsObject,
-};
 use anyhow::{anyhow, Context, Error};
 use background_jobs::{
   create_server,
@@ -21,95 +15,15 @@ use background_jobs::{
   QueueHandle,
   WorkerConfig,
 };
-use itertools::Itertools;
-use lemmy_db_schema::source::{community::Community, person::Person};
+use lemmy_db_schema::source::community::Community;
 use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
 use lemmy_websocket::LemmyContext;
-use log::{debug, info, warn};
+use log::{info, warn};
 use reqwest::Client;
 use serde::{Deserialize, Serialize};
 use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
 use url::Url;
 
-/// From a local community, send activity to all remote followers.
-///
-/// * `activity` the apub activity to send
-/// * `community` the sending community
-/// * `extra_inbox` actor inbox which should receive the activity, in addition to followers
-pub(crate) async fn send_to_community_followers<T, Kind>(
-  activity: T,
-  community: &Community,
-  extra_inbox: Option<Url>,
-  context: &LemmyContext,
-) -> Result<(), LemmyError>
-where
-  T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
-  Kind: Serialize,
-  <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
-  let extra_inbox: Vec<Url> = extra_inbox.into_iter().collect();
-  let follower_inboxes: Vec<Url> = vec![
-    community.get_follower_inboxes(context.pool()).await?,
-    extra_inbox,
-  ]
-  .iter()
-  .flatten()
-  .unique()
-  .filter(|inbox| inbox.host_str() != Some(&Settings::get().hostname))
-  .filter(|inbox| check_is_apub_id_valid(inbox, false).is_ok())
-  .map(|inbox| inbox.to_owned())
-  .collect();
-  debug!(
-    "Sending activity {:?} to followers of {}",
-    &activity.id_unchecked().map(ToString::to_string),
-    &community.actor_id
-  );
-
-  send_activity_internal(context, activity, community, follower_inboxes, true, false).await?;
-
-  Ok(())
-}
-
-/// Sends an activity from a local person to a remote community.
-///
-/// * `activity` the activity to send
-/// * `creator` the creator of the activity
-/// * `community` the destination community
-/// * `object_actor` if the object of the activity is an actor, it should be passed here so it can
-///                  be sent directly to the actor
-///
-pub(crate) async fn send_to_community<T, Kind>(
-  activity: T,
-  creator: &Person,
-  community: &Community,
-  object_actor: Option<Url>,
-  context: &LemmyContext,
-) -> Result<(), LemmyError>
-where
-  T: AsObject<Kind> + Extends<Kind> + Debug + BaseExt<Kind>,
-  Kind: Serialize,
-  <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
-  // if this is a local community, we need to do an announce from the community instead
-  if community.local {
-    community
-      .send_announce(activity.into_any_base()?, object_actor, context)
-      .await?;
-  } else {
-    let inbox = community.get_shared_inbox_or_inbox_url();
-    check_is_apub_id_valid(&inbox, false)?;
-    debug!(
-      "Sending activity {:?} to community {}",
-      &activity.id_unchecked().map(ToString::to_string),
-      &community.actor_id
-    );
-    // dont send to object_actor here, as that is responsibility of the community itself
-    send_activity_internal(context, activity, creator, vec![inbox], true, false).await?;
-  }
-
-  Ok(())
-}
-
 pub(crate) async fn send_to_community_new(
   activity: AnnouncableActivities,
   activity_id: &Url,
@@ -184,62 +98,6 @@ where
   Ok(())
 }
 
-/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
-/// handling signing and retrying failed deliveres.
-///
-/// The caller of this function needs to remove any blocked domains from `to`,
-/// using `check_is_apub_id_valid()`.
-async fn send_activity_internal<T, Kind>(
-  context: &LemmyContext,
-  activity: T,
-  actor: &dyn ActorType,
-  inboxes: Vec<Url>,
-  insert_into_db: bool,
-  sensitive: bool,
-) -> Result<(), LemmyError>
-where
-  T: AsObject<Kind> + Extends<Kind> + Debug,
-  Kind: Serialize,
-  <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
-{
-  if !Settings::get().federation.enabled || inboxes.is_empty() {
-    return Ok(());
-  }
-
-  // Don't send anything to ourselves
-  let hostname = Settings::get().get_hostname_without_port()?;
-  let inboxes: Vec<&Url> = inboxes
-    .iter()
-    .filter(|i| i.domain().expect("valid inbox url") != hostname)
-    .collect();
-
-  let activity = activity.into_any_base()?;
-  let serialised_activity = serde_json::to_string(&activity)?;
-
-  // This is necessary because send_comment and send_comment_mentions
-  // might send the same ap_id
-  if insert_into_db {
-    let id = activity.id().context(location_info!())?;
-    insert_activity(id, activity.clone(), true, sensitive, context.pool()).await?;
-  }
-
-  for i in inboxes {
-    let message = SendActivityTask {
-      activity: serialised_activity.to_owned(),
-      inbox: i.to_owned(),
-      actor_id: actor.actor_id(),
-      private_key: actor.private_key().context(location_info!())?,
-    };
-    if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
-      do_send(message, &Client::default()).await?;
-    } else {
-      context.activity_queue.queue::<SendActivityTask>(message)?;
-    }
-  }
-
-  Ok(())
-}
-
 #[derive(Clone, Debug, Deserialize, Serialize)]
 struct SendActivityTask {
   activity: String,
index 587c6cfb853855c032b4a32f1b8a031668175b3e..cd75031dce62523be2e27e75847d72893c64fbe8 100644 (file)
@@ -1,10 +1,14 @@
 use crate::{
+  activities::{
+    community::announce::{AnnouncableActivities, AnnounceActivity},
+    extract_community,
+    following::{follow::FollowCommunity, undo::UndoFollowCommunity},
+  },
   extensions::context::lemmy_context,
   generate_moderators_url,
   http::{
     create_apub_response,
     create_apub_tombstone_response,
-    inbox_enums::GroupInboxActivities,
     payload_to_string,
     receive_activity,
   },
@@ -18,6 +22,7 @@ use activitystreams::{
 };
 use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
 use lemmy_api_common::blocking;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
 use lemmy_db_queries::source::{activity::Activity_, community::Community_};
 use lemmy_db_schema::source::{activity::Activity, community::Community};
 use lemmy_db_views_actor::{
@@ -26,7 +31,8 @@ use lemmy_db_views_actor::{
 };
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::Deserialize;
+use log::trace;
+use serde::{Deserialize, Serialize};
 
 #[derive(Deserialize)]
 pub(crate) struct CommunityQuery {
@@ -52,6 +58,14 @@ pub(crate) async fn get_apub_community_http(
   }
 }
 
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+pub enum GroupInboxActivities {
+  FollowCommunity(FollowCommunity),
+  UndoFollowCommunity(UndoFollowCommunity),
+  AnnouncableActivities(AnnouncableActivities),
+}
+
 /// Handler for all incoming receive to community inboxes.
 pub async fn community_inbox(
   request: HttpRequest,
@@ -60,7 +74,26 @@ pub async fn community_inbox(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, LemmyError> {
   let unparsed = payload_to_string(payload).await?;
-  receive_activity::<GroupInboxActivities>(request, &unparsed, context).await
+  trace!("Received community inbox activity {}", unparsed);
+  let activity = serde_json::from_str::<GroupInboxActivities>(&unparsed)?;
+
+  receive_group_inbox(activity.clone(), request, &context).await?;
+
+  if let GroupInboxActivities::AnnouncableActivities(announcable) = activity {
+    let community = extract_community(&announcable.cc(), &context, &mut 0).await?;
+    if community.local {
+      AnnounceActivity::send(announcable, &community, vec![], &context).await?;
+    }
+  }
+  Ok(HttpResponse::Ok().finish())
+}
+
+pub(in crate::http) async fn receive_group_inbox(
+  activity: GroupInboxActivities,
+  request: HttpRequest,
+  context: &LemmyContext,
+) -> Result<HttpResponse, LemmyError> {
+  receive_activity(request, activity.clone(), context).await
 }
 
 /// Returns an empty followers collection, only populating the size (for privacy).
diff --git a/crates/apub/src/http/inbox_enums.rs b/crates/apub/src/http/inbox_enums.rs
deleted file mode 100644 (file)
index 1ed210b..0000000
+++ /dev/null
@@ -1,81 +0,0 @@
-use crate::activities::{
-  comment::create_or_update::CreateOrUpdateComment,
-  community::{
-    add_mod::AddMod,
-    announce::AnnounceActivity,
-    block_user::BlockUserFromCommunity,
-    undo_block_user::UndoBlockUserFromCommunity,
-    update::UpdateCommunity,
-  },
-  deletion::{delete::Delete, undo_delete::UndoDelete},
-  following::{accept::AcceptFollowCommunity, follow::FollowCommunity, undo::UndoFollowCommunity},
-  post::create_or_update::CreateOrUpdatePost,
-  private_message::{
-    create_or_update::CreateOrUpdatePrivateMessage,
-    delete::DeletePrivateMessage,
-    undo_delete::UndoDeletePrivateMessage,
-  },
-  removal::{remove::RemoveMod, undo_remove::UndoRemovePostCommentOrCommunity},
-  voting::{undo_vote::UndoVote, vote::Vote},
-};
-use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum PersonInboxActivities {
-  AcceptFollowCommunity(AcceptFollowCommunity),
-  CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
-  DeletePrivateMessage(DeletePrivateMessage),
-  UndoDeletePrivateMessage(UndoDeletePrivateMessage),
-  AnnounceActivity(Box<AnnounceActivity>),
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum GroupInboxActivities {
-  FollowCommunity(FollowCommunity),
-  UndoFollowCommunity(UndoFollowCommunity),
-  CreateOrUpdateComment(CreateOrUpdateComment),
-  CreateOrUpdatePost(Box<CreateOrUpdatePost>),
-  Vote(Vote),
-  UndoVote(UndoVote),
-  DeletePostCommentOrCommunity(Delete),
-  UndoDeletePostCommentOrCommunity(UndoDelete),
-  UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
-  UpdateCommunity(Box<UpdateCommunity>),
-  BlockUserFromCommunity(BlockUserFromCommunity),
-  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
-  AddMod(AddMod),
-  RemoveMod(RemoveMod),
-}
-
-#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler)]
-#[serde(untagged)]
-pub enum SharedInboxActivities {
-  // received by group
-  FollowCommunity(FollowCommunity),
-  UndoFollowCommunity(UndoFollowCommunity),
-  CreateOrUpdateComment(CreateOrUpdateComment),
-  CreateOrUpdatePost(Box<CreateOrUpdatePost>),
-  Vote(Vote),
-  UndoVote(UndoVote),
-  Delete(Delete),
-  UndoDelete(UndoDelete),
-  UndoRemovePostCommentOrCommunity(UndoRemovePostCommentOrCommunity),
-  UpdateCommunity(Box<UpdateCommunity>),
-  BlockUserFromCommunity(BlockUserFromCommunity),
-  UndoBlockUserFromCommunity(UndoBlockUserFromCommunity),
-  AddMod(AddMod),
-  RemoveMod(RemoveMod),
-  // received by person
-  AcceptFollowCommunity(AcceptFollowCommunity),
-  // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
-  // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
-  CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
-  DeletePrivateMessage(DeletePrivateMessage),
-  UndoDeletePrivateMessage(UndoDeletePrivateMessage),
-  AnnounceActivity(Box<AnnounceActivity>),
-}
index 477345fd4700f72d5c55ccbd3e1e7e0619f4cdb1..6fe8d182ea88df53b0ebfeabb6bdf2f985e6c6b7 100644 (file)
@@ -2,7 +2,10 @@ use crate::{
   check_is_apub_id_valid,
   extensions::signatures::verify_signature,
   fetcher::get_or_fetch_and_upsert_actor,
-  http::inbox_enums::SharedInboxActivities,
+  http::{
+    community::{receive_group_inbox, GroupInboxActivities},
+    person::{receive_person_inbox, PersonInboxActivities},
+  },
   insert_activity,
   APUB_JSON_CONTENT_TYPE,
 };
@@ -17,29 +20,47 @@ use anyhow::{anyhow, Context};
 use futures::StreamExt;
 use http::StatusCode;
 use lemmy_api_common::blocking;
-use lemmy_apub_lib::ActivityHandler;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
 use lemmy_db_queries::{source::activity::Activity_, DbPool};
 use lemmy_db_schema::source::activity::Activity;
 use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
 use lemmy_websocket::LemmyContext;
+use log::{info, trace};
 use serde::{Deserialize, Serialize};
 use std::{fmt::Debug, io::Read};
 use url::Url;
 
 mod comment;
 mod community;
-mod inbox_enums;
 mod person;
 mod post;
 pub mod routes;
 
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+pub enum SharedInboxActivities {
+  GroupInboxActivities(GroupInboxActivities),
+  // Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
+  // avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
+  PersonInboxActivities(PersonInboxActivities),
+}
+
 pub async fn shared_inbox(
   request: HttpRequest,
   payload: Payload,
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, LemmyError> {
   let unparsed = payload_to_string(payload).await?;
-  receive_activity::<SharedInboxActivities>(request, &unparsed, context).await
+  trace!("Received shared inbox activity {}", unparsed);
+  let activity = serde_json::from_str::<SharedInboxActivities>(&unparsed)?;
+  match activity {
+    SharedInboxActivities::GroupInboxActivities(g) => {
+      receive_group_inbox(g, request, &context).await
+    }
+    SharedInboxActivities::PersonInboxActivities(p) => {
+      receive_person_inbox(p, request, &context).await
+    }
+  }
 }
 
 async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
@@ -55,36 +76,36 @@ async fn payload_to_string(mut payload: Payload) -> Result<String, LemmyError> {
 // TODO: move most of this code to library
 async fn receive_activity<'a, T>(
   request: HttpRequest,
-  activity: &'a str,
-  context: web::Data<LemmyContext>,
+  activity: T,
+  context: &LemmyContext,
 ) -> Result<HttpResponse, LemmyError>
 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 = serde_json::from_str::<T>(activity)?;
-  let activity_data = activity.common();
-
   let request_counter = &mut 0;
-  let actor =
-    get_or_fetch_and_upsert_actor(&activity_data.actor, &context, request_counter).await?;
+  let actor = get_or_fetch_and_upsert_actor(activity.actor(), context, request_counter).await?;
   verify_signature(&request, &actor.public_key().context(location_info!())?)?;
 
   // Do nothing if we received the same activity before
-  if is_activity_already_known(context.pool(), activity_data.id_unchecked()).await? {
+  if is_activity_already_known(context.pool(), activity.id_unchecked()).await? {
     return Ok(HttpResponse::Ok().finish());
   }
-  check_is_apub_id_valid(&activity_data.actor, false)?;
-  println!(
-    "Verifying activity {}",
-    activity_data.id_unchecked().to_string()
-  );
-  activity.verify(&context, request_counter).await?;
+  check_is_apub_id_valid(activity.actor(), false)?;
+  info!("Verifying activity {}", activity.id_unchecked().to_string());
+  activity.verify(context, request_counter).await?;
   assert_activity_not_local(&activity)?;
 
   // Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
   // if we receive the same activity twice in very quick succession.
   insert_activity(
-    activity_data.id_unchecked(),
+    activity.id_unchecked(),
     activity.clone(),
     false,
     true,
@@ -92,11 +113,8 @@ where
   )
   .await?;
 
-  println!(
-    "Receiving activity {}",
-    activity_data.id_unchecked().to_string()
-  );
-  activity.receive(&context, request_counter).await?;
+  info!("Receiving activity {}", activity.id_unchecked().to_string());
+  activity.receive(context, request_counter).await?;
   Ok(HttpResponse::Ok().finish())
 }
 
@@ -168,12 +186,8 @@ pub(crate) async fn is_activity_already_known(
   }
 }
 
-fn assert_activity_not_local<T: Debug + ActivityHandler>(activity: &T) -> Result<(), LemmyError> {
-  let activity_domain = activity
-    .common()
-    .id_unchecked()
-    .domain()
-    .context(location_info!())?;
+fn assert_activity_not_local<T: Debug + ActivityFields>(activity: &T) -> Result<(), LemmyError> {
+  let activity_domain = activity.id_unchecked().domain().context(location_info!())?;
 
   if activity_domain == Settings::get().hostname {
     return Err(
index 42f25bb629788c6f5e0fc0c8e039bbf6cbd6f54f..dcab6001705b6c09741e5533a7d6ef09bbb32a4a 100644 (file)
@@ -1,9 +1,17 @@
 use crate::{
+  activities::{
+    community::announce::{AnnouncableActivities, AnnounceActivity},
+    following::accept::AcceptFollowCommunity,
+    private_message::{
+      create_or_update::CreateOrUpdatePrivateMessage,
+      delete::DeletePrivateMessage,
+      undo_delete::UndoDeletePrivateMessage,
+    },
+  },
   extensions::context::lemmy_context,
   http::{
     create_apub_response,
     create_apub_tombstone_response,
-    inbox_enums::PersonInboxActivities,
     payload_to_string,
     receive_activity,
   },
@@ -16,11 +24,13 @@ use activitystreams::{
 };
 use actix_web::{body::Body, web, web::Payload, HttpRequest, HttpResponse};
 use lemmy_api_common::blocking;
+use lemmy_apub_lib::{ActivityFields, ActivityHandler};
 use lemmy_db_queries::source::person::Person_;
 use lemmy_db_schema::source::person::Person;
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
-use serde::Deserialize;
+use log::trace;
+use serde::{Deserialize, Serialize};
 use url::Url;
 
 #[derive(Deserialize)]
@@ -49,6 +59,18 @@ pub(crate) async fn get_apub_person_http(
   }
 }
 
+#[derive(Clone, Debug, Deserialize, Serialize, ActivityHandler, ActivityFields)]
+#[serde(untagged)]
+pub enum PersonInboxActivities {
+  AcceptFollowCommunity(AcceptFollowCommunity),
+  /// Some activities can also be sent from user to user, eg a comment with mentions
+  AnnouncableActivities(AnnouncableActivities),
+  CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
+  DeletePrivateMessage(DeletePrivateMessage),
+  UndoDeletePrivateMessage(UndoDeletePrivateMessage),
+  AnnounceActivity(Box<AnnounceActivity>),
+}
+
 pub async fn person_inbox(
   request: HttpRequest,
   payload: Payload,
@@ -56,7 +78,17 @@ pub async fn person_inbox(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, LemmyError> {
   let unparsed = payload_to_string(payload).await?;
-  receive_activity::<PersonInboxActivities>(request, &unparsed, context).await
+  trace!("Received person inbox activity {}", unparsed);
+  let activity = serde_json::from_str::<PersonInboxActivities>(&unparsed)?;
+  receive_person_inbox(activity, request, &context).await
+}
+
+pub(in crate::http) async fn receive_person_inbox(
+  activity: PersonInboxActivities,
+  request: HttpRequest,
+  context: &LemmyContext,
+) -> Result<HttpResponse, LemmyError> {
+  receive_activity(request, activity, context).await
 }
 
 pub(crate) async fn get_apub_person_outbox(
index 47714300932f371d6b694c6e6dbcac9f32d58507..839e7d1486c51a811aaf947e89d3623b774e7923 100644 (file)
@@ -10,7 +10,6 @@ pub mod migrations;
 pub mod objects;
 
 use crate::extensions::signatures::PublicKey;
-use activitystreams::base::AnyBase;
 use anyhow::{anyhow, Context};
 use diesel::NotFound;
 use lemmy_api_common::blocking;
@@ -139,41 +138,6 @@ trait ActorType {
 pub trait CommunityType {
   fn followers_url(&self) -> Url;
   async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
-
-  async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
-
-  async fn send_announce(
-    &self,
-    activity: AnyBase,
-    object: Option<Url>,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-
-  async fn send_add_mod(
-    &self,
-    actor: &Person,
-    added_mod: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-  async fn send_remove_mod(
-    &self,
-    actor: &Person,
-    removed_mod: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-
-  async fn send_block_user(
-    &self,
-    actor: &Person,
-    blocked_user: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-  async fn send_undo_block_user(
-    &self,
-    actor: &Person,
-    blocked_user: Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
 }
 
 pub enum EndpointType {
index df73d5e8f0f2d1f72d2e3f116042bb8105043f4c..cc88b79c96d7aa528b6b3331464812a611b15552 100644 (file)
@@ -1,33 +1,15 @@
 pub mod values;
 
-use activitystreams::{
-  base::AnyBase,
-  error::DomainError,
-  primitives::OneOrMany,
-  unparsed::Unparsed,
-};
+use activitystreams::error::DomainError;
 pub use lemmy_apub_lib_derive::*;
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ActivityCommonFields {
-  #[serde(rename = "@context")]
-  pub context: OneOrMany<AnyBase>,
-  pub id: Url,
-  pub actor: Url,
-
-  // unparsed fields
-  #[serde(flatten)]
-  pub unparsed: Unparsed,
-}
-
-impl ActivityCommonFields {
-  pub fn id_unchecked(&self) -> &Url {
-    &self.id
-  }
+pub trait ActivityFields {
+  fn id_unchecked(&self) -> &Url;
+  fn actor(&self) -> &Url;
+  fn cc(&self) -> Vec<Url>;
 }
 
 #[async_trait::async_trait(?Send)]
@@ -43,7 +25,6 @@ pub trait ActivityHandler {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError>;
-  fn common(&self) -> &ActivityCommonFields;
 }
 
 pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
@@ -53,13 +34,6 @@ pub fn verify_domains_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
   Ok(())
 }
 
-pub fn verify_domains_match_opt(a: &Url, b: Option<&Url>) -> Result<(), LemmyError> {
-  if let Some(b2) = b {
-    return verify_domains_match(a, b2);
-  }
-  Ok(())
-}
-
 pub fn verify_urls_match(a: &Url, b: &Url) -> Result<(), LemmyError> {
   if a != b {
     return Err(DomainError.into());
index d35454a37a76f106426d74f090bd8a7c3d55e0e6..e7a1912cf4650f58dc29624bb29e98afe21bce3e 100644 (file)
@@ -1,6 +1,6 @@
 use proc_macro2::TokenStream;
 use quote::quote;
-use syn::{parse_macro_input, Data, DeriveInput};
+use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
 
 /// Generates implementation ActivityHandler for an enum, which looks like the following (handling
 /// all enum variants).
@@ -46,104 +46,118 @@ use syn::{parse_macro_input, Data, DeriveInput};
 ///   }
 ///
 /// ```
-///
-/// TODO: consider replacing this macro with https://crates.io/crates/typetag crate, though it
-///       doesnt support untagged enums which we need for apub.
 #[proc_macro_derive(ActivityHandler)]
 pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
-  // Parse the input tokens into a syntax tree.
   let input = parse_macro_input!(input as DeriveInput);
 
-  // Used in the quasi-quotation below as `#name`.
-  let name = input.ident;
+  let enum_name = input.ident;
 
   let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
 
-  let input_enum = if let Data::Enum(d) = input.data {
-    d
+  let enum_variants = if let Data::Enum(d) = input.data {
+    d.variants
   } else {
     unimplemented!()
   };
 
-  let impl_verify = input_enum
-    .variants
+  let body_verify = quote! {a.verify(context, request_counter).await};
+  let impl_verify = enum_variants
     .iter()
-    .map(|variant| variant_impl_verify(&name, variant));
-  let impl_receive = input_enum
-    .variants
+    .map(|v| generate_match_arm(&enum_name, v, &body_verify));
+  let body_receive = quote! {a.receive(context, request_counter).await};
+  let impl_receive = enum_variants
     .iter()
-    .map(|variant| variant_impl_receive(&name, variant));
-  let impl_common = input_enum
-    .variants
-    .iter()
-    .map(|variant| variant_impl_common(&name, variant));
+    .map(|v| generate_match_arm(&enum_name, v, &body_receive));
 
-  // The generated impl.
   let expanded = quote! {
       #[async_trait::async_trait(?Send)]
-      impl #impl_generics lemmy_apub_lib::ActivityHandler for #name #ty_generics #where_clause {
+      impl #impl_generics lemmy_apub_lib::ActivityHandler for #enum_name #ty_generics #where_clause {
           async fn verify(
               &self,
-              context: &LemmyContext,
+              context: &lemmy_websocket::LemmyContext,
               request_counter: &mut i32,
-            ) -> Result<(), LemmyError> {
+            ) -> Result<(), lemmy_utils::LemmyError> {
             match self {
               #(#impl_verify)*
             }
           }
           async fn receive(
             self,
-            context: &LemmyContext,
+            context: &lemmy_websocket::LemmyContext,
             request_counter: &mut i32,
-          ) -> Result<(), LemmyError> {
+          ) -> Result<(), lemmy_utils::LemmyError> {
             match self {
               #(#impl_receive)*
             }
           }
-          fn common(&self) -> &ActivityCommonFields {
-            match self {
-              #(#impl_common)*
-            }
-          }
       }
   };
-
-  // Hand the output tokens back to the compiler.
-  proc_macro::TokenStream::from(expanded)
+  expanded.into()
 }
 
-fn variant_impl_common(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
+fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
   let id = &variant.ident;
   match &variant.fields {
-    syn::Fields::Unnamed(_) => {
+    Unnamed(_) => {
       quote! {
-        #name::#id(a) => a.common(),
+        #enum_name::#id(a) => #body,
       }
     }
     _ => unimplemented!(),
   }
 }
 
-fn variant_impl_verify(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
-  let id = &variant.ident;
-  match &variant.fields {
-    syn::Fields::Unnamed(_) => {
+#[proc_macro_derive(ActivityFields)]
+pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+  let input = parse_macro_input!(input as DeriveInput);
+
+  let name = input.ident;
+
+  let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+  let expanded = match input.data {
+    Data::Enum(e) => {
+      let variants = e.variants;
+      let impl_id = variants
+        .iter()
+        .map(|v| generate_match_arm(&name, v, &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! {
-        #name::#id(a) => a.verify(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)* } }
+          }
       }
     }
-    _ => unimplemented!(),
-  }
-}
-
-fn variant_impl_receive(name: &syn::Ident, variant: &syn::Variant) -> TokenStream {
-  let id = &variant.ident;
-  match &variant.fields {
-    syn::Fields::Unnamed(_) => {
+    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! {
-        #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 { &self.id }
+              fn actor(&self) -> &url::Url { &self.actor }
+              fn cc(&self) -> Vec<url::Url> { #cc_impl }
+          }
       }
     }
     _ => unimplemented!(),
-  }
+  };
+  expanded.into()
 }