]> Untitled Git - lemmy.git/commitdiff
Rewrite activitypub following, person, community, pm (#1692)
authorNutomic <me@nutomic.com>
Thu, 12 Aug 2021 12:48:09 +0000 (12:48 +0000)
committerGitHub <noreply@github.com>
Thu, 12 Aug 2021 12:48:09 +0000 (08:48 -0400)
* Rewrite following activities

* Rewrite person apub

* Rewrite community apub

* Rewrite private message activity sending

45 files changed:
Cargo.lock
crates/api/src/community.rs
crates/api_crud/src/private_message/delete.rs
crates/apub/Cargo.toml
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/undo_block_user.rs
crates/apub/src/activities/community/update.rs
crates/apub/src/activities/deletion/delete.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/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/remove.rs
crates/apub/src/activities/removal/undo_remove.rs
crates/apub/src/activities/send/community.rs
crates/apub/src/activities/send/mod.rs
crates/apub/src/activities/send/person.rs
crates/apub/src/activities/send/private_message.rs [deleted file]
crates/apub/src/activities/voting/undo_vote.rs
crates/apub/src/activities/voting/vote.rs
crates/apub/src/activity_queue.rs
crates/apub/src/extensions/group_extension.rs [deleted file]
crates/apub/src/extensions/mod.rs
crates/apub/src/extensions/person_extension.rs [deleted file]
crates/apub/src/extensions/signatures.rs
crates/apub/src/fetcher/community.rs
crates/apub/src/fetcher/objects.rs
crates/apub/src/fetcher/person.rs
crates/apub/src/fetcher/search.rs
crates/apub/src/lib.rs
crates/apub/src/objects/comment.rs
crates/apub/src/objects/community.rs
crates/apub/src/objects/mod.rs
crates/apub/src/objects/person.rs
crates/apub/src/objects/post.rs
crates/apub/src/objects/private_message.rs
crates/apub_lib/src/lib.rs
crates/apub_lib_derive/src/lib.rs

index 146b72e74526cfb1b423006aa4ae4b66283e6920..34431d3ad760c398c2fdb1f8b3ace1cf68e22477 100644 (file)
@@ -795,8 +795,18 @@ version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
 dependencies = [
- "darling_core",
- "darling_macro",
+ "darling_core 0.10.2",
+ "darling_macro 0.10.2",
+]
+
+[[package]]
+name = "darling"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12"
+dependencies = [
+ "darling_core 0.13.0",
+ "darling_macro 0.13.0",
 ]
 
 [[package]]
@@ -809,7 +819,21 @@ dependencies = [
  "ident_case",
  "proc-macro2 1.0.27",
  "quote 1.0.9",
- "strsim",
+ "strsim 0.9.3",
+ "syn 1.0.73",
+]
+
+[[package]]
+name = "darling_core"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3"
+dependencies = [
+ "fnv",
+ "ident_case",
+ "proc-macro2 1.0.27",
+ "quote 1.0.9",
+ "strsim 0.10.0",
  "syn 1.0.73",
 ]
 
@@ -819,7 +843,18 @@ version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
 dependencies = [
- "darling_core",
+ "darling_core 0.10.2",
+ "quote 1.0.9",
+ "syn 1.0.73",
+]
+
+[[package]]
+name = "darling_macro"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc"
+dependencies = [
+ "darling_core 0.13.0",
  "quote 1.0.9",
  "syn 1.0.73",
 ]
@@ -840,7 +875,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
 dependencies = [
- "darling",
+ "darling 0.10.2",
  "derive_builder_core",
  "proc-macro2 1.0.27",
  "quote 1.0.9",
@@ -853,7 +888,7 @@ version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
 dependencies = [
- "darling",
+ "darling 0.10.2",
  "proc-macro2 1.0.27",
  "quote 1.0.9",
  "syn 1.0.73",
@@ -1661,7 +1696,6 @@ name = "lemmy_apub"
 version = "0.11.3"
 dependencies = [
  "activitystreams",
- "activitystreams-ext",
  "actix",
  "actix-rt",
  "actix-web",
@@ -1695,6 +1729,7 @@ dependencies = [
  "reqwest",
  "serde",
  "serde_json",
+ "serde_with",
  "sha2",
  "strum",
  "strum_macros",
@@ -2732,6 +2767,12 @@ dependencies = [
  "webpki",
 ]
 
+[[package]]
+name = "rustversion"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
+
 [[package]]
 name = "ryu"
 version = "1.0.5"
@@ -2861,6 +2902,29 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_with"
+version = "1.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ad9fdbb69badc8916db738c25efd04f0a65297d26c2f8de4b62e57b8c12bc72"
+dependencies = [
+ "rustversion",
+ "serde",
+ "serde_with_macros",
+]
+
+[[package]]
+name = "serde_with_macros"
+version = "1.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1569374bd54623ec8bd592cf22ba6e03c0f177ff55fbc8c29a49e296e7adecf"
+dependencies = [
+ "darling 0.13.0",
+ "proc-macro2 1.0.27",
+ "quote 1.0.9",
+ "syn 1.0.73",
+]
+
 [[package]]
 name = "serial_test"
 version = "0.5.1"
@@ -3056,6 +3120,12 @@ version = "0.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
 
+[[package]]
+name = "strsim"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
+
 [[package]]
 name = "strum"
 version = "0.21.0"
index 017ef84934dc3280d3f4e06deeb1aef134137ef3..0ca4ac47ad8be6406ac475690bbcab78af439924 100644 (file)
@@ -8,7 +8,13 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   is_mod_or_admin,
 };
-use lemmy_apub::{ActorType, CommunityType, UserType};
+use lemmy_apub::{
+  activities::following::{
+    follow::FollowCommunity as FollowCommunityApub,
+    undo::UndoFollowCommunity,
+  },
+  CommunityType,
+};
 use lemmy_db_queries::{
   source::{comment::Comment_, community::CommunityModerator_, post::Post_},
   Bannable,
@@ -74,15 +80,9 @@ impl Perform for FollowCommunity {
     } else if data.follow {
       // Dont actually add to the community followers here, because you need
       // to wait for the accept
-      local_user_view
-        .person
-        .send_follow(&community.actor_id(), context)
-        .await?;
+      FollowCommunityApub::send(&local_user_view.person, &community, context).await?;
     } else {
-      local_user_view
-        .person
-        .send_unfollow(&community.actor_id(), context)
-        .await?;
+      UndoFollowCommunity::send(&local_user_view.person, &community, context).await?;
       let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
       if blocking(context.pool(), unfollow).await?.is_err() {
         return Err(ApiError::err("community_follower_already_exists").into());
index 757596cd1d629393d803f7fe48a076d4eec9b0b9..78dcd931122cd609d602dbaa8a126674caaeab6d 100644 (file)
@@ -5,7 +5,10 @@ use lemmy_api_common::{
   get_local_user_view_from_jwt,
   person::{DeletePrivateMessage, PrivateMessageResponse},
 };
-use lemmy_apub::ApubObjectType;
+use lemmy_apub::activities::private_message::{
+  delete::DeletePrivateMessage as DeletePrivateMessageApub,
+  undo_delete::UndoDeletePrivateMessage,
+};
 use lemmy_db_queries::{source::private_message::PrivateMessage_, Crud, DeleteableOrRemoveable};
 use lemmy_db_schema::source::private_message::PrivateMessage;
 use lemmy_db_views::{local_user_view::LocalUserView, private_message_view::PrivateMessageView};
@@ -45,13 +48,14 @@ impl PerformCrud for DeletePrivateMessage {
 
     // Send the apub update
     if data.deleted {
-      updated_private_message
-        .blank_out_deleted_or_removed_info()
-        .send_delete(&local_user_view.person, context)
-        .await?;
+      DeletePrivateMessageApub::send(
+        &local_user_view.person,
+        &updated_private_message.blank_out_deleted_or_removed_info(),
+        context,
+      )
+      .await?;
     } else {
-      updated_private_message
-        .send_undo_delete(&local_user_view.person, context)
+      UndoDeletePrivateMessage::send(&local_user_view.person, &updated_private_message, context)
         .await?;
     }
 
index 1937dffd790c96b68642199646c92a8764a03eb8..e0c714c10039ab50d20457a66464210be21697bb 100644 (file)
@@ -21,11 +21,11 @@ lemmy_api_common = { version = "=0.11.3", path = "../api_common" }
 lemmy_websocket = { version = "=0.11.3", path = "../websocket" }
 diesel = "1.4.7"
 activitystreams = "0.7.0-alpha.11"
-activitystreams-ext = "0.1.0-alpha.2"
 bcrypt = "0.10.0"
 chrono = { version = "0.4.19", features = ["serde"] }
 serde_json = { version = "1.0.64", features = ["preserve_order"] }
 serde = { version = "1.0.126", features = ["derive"] }
+serde_with = "1.9.4"
 actix = "0.12.0"
 actix-web = { version = "4.0.0-beta.8", default-features = false }
 actix-rt = { version = "2.2.0", default-features = false }
index 4cafafbaaf3296a7c821a50489024cd87b2faf82..bfc1b7fe6d4544ed63f85b4a032a5b91d200d689 100644 (file)
@@ -96,7 +96,7 @@ impl ActivityHandler for CreateOrUpdateComment {
       request_counter,
     )
     .await?;
-    verify_domains_match(&self.common.actor, &self.object.id)?;
+    verify_domains_match(&self.common.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?;
@@ -104,18 +104,12 @@ impl ActivityHandler for CreateOrUpdateComment {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let comment = Comment::from_apub(
-      &self.object,
-      context,
-      self.common.actor.clone(),
-      request_counter,
-      false,
-    )
-    .await?;
+    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 notif_type = match self.kind {
index 0dcd9818b5a9966e8c94823c4aa2e65e386a28bf..db6d369a192350ba64fed267c1c9f3e2875fa31e 100644 (file)
@@ -45,7 +45,7 @@ impl ActivityHandler for AddMod {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
@@ -71,7 +71,7 @@ impl ActivityHandler for AddMod {
       .await??;
     }
     if community.local {
-      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
+      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
       community
         .send_announce(anybase, Some(self.object.clone()), context)
         .await?;
index 9cde8a43cc0f1254c76371886d6d1abc9eba0979..fc3bebfedf58cd70ea890168f8e7ff15bfa25b76 100644 (file)
@@ -110,7 +110,7 @@ impl ActivityHandler for AnnounceActivity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 34909b2e6086904264c906d152fa3f3acebe6d8c..bf362c4827a45a9a0445c54a72f6998ab3ea76c1 100644 (file)
@@ -42,7 +42,7 @@ impl ActivityHandler for BlockUserFromCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 53b665db15fd210e918580a97d3f1cdf1381081f..514c90ccd046fb66a95aafce869ad0b9f3e605f6 100644 (file)
@@ -43,7 +43,7 @@ impl ActivityHandler for UndoBlockUserFromCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index fd0bf2d456a76c3fe70dcaaa5dcc3b0390456ce3..964a423889d727d1faa077f945e0ad111f18dc9b 100644 (file)
@@ -5,8 +5,7 @@ use crate::{
     verify_mod_action,
     verify_person_in_community,
   },
-  objects::FromApubToForm,
-  GroupExt,
+  objects::community::Group,
 };
 use activitystreams::activity::kind::UpdateType;
 use lemmy_api_common::blocking;
@@ -23,7 +22,7 @@ use url::Url;
 #[serde(rename_all = "camelCase")]
 pub struct UpdateCommunity {
   to: PublicUrl,
-  object: GroupExt,
+  object: Group,
   cc: [Url; 1],
   #[serde(rename = "type")]
   kind: UpdateType,
@@ -45,9 +44,9 @@ impl ActivityHandler for UpdateCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
-    request_counter: &mut i32,
+    _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
     let cc = self.cc[0].clone().into();
     let community = blocking(context.pool(), move |conn| {
@@ -55,14 +54,8 @@ impl ActivityHandler for UpdateCommunity {
     })
     .await??;
 
-    let updated_community = CommunityForm::from_apub(
-      &self.object,
-      context,
-      community.actor_id.clone().into(),
-      request_counter,
-      false,
-    )
-    .await?;
+    let updated_community =
+      Group::from_apub_to_form(&self.object, &community.actor_id.clone().into()).await?;
     let cf = CommunityForm {
       name: updated_community.name,
       title: updated_community.title,
index cabfcce8659fb0f847cf04480a22d219273bdc1d..d377a6696bdf731945dc93f6694e23527022b090 100644 (file)
@@ -74,7 +74,7 @@ impl ActivityHandler for DeletePostCommentOrCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index ea70e5f5e76486a744dd959416ce0281a457d1da..e57e6b401c950a320f3c9464322d2b1903df6660 100644 (file)
@@ -62,7 +62,7 @@ impl ActivityHandler for UndoDeletePostCommentOrCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 6bd5d272f7f4d8420c8929a46975e3149ca9e9ff..bc6895899d1437f67b5bd074481d60de413acb4c 100644 (file)
@@ -1,12 +1,23 @@
 use crate::{
-  activities::{following::follow::FollowCommunity, verify_activity, verify_community},
+  activities::{
+    following::follow::FollowCommunity,
+    generate_activity_id,
+    verify_activity,
+    verify_community,
+  },
+  activity_queue::send_activity_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::AcceptType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
-use lemmy_db_queries::Followable;
-use lemmy_db_schema::source::community::CommunityFollower;
+use lemmy_db_queries::{ApubObject, Followable};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityFollower},
+  person::Person,
+};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
@@ -22,6 +33,35 @@ pub struct AcceptFollowCommunity {
   common: ActivityCommonFields,
 }
 
+impl AcceptFollowCommunity {
+  pub async fn send(follow: FollowCommunity, context: &LemmyContext) -> Result<(), LemmyError> {
+    let community_id = follow.object.clone();
+    let community = blocking(context.pool(), move |conn| {
+      Community::read_from_apub_id(conn, &community_id.into())
+    })
+    .await??;
+    let person_id = follow.common.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 {
+      to: person.actor_id(),
+      object: follow,
+      kind: AcceptType::Accept,
+      common: ActivityCommonFields {
+        context: lemmy_context(),
+        id: id.clone(),
+        actor: community.actor_id(),
+        unparsed: Default::default(),
+      },
+    };
+    let inbox = vec![person.inbox_url.into()];
+    send_activity_new(context, &accept, &id, &community, inbox, true).await
+  }
+}
 /// Handle accepted follows
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for AcceptFollowCommunity {
@@ -39,7 +79,7 @@ impl ActivityHandler for AcceptFollowCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 49c9970c0f1484b865fea39911e72140c43a3700..aa96fb39f453f5e91642512088af39e58c2f9287 100644 (file)
@@ -1,18 +1,24 @@
 use crate::{
-  activities::{verify_activity, verify_person},
+  activities::{
+    following::accept::AcceptFollowCommunity,
+    generate_activity_id,
+    verify_activity,
+    verify_person,
+  },
+  activity_queue::send_activity_new,
+  extensions::context::lemmy_context,
   fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
-  CommunityType,
+  ActorType,
 };
-use activitystreams::{
-  activity::{kind::FollowType, Follow},
-  base::{AnyBase, ExtendsExt},
-};
-use anyhow::Context;
+use activitystreams::activity::kind::FollowType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
 use lemmy_db_queries::Followable;
-use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm};
-use lemmy_utils::{location_info, LemmyError};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityFollower, CommunityFollowerForm},
+  person::Person,
+};
+use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
 
@@ -22,11 +28,44 @@ pub struct FollowCommunity {
   pub(in crate::activities::following) to: Url,
   pub(in crate::activities::following) object: Url,
   #[serde(rename = "type")]
-  kind: FollowType,
+  pub(in crate::activities::following) kind: FollowType,
   #[serde(flatten)]
   pub(in crate::activities::following) common: ActivityCommonFields,
 }
 
+impl FollowCommunity {
+  pub async fn send(
+    actor: &Person,
+    community: &Community,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let community_follower_form = CommunityFollowerForm {
+      community_id: community.id,
+      person_id: actor.id,
+      pending: true,
+    };
+    blocking(context.pool(), move |conn| {
+      CommunityFollower::follow(conn, &community_follower_form).ok()
+    })
+    .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 inbox = vec![community.inbox_url.clone().into()];
+    send_activity_new(context, &follow, &id, actor, inbox, true).await
+  }
+}
+
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for FollowCommunity {
   async fn verify(
@@ -41,7 +80,7 @@ impl ActivityHandler for FollowCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
@@ -61,10 +100,7 @@ impl ActivityHandler for FollowCommunity {
     })
     .await?;
 
-    // TODO: avoid the conversion and pass our own follow struct directly
-    let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
-    let anybase = Follow::from_any_base(anybase)?.context(location_info!())?;
-    community.send_accept_follow(anybase, context).await
+    AcceptFollowCommunity::send(self, context).await
   }
 
   fn common(&self) -> &ActivityCommonFields {
index 1936997f4ce4675b07c1c627d6d4745f291cdc41..7fbc7be5b33ab4b6d9b458b8b6d65de415d1d7d6 100644 (file)
@@ -1,12 +1,23 @@
 use crate::{
-  activities::{following::follow::FollowCommunity, verify_activity, verify_person},
+  activities::{
+    following::follow::FollowCommunity,
+    generate_activity_id,
+    verify_activity,
+    verify_person,
+  },
+  activity_queue::send_activity_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;
+use activitystreams::activity::kind::{FollowType, UndoType};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{verify_urls_match, ActivityCommonFields, ActivityHandler};
 use lemmy_db_queries::Followable;
-use lemmy_db_schema::source::community::{CommunityFollower, CommunityFollowerForm};
+use lemmy_db_schema::source::{
+  community::{Community, CommunityFollower, CommunityFollowerForm},
+  person::Person,
+};
 use lemmy_utils::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
@@ -22,6 +33,39 @@ pub struct UndoFollowCommunity {
   common: ActivityCommonFields,
 }
 
+impl UndoFollowCommunity {
+  pub async fn send(
+    actor: &Person,
+    community: &Community,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let id = generate_activity_id(UndoType::Undo)?;
+    let undo = UndoFollowCommunity {
+      to: community.actor_id(),
+      object: FollowCommunity {
+        to: community.actor_id(),
+        object: community.actor_id(),
+        kind: FollowType::Follow,
+        common: ActivityCommonFields {
+          context: lemmy_context(),
+          id: generate_activity_id(FollowType::Follow)?,
+          actor: actor.actor_id(),
+          unparsed: Default::default(),
+        },
+      },
+      kind: UndoType::Undo,
+      common: ActivityCommonFields {
+        context: lemmy_context(),
+        id: id.clone(),
+        actor: actor.actor_id(),
+        unparsed: Default::default(),
+      },
+    };
+    let inbox = vec![community.get_shared_inbox_or_inbox_url()];
+    send_activity_new(context, &undo, &id, actor, inbox, true).await
+  }
+}
+
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for UndoFollowCommunity {
   async fn verify(
@@ -38,7 +82,7 @@ impl ActivityHandler for UndoFollowCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 4af720753707fe378a8b1e9ddfb4118c35ebcc36..94838e16f496df39838f9f4e1334627e59ca11ea 100644 (file)
@@ -87,7 +87,7 @@ impl ActivityHandler for CreateOrUpdatePost {
     verify_person_in_community(&self.common.actor, &community_id, context, request_counter).await?;
     match self.kind {
       CreateOrUpdateType::Create => {
-        verify_domains_match(&self.common.actor, &self.object.id)?;
+        verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
         verify_urls_match(&self.common.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
@@ -104,7 +104,7 @@ impl ActivityHandler for CreateOrUpdatePost {
         if is_mod_action {
           verify_mod_action(&self.common.actor, community_id, context).await?;
         } else {
-          verify_domains_match(&self.common.actor, &self.object.id)?;
+          verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
           verify_urls_match(&self.common.actor, &self.object.attributed_to)?;
         }
       }
@@ -114,20 +114,13 @@ impl ActivityHandler for CreateOrUpdatePost {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
     let actor =
       get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?;
-    let post = Post::from_apub(
-      &self.object,
-      context,
-      actor.actor_id(),
-      request_counter,
-      false,
-    )
-    .await?;
+    let post = Post::from_apub(&self.object, context, &actor.actor_id(), request_counter).await?;
 
     let notif_type = match self.kind {
       CreateOrUpdateType::Create => UserOperationCrud::CreatePost,
index 05f3c98f25ff9b5b6d9ee223da0a08da6c18857b..09582689be35f189b54a0b03292ce04efa2fd35a 100644 (file)
@@ -53,15 +53,8 @@ impl CreateOrUpdatePrivateMessage {
         unparsed: Default::default(),
       },
     };
-    send_activity_new(
-      context,
-      &create_or_update,
-      &id,
-      actor,
-      vec![recipient.get_shared_inbox_or_inbox_url()],
-      true,
-    )
-    .await
+    let inbox = vec![recipient.get_shared_inbox_or_inbox_url()];
+    send_activity_new(context, &create_or_update, &id, actor, inbox, true).await
   }
 }
 #[async_trait::async_trait(?Send)]
@@ -73,24 +66,18 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
   ) -> Result<(), LemmyError> {
     verify_activity(self.common())?;
     verify_person(&self.common.actor, context, request_counter).await?;
-    verify_domains_match(&self.common.actor, &self.object.id)?;
+    verify_domains_match(&self.common.actor, self.object.id_unchecked())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    let private_message = PrivateMessage::from_apub(
-      &self.object,
-      context,
-      self.common.actor.clone(),
-      request_counter,
-      false,
-    )
-    .await?;
+    let private_message =
+      PrivateMessage::from_apub(&self.object, context, &self.common.actor, request_counter).await?;
 
     let notif_type = match self.kind {
       CreateOrUpdateType::Create => UserOperationCrud::CreatePrivateMessage,
index b7f1a3cd9ea86a558a9efe90e664e26fa271c2c9..4c990a2d177ce4fbffbbb536535ecf8ac953c11d 100644 (file)
@@ -1,9 +1,19 @@
-use crate::activities::{private_message::send_websocket_message, verify_activity, verify_person};
+use crate::{
+  activities::{
+    generate_activity_id,
+    private_message::send_websocket_message,
+    verify_activity,
+    verify_person,
+  },
+  activity_queue::send_activity_new,
+  extensions::context::lemmy_context,
+  ActorType,
+};
 use activitystreams::activity::kind::DeleteType;
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{verify_domains_match, ActivityCommonFields, ActivityHandler};
-use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject};
-use lemmy_db_schema::source::private_message::PrivateMessage;
+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::{LemmyContext, UserOperationCrud};
 use url::Url;
@@ -11,14 +21,41 @@ use url::Url;
 #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct DeletePrivateMessage {
-  to: Url,
+  pub(in crate::activities::private_message) to: Url,
   pub(in crate::activities::private_message) object: Url,
   #[serde(rename = "type")]
-  kind: DeleteType,
+  pub(in crate::activities::private_message) kind: DeleteType,
   #[serde(flatten)]
   pub(in crate::activities::private_message) common: ActivityCommonFields,
 }
 
+impl DeletePrivateMessage {
+  pub async fn send(
+    actor: &Person,
+    pm: &PrivateMessage,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    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
+  }
+}
+
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for DeletePrivateMessage {
   async fn verify(
@@ -33,7 +70,7 @@ impl ActivityHandler for DeletePrivateMessage {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index dc297b5af4d6facb0564fd7077916a5342bc6b78..5bbca90e6e28a28b873adf98b2693e10b03a35cb 100644 (file)
@@ -1,9 +1,15 @@
-use crate::activities::{
-  private_message::{delete::DeletePrivateMessage, send_websocket_message},
-  verify_activity,
-  verify_person,
+use crate::{
+  activities::{
+    generate_activity_id,
+    private_message::{delete::DeletePrivateMessage, send_websocket_message},
+    verify_activity,
+    verify_person,
+  },
+  activity_queue::send_activity_new,
+  extensions::context::lemmy_context,
+  ActorType,
 };
-use activitystreams::activity::kind::UndoType;
+use activitystreams::activity::kind::{DeleteType, UndoType};
 use lemmy_api_common::blocking;
 use lemmy_apub_lib::{
   verify_domains_match,
@@ -11,8 +17,8 @@ use lemmy_apub_lib::{
   ActivityCommonFields,
   ActivityHandler,
 };
-use lemmy_db_queries::{source::private_message::PrivateMessage_, ApubObject};
-use lemmy_db_schema::source::private_message::PrivateMessage;
+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::{LemmyContext, UserOperationCrud};
 use url::Url;
@@ -28,6 +34,45 @@ pub struct UndoDeletePrivateMessage {
   common: ActivityCommonFields,
 }
 
+impl UndoDeletePrivateMessage {
+  pub async fn send(
+    actor: &Person,
+    pm: &PrivateMessage,
+    context: &LemmyContext,
+  ) -> Result<(), LemmyError> {
+    let recipient_id = pm.recipient_id;
+    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 id = generate_activity_id(UndoType::Undo)?;
+    let undo = UndoDeletePrivateMessage {
+      to: recipient.actor_id(),
+      object,
+      kind: UndoType::Undo,
+      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, &undo, &id, actor, inbox, true).await
+  }
+}
+
 #[async_trait::async_trait(?Send)]
 impl ActivityHandler for UndoDeletePrivateMessage {
   async fn verify(
@@ -44,7 +89,7 @@ impl ActivityHandler for UndoDeletePrivateMessage {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 053ddadf8a2b55a272887e979613aa99b9c7c2c3..83aaabc6f1a35598219f4400391e2a442c4e9c5a 100644 (file)
@@ -77,7 +77,7 @@ impl ActivityHandler for RemovePostCommentCommunityOrMod {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
@@ -115,7 +115,7 @@ impl ActivityHandler for RemovePostCommentCommunityOrMod {
         CommunityModerator::leave(conn, &form)
       })
       .await??;
-      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(self)?)?;
+      let anybase = AnyBase::from_arbitrary_json(serde_json::to_string(&self)?)?;
       community
         .send_announce(anybase, Some(self.object.clone()), context)
         .await?;
index db4518b27de52a3ebb623093e807efda69457db3..003eb4aaa281e237f8dac06c9e76a063f4915606 100644 (file)
@@ -62,7 +62,7 @@ impl ActivityHandler for UndoRemovePostCommentOrCommunity {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 852410624cfbd0028b0bc781adcfe5cfc49571c4..6f88dd36fb76848c559de8ff8853d845073c60ba 100644 (file)
@@ -1,9 +1,9 @@
 use crate::{
   activities::generate_activity_id,
-  activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
+  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, person::get_or_fetch_and_upsert_person},
+  fetcher::get_or_fetch_and_upsert_actor,
   generate_moderators_url,
   insert_activity,
   objects::ToApub,
@@ -13,7 +13,6 @@ use crate::{
 use activitystreams::{
   activity::{
     kind::{
-      AcceptType,
       AddType,
       AnnounceType,
       BlockType,
@@ -23,13 +22,10 @@ use activitystreams::{
       UndoType,
       UpdateType,
     },
-    Accept,
-    ActorAndObjectRefExt,
     Add,
     Announce,
     Block,
     Delete,
-    Follow,
     OptTargetRefExt,
     Remove,
     Undo,
@@ -81,31 +77,6 @@ impl CommunityType for Community {
     self.followers_url.clone().into()
   }
 
-  /// As a local community, accept the follow request from a remote person.
-  async fn send_accept_follow(
-    &self,
-    follow: Follow,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let actor_uri = follow
-      .actor()?
-      .as_single_xsd_any_uri()
-      .context(location_info!())?;
-    let person = get_or_fetch_and_upsert_person(actor_uri, context, &mut 0).await?;
-
-    let mut accept = Accept::new(
-      self.actor_id.to_owned().into_inner(),
-      follow.into_any_base()?,
-    );
-    accept
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(AcceptType::Accept)?)
-      .set_to(person.actor_id());
-
-    send_activity_single_dest(accept, self, person.inbox_url.into(), context).await?;
-    Ok(())
-  }
-
   /// 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> {
@@ -114,7 +85,7 @@ impl CommunityType for Community {
     } else {
       let mut update = Update::new(
         mod_.actor_id(),
-        self.to_apub(context.pool()).await?.into_any_base()?,
+        AnyBase::from_arbitrary_json(self.to_apub(context.pool()).await?)?,
       );
       update
         .set_many_contexts(lemmy_context())
index 65135bddaca339269d6b2f719b5294fa82d14e95..c00ecb1a7d89b382b60b758e7e375703a2bfb61a 100644 (file)
@@ -2,4 +2,3 @@ pub(crate) mod comment;
 pub(crate) mod community;
 pub(crate) mod person;
 pub(crate) mod post;
-pub(crate) mod private_message;
index b7b43c433d9d9eb2e2ccaa4c4d04c0cfdb117e4c..cb53bd6a93d3551206282b2e0d1008106b696009 100644 (file)
@@ -1,27 +1,5 @@
-use crate::{
-  activities::generate_activity_id,
-  activity_queue::send_activity_single_dest,
-  extensions::context::lemmy_context,
-  ActorType,
-  UserType,
-};
-use activitystreams::{
-  activity::{
-    kind::{FollowType, UndoType},
-    Follow,
-    Undo,
-  },
-  base::{BaseExt, ExtendsExt},
-  object::ObjectExt,
-};
-use lemmy_api_common::blocking;
-use lemmy_db_queries::{ApubObject, Followable};
-use lemmy_db_schema::source::{
-  community::{Community, CommunityFollower, CommunityFollowerForm},
-  person::Person,
-};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
+use crate::ActorType;
+use lemmy_db_schema::source::person::Person;
 use url::Url;
 
 impl ActorType for Person {
@@ -51,69 +29,3 @@ impl ActorType for Person {
       .into()
   }
 }
-
-#[async_trait::async_trait(?Send)]
-impl UserType for Person {
-  /// As a given local person, send out a follow request to a remote community.
-  async fn send_follow(
-    &self,
-    follow_actor_id: &Url,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let follow_actor_id = follow_actor_id.to_owned();
-    let community = blocking(context.pool(), move |conn| {
-      Community::read_from_apub_id(conn, &follow_actor_id.into())
-    })
-    .await??;
-
-    let community_follower_form = CommunityFollowerForm {
-      community_id: community.id,
-      person_id: self.id,
-      pending: true,
-    };
-    blocking(context.pool(), move |conn| {
-      CommunityFollower::follow(conn, &community_follower_form).ok()
-    })
-    .await?;
-
-    let mut follow = Follow::new(self.actor_id(), community.actor_id());
-    follow
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(FollowType::Follow)?)
-      .set_to(community.actor_id());
-
-    send_activity_single_dest(follow, self, community.inbox_url.into(), context).await?;
-    Ok(())
-  }
-
-  async fn send_unfollow(
-    &self,
-    follow_actor_id: &Url,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let follow_actor_id = follow_actor_id.to_owned();
-    let community = blocking(context.pool(), move |conn| {
-      Community::read_from_apub_id(conn, &follow_actor_id.into())
-    })
-    .await??;
-
-    let mut follow = Follow::new(self.actor_id(), community.actor_id());
-    follow
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(FollowType::Follow)?)
-      .set_to(community.actor_id());
-
-    // Undo that fake activity
-    let mut undo = Undo::new(
-      self.actor_id.to_owned().into_inner(),
-      follow.into_any_base()?,
-    );
-    undo
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(UndoType::Undo)?)
-      .set_to(community.actor_id());
-
-    send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?;
-    Ok(())
-  }
-}
diff --git a/crates/apub/src/activities/send/private_message.rs b/crates/apub/src/activities/send/private_message.rs
deleted file mode 100644 (file)
index edcff37..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-use crate::{
-  activities::generate_activity_id,
-  activity_queue::send_activity_single_dest,
-  extensions::context::lemmy_context,
-  ActorType,
-  ApubObjectType,
-};
-use activitystreams::{
-  activity::{
-    kind::{DeleteType, UndoType},
-    Delete,
-    Undo,
-  },
-  base::{BaseExt, ExtendsExt},
-  object::ObjectExt,
-};
-use lemmy_api_common::blocking;
-use lemmy_db_queries::Crud;
-use lemmy_db_schema::source::{person::Person, private_message::PrivateMessage};
-use lemmy_utils::LemmyError;
-use lemmy_websocket::LemmyContext;
-
-#[async_trait::async_trait(?Send)]
-impl ApubObjectType for PrivateMessage {
-  async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
-    let recipient_id = self.recipient_id;
-    let recipient =
-      blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
-
-    let mut delete = Delete::new(
-      creator.actor_id.to_owned().into_inner(),
-      self.ap_id.to_owned().into_inner(),
-    );
-    delete
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(DeleteType::Delete)?)
-      .set_to(recipient.actor_id());
-
-    send_activity_single_dest(delete, creator, recipient.inbox_url.into(), context).await?;
-    Ok(())
-  }
-
-  async fn send_undo_delete(
-    &self,
-    creator: &Person,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    let recipient_id = self.recipient_id;
-    let recipient =
-      blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
-
-    let mut delete = Delete::new(
-      creator.actor_id.to_owned().into_inner(),
-      self.ap_id.to_owned().into_inner(),
-    );
-    delete
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(DeleteType::Delete)?)
-      .set_to(recipient.actor_id());
-
-    // Undo that fake activity
-    let mut undo = Undo::new(
-      creator.actor_id.to_owned().into_inner(),
-      delete.into_any_base()?,
-    );
-    undo
-      .set_many_contexts(lemmy_context())
-      .set_id(generate_activity_id(UndoType::Undo)?)
-      .set_to(recipient.actor_id());
-
-    send_activity_single_dest(undo, creator, recipient.inbox_url.into(), context).await?;
-    Ok(())
-  }
-
-  async fn send_remove(&self, _mod_: &Person, _context: &LemmyContext) -> Result<(), LemmyError> {
-    unimplemented!()
-  }
-
-  async fn send_undo_remove(
-    &self,
-    _mod_: &Person,
-    _context: &LemmyContext,
-  ) -> Result<(), LemmyError> {
-    unimplemented!()
-  }
-}
index 8c3137e87dcd5e533c9d67d3f5554b8f09eae09d..5b5feac9d7246c6391f5056efa5463a24145773f 100644 (file)
@@ -101,7 +101,7 @@ impl ActivityHandler for UndoVote {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index 0f5a2b5c40231d1222f304e69e91b6cd505c0cc3..4183c2ad0a6005b0d80313ba38fc95722273fbca 100644 (file)
@@ -113,7 +113,7 @@ impl ActivityHandler for Vote {
   }
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
index c7ff0a81d30aaa01f7a6a53fb048485192e03780..48cccc612d9862ff4b40a556ba9c42483f7637e3 100644 (file)
@@ -31,34 +31,6 @@ use serde::{Deserialize, Serialize};
 use std::{collections::BTreeMap, env, fmt::Debug, future::Future, pin::Pin};
 use url::Url;
 
-/// Sends a local activity to a single, remote actor.
-///
-/// * `activity` the apub activity to be sent
-/// * `creator` the local actor which created the activity
-/// * `inbox` the inbox url where the activity should be delivered to
-pub(crate) async fn send_activity_single_dest<T, Kind>(
-  activity: T,
-  creator: &dyn ActorType,
-  inbox: 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 check_is_apub_id_valid(&inbox, false).is_ok() {
-    debug!(
-      "Sending activity {:?} to {}",
-      &activity.id_unchecked().map(ToString::to_string),
-      &inbox
-    );
-    send_activity_internal(context, activity, creator, vec![inbox], true, true).await?;
-  }
-
-  Ok(())
-}
-
 /// From a local community, send activity to all remote followers.
 ///
 /// * `activity` the apub activity to send
diff --git a/crates/apub/src/extensions/group_extension.rs b/crates/apub/src/extensions/group_extension.rs
deleted file mode 100644 (file)
index c83becf..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-use activitystreams::unparsed::UnparsedMutExt;
-use activitystreams_ext::UnparsedExtension;
-use lemmy_utils::LemmyError;
-use serde::{Deserialize, Serialize};
-use url::Url;
-
-/// Activitystreams extension to allow (de)serializing additional Community field
-/// `sensitive` (called 'nsfw' in Lemmy).
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct GroupExtension {
-  pub sensitive: Option<bool>,
-  pub moderators: Option<Url>,
-}
-
-impl GroupExtension {
-  pub fn new(sensitive: bool, moderators_url: Url) -> Result<GroupExtension, LemmyError> {
-    Ok(GroupExtension {
-      sensitive: Some(sensitive),
-      moderators: Some(moderators_url),
-    })
-  }
-}
-
-impl<U> UnparsedExtension<U> for GroupExtension
-where
-  U: UnparsedMutExt,
-{
-  type Error = serde_json::Error;
-
-  fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
-    Ok(GroupExtension {
-      sensitive: unparsed_mut.remove("sensitive")?,
-      moderators: unparsed_mut.remove("moderators")?,
-    })
-  }
-
-  fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
-    unparsed_mut.insert("sensitive", self.sensitive)?;
-    unparsed_mut.insert("moderators", self.moderators)?;
-    Ok(())
-  }
-}
index 781e89e6ec5541147730426a9b4df1f0eabba328..7c58789a964d1b8b73d4fc8a89de27ec839f1920 100644 (file)
@@ -1,4 +1,2 @@
 pub mod context;
-pub(crate) mod group_extension;
-pub(crate) mod person_extension;
 pub mod signatures;
diff --git a/crates/apub/src/extensions/person_extension.rs b/crates/apub/src/extensions/person_extension.rs
deleted file mode 100644 (file)
index 248adcc..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-use activitystreams::unparsed::UnparsedMutExt;
-use activitystreams_ext::UnparsedExtension;
-use lemmy_utils::LemmyError;
-use serde::{Deserialize, Serialize};
-
-/// Activitystreams extension to allow (de)serializing additional Person field
-/// `also_known_as` (used for Matrix profile link).
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct PersonExtension {
-  pub matrix_user_id: Option<String>,
-}
-
-impl PersonExtension {
-  pub fn new(matrix_user_id: Option<String>) -> Result<PersonExtension, LemmyError> {
-    Ok(PersonExtension { matrix_user_id })
-  }
-}
-
-impl<U> UnparsedExtension<U> for PersonExtension
-where
-  U: UnparsedMutExt,
-{
-  type Error = serde_json::Error;
-
-  fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
-    Ok(PersonExtension {
-      matrix_user_id: unparsed_mut.remove("matrix_user_id")?,
-    })
-  }
-
-  fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
-    unparsed_mut.insert("matrix_user_id", self.matrix_user_id)?;
-    Ok(())
-  }
-}
index be323d51b92b6134e9aece22639a1ec891794b46..8fded5152c82e6c65eb6d1f528a0889bde5bc3d8 100644 (file)
@@ -1,5 +1,3 @@
-use activitystreams::unparsed::UnparsedMutExt;
-use activitystreams_ext::UnparsedExtension;
 use actix_web::HttpRequest;
 use anyhow::anyhow;
 use http::{header::HeaderName, HeaderMap, HeaderValue};
@@ -90,15 +88,6 @@ pub fn verify_signature(request: &HttpRequest, public_key: &str) -> Result<(), L
   }
 }
 
-/// Extension for actor public key, which is needed on person and community for HTTP signatures.
-///
-/// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
-#[derive(Clone, Debug, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct PublicKeyExtension {
-  pub public_key: PublicKey,
-}
-
 #[derive(Clone, Debug, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PublicKey {
@@ -106,29 +95,3 @@ pub struct PublicKey {
   pub owner: Url,
   pub public_key_pem: String,
 }
-
-impl PublicKey {
-  pub fn to_ext(&self) -> PublicKeyExtension {
-    PublicKeyExtension {
-      public_key: self.to_owned(),
-    }
-  }
-}
-
-impl<U> UnparsedExtension<U> for PublicKeyExtension
-where
-  U: UnparsedMutExt,
-{
-  type Error = serde_json::Error;
-
-  fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
-    Ok(PublicKeyExtension {
-      public_key: unparsed_mut.remove("publicKey")?,
-    })
-  }
-
-  fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
-    unparsed_mut.insert("publicKey", self.public_key)?;
-    Ok(())
-  }
-}
index 670493a98539aadd544f224914894854432a6f4e..ff94129bed3e87df73aba1b9a3f2093ed267140d 100644 (file)
@@ -6,13 +6,9 @@ use crate::{
     person::get_or_fetch_and_upsert_person,
     should_refetch_actor,
   },
-  objects::FromApub,
-  GroupExt,
-};
-use activitystreams::{
-  actor::ApActorExt,
-  collection::{CollectionExt, OrderedCollection},
+  objects::{community::Group, FromApub},
 };
+use activitystreams::collection::{CollectionExt, OrderedCollection};
 use anyhow::Context;
 use diesel::result::Error::NotFound;
 use lemmy_api_common::blocking;
@@ -63,7 +59,7 @@ async fn fetch_remote_community(
   old_community: Option<Community>,
   request_counter: &mut i32,
 ) -> Result<Community, LemmyError> {
-  let group = fetch_remote_object::<GroupExt>(context.client(), apub_id, request_counter).await;
+  let group = fetch_remote_object::<Group>(context.client(), apub_id, request_counter).await;
 
   if let Some(c) = old_community.to_owned() {
     if is_deleted(&group) {
@@ -78,22 +74,20 @@ async fn fetch_remote_community(
   }
 
   let group = group?;
-  let community =
-    Community::from_apub(&group, context, apub_id.to_owned(), request_counter, false).await?;
+  let community = Community::from_apub(&group, context, apub_id, request_counter).await?;
 
   update_community_mods(&group, &community, context, request_counter).await?;
 
   // only fetch outbox for new communities, otherwise this can create an infinite loop
   if old_community.is_none() {
-    let outbox = group.inner.outbox()?.context(location_info!())?;
-    fetch_community_outbox(context, outbox, request_counter).await?
+    fetch_community_outbox(context, &group.outbox, request_counter).await?
   }
 
   Ok(community)
 }
 
 async fn update_community_mods(
-  group: &GroupExt,
+  group: &Group,
   community: &Community,
   context: &LemmyContext,
   request_counter: &mut i32,
@@ -168,10 +162,10 @@ async fn fetch_community_outbox(
 
 pub(crate) async fn fetch_community_mods(
   context: &LemmyContext,
-  group: &GroupExt,
+  group: &Group,
   recursion_counter: &mut i32,
 ) -> Result<Vec<Url>, LemmyError> {
-  if let Some(mods_url) = &group.ext_one.moderators {
+  if let Some(mods_url) = &group.moderators {
     let mods =
       fetch_remote_object::<OrderedCollection>(context.client(), mods_url, recursion_counter)
         .await?;
index a06b99d6394cc3e117c7a3bc2716d7c496abc733..835bf63a3199763cd5f9ec4c52a8d27a49606c32 100644 (file)
@@ -34,14 +34,7 @@ pub async fn get_or_fetch_and_insert_post(
       debug!("Fetching and creating remote post: {}", post_ap_id);
       let page =
         fetch_remote_object::<Page>(context.client(), post_ap_id, recursion_counter).await?;
-      let post = Post::from_apub(
-        &page,
-        context,
-        post_ap_id.to_owned(),
-        recursion_counter,
-        false,
-      )
-      .await?;
+      let post = Post::from_apub(&page, context, post_ap_id, recursion_counter).await?;
 
       Ok(post)
     }
@@ -73,14 +66,7 @@ pub async fn get_or_fetch_and_insert_comment(
       );
       let comment =
         fetch_remote_object::<Note>(context.client(), comment_ap_id, recursion_counter).await?;
-      let comment = Comment::from_apub(
-        &comment,
-        context,
-        comment_ap_id.to_owned(),
-        recursion_counter,
-        false,
-      )
-      .await?;
+      let comment = Comment::from_apub(&comment, context, comment_ap_id, recursion_counter).await?;
 
       let post_id = comment.post_id;
       let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
index 4e6191abb035a25777b19effdaec3b45bc6ac577..3acedb0863f183b27bd67149b8bc6250d15024b7 100644 (file)
@@ -1,7 +1,6 @@
 use crate::{
   fetcher::{fetch::fetch_remote_object, is_deleted, should_refetch_actor},
-  objects::FromApub,
-  PersonExt,
+  objects::{person::Person as ApubPerson, FromApub},
 };
 use anyhow::anyhow;
 use diesel::result::Error::NotFound;
@@ -33,7 +32,7 @@ pub async fn get_or_fetch_and_upsert_person(
     Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
       debug!("Fetching and updating from remote person: {}", apub_id);
       let person =
-        fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await;
+        fetch_remote_object::<ApubPerson>(context.client(), apub_id, recursion_counter).await;
 
       if is_deleted(&person) {
         // TODO: use Person::update_deleted() once implemented
@@ -46,14 +45,7 @@ pub async fn get_or_fetch_and_upsert_person(
         return Ok(u);
       }
 
-      let person = Person::from_apub(
-        &person?,
-        context,
-        apub_id.to_owned(),
-        recursion_counter,
-        false,
-      )
-      .await?;
+      let person = Person::from_apub(&person?, context, apub_id, recursion_counter).await?;
 
       let person_id = person.id;
       blocking(context.pool(), move |conn| {
@@ -67,16 +59,9 @@ pub async fn get_or_fetch_and_upsert_person(
     Err(NotFound {}) => {
       debug!("Fetching and creating remote person: {}", apub_id);
       let person =
-        fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
+        fetch_remote_object::<ApubPerson>(context.client(), apub_id, recursion_counter).await?;
 
-      let person = Person::from_apub(
-        &person,
-        context,
-        apub_id.to_owned(),
-        recursion_counter,
-        false,
-      )
-      .await?;
+      let person = Person::from_apub(&person, context, apub_id, recursion_counter).await?;
 
       Ok(person)
     }
index 647d057a66193254c8f4312e8b281460ed11d77a..2c9bebb0284a51e95e8d43c6b112b2aa63274b09 100644 (file)
@@ -6,13 +6,10 @@ use crate::{
     is_deleted,
   },
   find_object_by_id,
-  objects::{comment::Note, post::Page, FromApub},
-  GroupExt,
+  objects::{comment::Note, community::Group, person::Person as ApubPerson, post::Page, FromApub},
   Object,
-  PersonExt,
 };
-use activitystreams::base::BaseExt;
-use anyhow::{anyhow, Context};
+use anyhow::anyhow;
 use lemmy_api_common::{blocking, site::SearchResponse};
 use lemmy_db_queries::{
   source::{
@@ -42,8 +39,8 @@ use url::Url;
 #[derive(serde::Deserialize, Debug)]
 #[serde(untagged)]
 enum SearchAcceptedObjects {
-  Person(Box<PersonExt>),
-  Group(Box<GroupExt>),
+  Person(Box<ApubPerson>),
+  Group(Box<Group>),
   Page(Box<Page>),
   Comment(Box<Note>),
 }
@@ -109,7 +106,6 @@ async fn build_response(
   recursion_counter: &mut i32,
   context: &LemmyContext,
 ) -> Result<SearchResponse, LemmyError> {
-  let domain = query_url.domain().context("url has no domain")?;
   let mut response = SearchResponse {
     type_: SearchType::All.to_string(),
     comments: vec![],
@@ -120,9 +116,8 @@ async fn build_response(
 
   match fetch_response {
     SearchAcceptedObjects::Person(p) => {
-      let person_uri = p.inner.id(domain)?.context("person has no id")?;
-
-      let person = get_or_fetch_and_upsert_person(person_uri, context, recursion_counter).await?;
+      let person_id = p.id(&query_url)?;
+      let person = get_or_fetch_and_upsert_person(person_id, context, recursion_counter).await?;
 
       response.users = vec![
         blocking(context.pool(), move |conn| {
@@ -132,8 +127,7 @@ async fn build_response(
       ];
     }
     SearchAcceptedObjects::Group(g) => {
-      let community_uri = g.inner.id(domain)?.context("group has no id")?;
-
+      let community_uri = g.id(&query_url)?;
       let community =
         get_or_fetch_and_upsert_community(community_uri, context, recursion_counter).await?;
 
@@ -145,13 +139,13 @@ async fn build_response(
       ];
     }
     SearchAcceptedObjects::Page(p) => {
-      let p = Post::from_apub(&p, context, query_url, recursion_counter, false).await?;
+      let p = Post::from_apub(&p, context, &query_url, recursion_counter).await?;
 
       response.posts =
         vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
     }
     SearchAcceptedObjects::Comment(c) => {
-      let c = Comment::from_apub(&c, context, query_url, recursion_counter, false).await?;
+      let c = Comment::from_apub(&c, context, &query_url, recursion_counter).await?;
 
       response.comments = vec![
         blocking(context.pool(), move |conn| {
index 75f97aadd8ca57b4fe54ec90642a7a5268764d5b..950f11dc414fc8229771c6ef69f805d8d37c16b6 100644 (file)
@@ -9,21 +9,8 @@ pub mod http;
 pub mod migrations;
 pub mod objects;
 
-use crate::{
-  extensions::{
-    group_extension::GroupExtension,
-    person_extension::PersonExtension,
-    signatures::{PublicKey, PublicKeyExtension},
-  },
-  fetcher::community::get_or_fetch_and_upsert_community,
-};
-use activitystreams::{
-  activity::Follow,
-  actor,
-  base::AnyBase,
-  object::{ApObject, AsObject, ObjectExt},
-};
-use activitystreams_ext::Ext2;
+use crate::extensions::signatures::PublicKey;
+use activitystreams::base::AnyBase;
 use anyhow::{anyhow, Context};
 use diesel::NotFound;
 use lemmy_api_common::blocking;
@@ -47,20 +34,6 @@ use serde::Serialize;
 use std::net::IpAddr;
 use url::{ParseError, Url};
 
-/// Activitystreams type for community
-pub type GroupExt =
-  Ext2<actor::ApActor<ApObject<actor::Group>>, GroupExtension, PublicKeyExtension>;
-/// Activitystreams type for person
-type PersonExt =
-  Ext2<actor::ApActor<ApObject<actor::Actor<UserTypes>>>, PersonExtension, PublicKeyExtension>;
-pub type SiteExt = actor::ApActor<ApObject<actor::Service>>;
-
-#[derive(Clone, Copy, Debug, serde::Deserialize, serde::Serialize, PartialEq)]
-pub enum UserTypes {
-  Person,
-  Service,
-}
-
 pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
 
 /// Checks if the ID is allowed for sending or receiving.
@@ -71,7 +44,10 @@ pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
 /// - URL being in the allowlist (if it is active)
 /// - URL not being in the blocklist (if it is active)
 ///
-pub fn check_is_apub_id_valid(apub_id: &Url, use_strict_allowlist: bool) -> Result<(), LemmyError> {
+pub(crate) fn check_is_apub_id_valid(
+  apub_id: &Url,
+  use_strict_allowlist: bool,
+) -> Result<(), LemmyError> {
   let settings = Settings::get();
   let domain = apub_id.domain().context(location_info!())?.to_string();
   let local_instance = settings.get_hostname_without_port()?;
@@ -169,15 +145,12 @@ pub trait ActorType {
     Ok(Url::parse(&format!("{}/outbox", &self.actor_id()))?)
   }
 
-  fn get_public_key_ext(&self) -> Result<PublicKeyExtension, LemmyError> {
-    Ok(
-      PublicKey {
-        id: format!("{}#main-key", self.actor_id()),
-        owner: self.actor_id(),
-        public_key_pem: self.public_key().context(location_info!())?,
-      }
-      .to_ext(),
-    )
+  fn get_public_key(&self) -> Result<PublicKey, LemmyError> {
+    Ok(PublicKey {
+      id: format!("{}#main-key", self.actor_id()),
+      owner: self.actor_id(),
+      public_key_pem: self.public_key().context(location_info!())?,
+    })
   }
 }
 
@@ -185,11 +158,6 @@ pub 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_accept_follow(
-    &self,
-    follow: Follow,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
 
   async fn send_update(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
   async fn send_delete(&self, mod_: Person, context: &LemmyContext) -> Result<(), LemmyError>;
@@ -232,20 +200,6 @@ pub trait CommunityType {
   ) -> Result<(), LemmyError>;
 }
 
-#[async_trait::async_trait(?Send)]
-pub trait UserType {
-  async fn send_follow(
-    &self,
-    follow_actor_id: &Url,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-  async fn send_unfollow(
-    &self,
-    follow_actor_id: &Url,
-    context: &LemmyContext,
-  ) -> Result<(), LemmyError>;
-}
-
 pub enum EndpointType {
   Community,
   Person,
@@ -255,7 +209,7 @@ pub enum EndpointType {
 }
 
 /// Generates an apub endpoint for a given domain, IE xyz.tld
-pub fn generate_apub_endpoint_for_domain(
+pub(crate) fn generate_apub_endpoint_for_domain(
   endpoint_type: EndpointType,
   name: &str,
   domain: &str,
@@ -332,7 +286,7 @@ pub fn build_actor_id_from_shortname(
 
 /// Store a sent or received activity in the database, for logging purposes. These records are not
 /// persistent.
-pub async fn insert_activity<T>(
+pub(crate) async fn insert_activity<T>(
   ap_id: &Url,
   activity: T,
   local: bool,
@@ -368,7 +322,7 @@ impl PostOrComment {
 /// Tries to find a post or comment in the local database, without any network requests.
 /// This is used to handle deletions and removals, because in case we dont have the object, we can
 /// simply ignore the activity.
-pub async fn find_post_or_comment_by_id(
+pub(crate) async fn find_post_or_comment_by_id(
   context: &LemmyContext,
   apub_id: Url,
 ) -> Result<PostOrComment, LemmyError> {
@@ -402,7 +356,10 @@ pub enum Object {
   PrivateMessage(Box<PrivateMessage>),
 }
 
-pub async fn find_object_by_id(context: &LemmyContext, apub_id: Url) -> Result<Object, LemmyError> {
+pub(crate) async fn find_object_by_id(
+  context: &LemmyContext,
+  apub_id: Url,
+) -> Result<Object, LemmyError> {
   let ap_id = apub_id.clone();
   if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
     return Ok(match pc {
@@ -440,7 +397,7 @@ pub async fn find_object_by_id(context: &LemmyContext, apub_id: Url) -> Result<O
   Err(NotFound.into())
 }
 
-pub async fn check_community_or_site_ban(
+pub(crate) async fn check_community_or_site_ban(
   person: &Person,
   community_id: CommunityId,
   pool: &DbPool,
@@ -457,48 +414,3 @@ pub async fn check_community_or_site_ban(
 
   Ok(())
 }
-
-pub fn get_activity_to_and_cc<T, Kind>(activity: &T) -> Vec<Url>
-where
-  T: AsObject<Kind>,
-{
-  let mut to_and_cc = vec![];
-  if let Some(to) = activity.to() {
-    let to = to.to_owned().unwrap_to_vec();
-    let mut to = to
-      .iter()
-      .map(|t| t.as_xsd_any_uri())
-      .flatten()
-      .map(|t| t.to_owned())
-      .collect();
-    to_and_cc.append(&mut to);
-  }
-  if let Some(cc) = activity.cc() {
-    let cc = cc.to_owned().unwrap_to_vec();
-    let mut cc = cc
-      .iter()
-      .map(|c| c.as_xsd_any_uri())
-      .flatten()
-      .map(|c| c.to_owned())
-      .collect();
-    to_and_cc.append(&mut cc);
-  }
-  to_and_cc
-}
-
-pub async fn get_community_from_to_or_cc<T, Kind>(
-  activity: &T,
-  context: &LemmyContext,
-  request_counter: &mut i32,
-) -> Result<Community, LemmyError>
-where
-  T: AsObject<Kind>,
-{
-  for cid in get_activity_to_and_cc(activity) {
-    let community = get_or_fetch_and_upsert_community(&cid, context, request_counter).await;
-    if community.is_ok() {
-      return community;
-    }
-  }
-  Err(NotFound.into())
-}
index d402613a002cd1295862a09ad82e53ca01cfc0eb..1bebb39e99bbd8756ecc7502c9c5c1e08992e2a6 100644 (file)
@@ -41,16 +41,18 @@ use lemmy_utils::{
 };
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use std::ops::Deref;
 use url::Url;
 
+#[skip_serializing_none]
 #[derive(Clone, Debug, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Note {
   #[serde(rename = "@context")]
   context: OneOrMany<AnyBase>,
   r#type: NoteType,
-  pub(crate) id: Url,
+  id: Url,
   pub(crate) attributed_to: Url,
   /// Indicates that the object is publicly readable. Unlike [`Post.to`], this one doesn't contain
   /// the community ID, as it would be incompatible with Pleroma (and we can get the community from
@@ -67,6 +69,14 @@ pub struct Note {
 }
 
 impl Note {
+  pub(crate) fn id_unchecked(&self) -> &Url {
+    &self.id
+  }
+  pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
+    verify_domains_match(&self.id, expected_domain)?;
+    Ok(&self.id)
+  }
+
   async fn get_parents(
     &self,
     context: &LemmyContext,
@@ -212,10 +222,10 @@ impl FromApub for Comment {
   async fn from_apub(
     note: &Note,
     context: &LemmyContext,
-    _expected_domain: Url,
+    expected_domain: &Url,
     request_counter: &mut i32,
-    _mod_action_allowed: bool,
   ) -> Result<Comment, LemmyError> {
+    let ap_id = Some(note.id(expected_domain)?.clone().into());
     let creator =
       get_or_fetch_and_upsert_person(&note.attributed_to, context, request_counter).await?;
     let (post, parent_comment_id) = note.get_parents(context, request_counter).await?;
@@ -233,7 +243,7 @@ impl FromApub for Comment {
       published: Some(note.published.naive_local()),
       updated: note.updated.map(|u| u.to_owned().naive_local()),
       deleted: None,
-      ap_id: Some(note.id.clone().into()),
+      ap_id,
       local: Some(false),
     };
     Ok(blocking(context.pool(), move |conn| Comment::upsert(conn, &form)).await??)
index f9948588d5e9b6b322680ee1d3f8ee35fcf2f31d..5269d76630c3add1701d1509bf7f2bcf05b09d62 100644 (file)
 use crate::{
-  extensions::{context::lemmy_context, group_extension::GroupExtension},
+  extensions::{context::lemmy_context, signatures::PublicKey},
   fetcher::community::fetch_community_mods,
   generate_moderators_url,
-  objects::{
-    check_object_domain,
-    create_tombstone,
-    get_object_from_apub,
-    get_source_markdown_value,
-    set_content_and_source,
-    FromApub,
-    FromApubToForm,
-    ToApub,
-  },
+  objects::{create_tombstone, FromApub, ImageObject, Source, ToApub},
   ActorType,
-  GroupExt,
 };
 use activitystreams::{
-  actor::{kind::GroupType, ApActor, Endpoints, Group},
-  base::BaseExt,
-  object::{ApObject, Image, Tombstone},
-  prelude::*,
+  actor::{kind::GroupType, Endpoints},
+  base::AnyBase,
+  object::{kind::ImageType, Tombstone},
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams_ext::Ext2;
-use anyhow::Context;
+use chrono::{DateTime, FixedOffset};
 use lemmy_api_common::blocking;
-use lemmy_db_queries::DbPool;
+use lemmy_apub_lib::{
+  values::{MediaTypeHtml, MediaTypeMarkdown},
+  verify_domains_match,
+};
+use lemmy_db_queries::{ApubObject, DbPool};
 use lemmy_db_schema::{
   naive_now,
   source::community::{Community, CommunityForm},
 };
-use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
 use lemmy_utils::{
-  location_info,
-  utils::{check_slurs, check_slurs_opt, convert_datetime},
+  utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
   LemmyError,
 };
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use url::Url;
 
-#[async_trait::async_trait(?Send)]
-impl ToApub for Community {
-  type ApubType = GroupExt;
-
-  async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
-    let id = self.id;
-    let moderators = blocking(pool, move |conn| {
-      CommunityModeratorView::for_community(conn, id)
-    })
-    .await??;
-    let moderators: Vec<Url> = moderators
-      .into_iter()
-      .map(|m| m.moderator.actor_id.into_inner())
-      .collect();
-
-    let mut group = ApObject::new(Group::new());
-    group
-      .set_many_contexts(lemmy_context())
-      .set_id(self.actor_id.to_owned().into())
-      .set_name(self.title.to_owned())
-      .set_published(convert_datetime(self.published))
-      // NOTE: included attritubed_to field for compatibility with lemmy v0.9.9
-      .set_many_attributed_tos(moderators);
+#[skip_serializing_none]
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Group {
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
+  #[serde(rename = "type")]
+  kind: GroupType,
+  id: Url,
+  /// username, set at account creation and can never be changed
+  preferred_username: String,
+  /// title (can be changed at any time)
+  name: String,
+  content: Option<String>,
+  media_type: Option<MediaTypeHtml>,
+  source: Option<Source>,
+  icon: Option<ImageObject>,
+  /// banner
+  image: Option<ImageObject>,
+  // lemmy extension
+  sensitive: Option<bool>,
+  // lemmy extension
+  pub(crate) moderators: Option<Url>,
+  inbox: Url,
+  pub(crate) outbox: Url,
+  followers: Url,
+  endpoints: Endpoints<Url>,
+  public_key: PublicKey,
+  published: DateTime<FixedOffset>,
+  updated: Option<DateTime<FixedOffset>>,
+  #[serde(flatten)]
+  unparsed: Unparsed,
+}
 
-    if let Some(u) = self.updated.to_owned() {
-      group.set_updated(convert_datetime(u));
-    }
-    if let Some(d) = self.description.to_owned() {
-      set_content_and_source(&mut group, &d)?;
-    }
+impl Group {
+  pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
+    verify_domains_match(&self.id, expected_domain)?;
+    Ok(&self.id)
+  }
+  pub(crate) async fn from_apub_to_form(
+    group: &Group,
+    expected_domain: &Url,
+  ) -> Result<CommunityForm, LemmyError> {
+    let actor_id = Some(group.id(expected_domain)?.clone().into());
+    let name = group.preferred_username.clone();
+    let title = group.name.clone();
+    let description = group.source.clone().map(|s| s.content);
+    let shared_inbox = group.endpoints.shared_inbox.clone().map(|s| s.into());
 
-    if let Some(icon_url) = &self.icon {
-      let mut image = Image::new();
-      image.set_url::<Url>(icon_url.to_owned().into());
-      group.set_icon(image.into_any_base()?);
-    }
+    check_slurs(&name)?;
+    check_slurs(&title)?;
+    check_slurs_opt(&description)?;
 
-    if let Some(banner_url) = &self.banner {
-      let mut image = Image::new();
-      image.set_url::<Url>(banner_url.to_owned().into());
-      group.set_image(image.into_any_base()?);
-    }
+    Ok(CommunityForm {
+      name,
+      title,
+      description,
+      removed: None,
+      published: Some(group.published.naive_local()),
+      updated: group.updated.map(|u| u.naive_local()),
+      deleted: None,
+      nsfw: Some(group.sensitive.unwrap_or(false)),
+      actor_id,
+      local: Some(false),
+      private_key: None,
+      public_key: Some(group.public_key.public_key_pem.clone()),
+      last_refreshed_at: Some(naive_now()),
+      icon: Some(group.icon.clone().map(|i| i.url.into())),
+      banner: Some(group.image.clone().map(|i| i.url.into())),
+      followers_url: Some(group.followers.clone().into()),
+      inbox_url: Some(group.inbox.clone().into()),
+      shared_inbox_url: Some(shared_inbox),
+    })
+  }
+}
 
-    let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), group);
-    ap_actor
-      .set_preferred_username(self.name.to_owned())
-      .set_outbox(self.get_outbox_url()?)
-      .set_followers(self.followers_url.clone().into())
-      .set_endpoints(Endpoints {
-        shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
+#[async_trait::async_trait(?Send)]
+impl ToApub for Community {
+  type ApubType = Group;
+
+  async fn to_apub(&self, _pool: &DbPool) -> Result<Group, LemmyError> {
+    let source = self.description.clone().map(|bio| Source {
+      content: bio,
+      media_type: MediaTypeMarkdown::Markdown,
+    });
+    let icon = self.icon.clone().map(|url| ImageObject {
+      kind: ImageType::Image,
+      url: url.into(),
+    });
+    let image = self.banner.clone().map(|url| ImageObject {
+      kind: ImageType::Image,
+      url: url.into(),
+    });
+
+    let group = Group {
+      context: lemmy_context(),
+      kind: GroupType::Group,
+      id: self.actor_id(),
+      preferred_username: self.name.clone(),
+      name: self.title.clone(),
+      content: self.description.as_ref().map(|b| markdown_to_html(b)),
+      media_type: self.description.as_ref().map(|_| MediaTypeHtml::Html),
+      source,
+      icon,
+      image,
+      sensitive: Some(self.nsfw),
+      moderators: Some(generate_moderators_url(&self.actor_id)?.into()),
+      inbox: self.inbox_url.clone().into(),
+      outbox: self.get_outbox_url()?,
+      followers: self.followers_url.clone().into(),
+      endpoints: Endpoints {
+        shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
         ..Default::default()
-      });
-
-    Ok(Ext2::new(
-      ap_actor,
-      GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
-      self.get_public_key_ext()?,
-    ))
+      },
+      public_key: self.get_public_key()?,
+      published: convert_datetime(self.published),
+      updated: self.updated.map(convert_datetime),
+      unparsed: Default::default(),
+    };
+    Ok(group)
   }
 
   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
@@ -110,116 +166,19 @@ impl ToApub for Community {
 
 #[async_trait::async_trait(?Send)]
 impl FromApub for Community {
-  type ApubType = GroupExt;
+  type ApubType = Group;
 
   /// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
   async fn from_apub(
-    group: &GroupExt,
+    group: &Group,
     context: &LemmyContext,
-    expected_domain: Url,
+    expected_domain: &Url,
     request_counter: &mut i32,
-    mod_action_allowed: bool,
   ) -> Result<Community, LemmyError> {
-    get_object_from_apub(
-      group,
-      context,
-      expected_domain,
-      request_counter,
-      mod_action_allowed,
-    )
-    .await
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl FromApubToForm<GroupExt> for CommunityForm {
-  async fn from_apub(
-    group: &GroupExt,
-    context: &LemmyContext,
-    expected_domain: Url,
-    request_counter: &mut i32,
-    _mod_action_allowed: bool,
-  ) -> Result<Self, LemmyError> {
     fetch_community_mods(context, group, request_counter).await?;
+    let form = Group::from_apub_to_form(group, expected_domain).await?;
 
-    let name = group
-      .inner
-      .preferred_username()
-      .context(location_info!())?
-      .to_string();
-    let title = group
-      .inner
-      .name()
-      .context(location_info!())?
-      .as_one()
-      .context(location_info!())?
-      .as_xsd_string()
-      .context(location_info!())?
-      .to_string();
-
-    let description = get_source_markdown_value(group)?;
-
-    check_slurs(&name)?;
-    check_slurs(&title)?;
-    check_slurs_opt(&description)?;
-
-    let icon = match group.icon() {
-      Some(any_image) => Some(
-        Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
-          .context(location_info!())?
-          .context(location_info!())?
-          .url()
-          .context(location_info!())?
-          .as_single_xsd_any_uri()
-          .map(|u| u.to_owned().into()),
-      ),
-      None => None,
-    };
-    let banner = match group.image() {
-      Some(any_image) => Some(
-        Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
-          .context(location_info!())?
-          .context(location_info!())?
-          .url()
-          .context(location_info!())?
-          .as_single_xsd_any_uri()
-          .map(|u| u.to_owned().into()),
-      ),
-      None => None,
-    };
-    let shared_inbox = group
-      .inner
-      .endpoints()?
-      .map(|e| e.shared_inbox)
-      .flatten()
-      .map(|s| s.to_owned().into());
-
-    Ok(CommunityForm {
-      name,
-      title,
-      description,
-      removed: None,
-      published: group.inner.published().map(|u| u.to_owned().naive_local()),
-      updated: group.inner.updated().map(|u| u.to_owned().naive_local()),
-      deleted: None,
-      nsfw: Some(group.ext_one.sensitive.unwrap_or(false)),
-      actor_id: Some(check_object_domain(group, expected_domain, true)?),
-      local: Some(false),
-      private_key: None,
-      public_key: Some(group.ext_two.to_owned().public_key.public_key_pem),
-      last_refreshed_at: Some(naive_now()),
-      icon,
-      banner,
-      followers_url: Some(
-        group
-          .inner
-          .followers()?
-          .context(location_info!())?
-          .to_owned()
-          .into(),
-      ),
-      inbox_url: Some(group.inner.inbox()?.to_owned().into()),
-      shared_inbox_url: Some(shared_inbox),
-    })
+    let community = blocking(context.pool(), move |conn| Community::upsert(conn, &form)).await??;
+    Ok(community)
   }
 }
index 0a29f29a36855d2ffd1e1309f7ddb1ab49e7a1c1..2114a1d89ae8436267edb2ae92265668fbeea710 100644 (file)
@@ -1,22 +1,13 @@
-use crate::{check_is_apub_id_valid, fetcher::person::get_or_fetch_and_upsert_person};
+use crate::fetcher::person::get_or_fetch_and_upsert_person;
 use activitystreams::{
-  base::{AsBase, BaseExt, ExtendsExt},
-  markers::Base,
-  mime::{FromStrError, Mime},
-  object::{ApObjectExt, Object, ObjectExt, Tombstone, TombstoneExt},
+  base::BaseExt,
+  object::{kind::ImageType, Tombstone, TombstoneExt},
 };
-use anyhow::{anyhow, Context};
+use anyhow::anyhow;
 use chrono::NaiveDateTime;
-use lemmy_api_common::blocking;
 use lemmy_apub_lib::values::MediaTypeMarkdown;
-use lemmy_db_queries::{ApubObject, Crud, DbPool};
-use lemmy_db_schema::DbUrl;
-use lemmy_utils::{
-  location_info,
-  settings::structs::Settings,
-  utils::{convert_datetime, markdown_to_html},
-  LemmyError,
-};
+use lemmy_db_queries::DbPool;
+use lemmy_utils::{utils::convert_datetime, LemmyError};
 use lemmy_websocket::LemmyContext;
 use url::Url;
 
@@ -46,22 +37,8 @@ pub trait FromApub {
   async fn from_apub(
     apub: &Self::ApubType,
     context: &LemmyContext,
-    expected_domain: Url,
+    expected_domain: &Url,
     request_counter: &mut i32,
-    mod_action_allowed: bool,
-  ) -> Result<Self, LemmyError>
-  where
-    Self: Sized;
-}
-
-#[async_trait::async_trait(?Send)]
-pub trait FromApubToForm<ApubType> {
-  async fn from_apub(
-    apub: &ApubType,
-    context: &LemmyContext,
-    expected_domain: Url,
-    request_counter: &mut i32,
-    mod_action_allowed: bool,
   ) -> Result<Self, LemmyError>
   where
     Self: Sized;
@@ -74,6 +51,14 @@ pub struct Source {
   media_type: MediaTypeMarkdown,
 }
 
+#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct ImageObject {
+  #[serde(rename = "type")]
+  kind: ImageType,
+  url: Url,
+}
+
 /// Updated is actually the deletion time
 fn create_tombstone<T>(
   deleted: bool,
@@ -98,120 +83,3 @@ where
     Err(anyhow!("Cant convert object to tombstone if it wasnt deleted").into())
   }
 }
-
-pub(in crate::objects) fn check_object_domain<T, Kind>(
-  apub: &T,
-  expected_domain: Url,
-  use_strict_allowlist: bool,
-) -> Result<DbUrl, LemmyError>
-where
-  T: Base + AsBase<Kind>,
-{
-  let domain = expected_domain.domain().context(location_info!())?;
-  let object_id = apub.id(domain)?.context(location_info!())?;
-  check_is_apub_id_valid(object_id, use_strict_allowlist)?;
-  Ok(object_id.to_owned().into())
-}
-
-pub(in crate::objects) fn set_content_and_source<T, Kind1, Kind2>(
-  object: &mut T,
-  markdown_text: &str,
-) -> Result<(), LemmyError>
-where
-  T: ApObjectExt<Kind1> + ObjectExt<Kind2> + AsBase<Kind2>,
-{
-  let mut source = Object::<()>::new_none_type();
-  source
-    .set_content(markdown_text)
-    .set_media_type(mime_markdown()?);
-  object.set_source(source.into_any_base()?);
-
-  object.set_content(markdown_to_html(markdown_text));
-  object.set_media_type(mime_html()?);
-  Ok(())
-}
-
-pub(in crate::objects) fn get_source_markdown_value<T, Kind1, Kind2>(
-  object: &T,
-) -> Result<Option<String>, LemmyError>
-where
-  T: ApObjectExt<Kind1> + ObjectExt<Kind2> + AsBase<Kind2>,
-{
-  let content = object
-    .content()
-    .map(|s| s.as_single_xsd_string().map(|s2| s2.to_string()))
-    .flatten();
-  if content.is_some() {
-    let source = object.source().context(location_info!())?;
-    let source = Object::<()>::from_any_base(source.to_owned())?.context(location_info!())?;
-    check_is_markdown(source.media_type())?;
-    let source_content = source
-      .content()
-      .map(|s| s.as_single_xsd_string().map(|s2| s2.to_string()))
-      .flatten()
-      .context(location_info!())?;
-    return Ok(Some(source_content));
-  }
-  Ok(None)
-}
-
-fn mime_markdown() -> Result<Mime, FromStrError> {
-  "text/markdown".parse()
-}
-
-fn mime_html() -> Result<Mime, FromStrError> {
-  "text/html".parse()
-}
-
-pub(in crate::objects) fn check_is_markdown(mime: Option<&Mime>) -> Result<(), LemmyError> {
-  let mime = mime.context(location_info!())?;
-  if !mime.eq(&mime_markdown()?) {
-    Err(LemmyError::from(anyhow!(
-      "Lemmy only supports markdown content"
-    )))
-  } else {
-    Ok(())
-  }
-}
-
-/// Converts an ActivityPub object (eg `Note`) to a database object (eg `Comment`). If an object
-/// with the same ActivityPub ID already exists in the database, it is returned directly. Otherwise
-/// the apub object is parsed, inserted and returned.
-pub async fn get_object_from_apub<From, Kind, To, ToForm, IdType>(
-  from: &From,
-  context: &LemmyContext,
-  expected_domain: Url,
-  request_counter: &mut i32,
-  is_mod_action: bool,
-) -> Result<To, LemmyError>
-where
-  From: BaseExt<Kind>,
-  To: ApubObject<ToForm> + Crud<ToForm, IdType> + Send + 'static,
-  ToForm: FromApubToForm<From> + Send + 'static,
-{
-  let object_id = from.id_unchecked().context(location_info!())?.to_owned();
-  let domain = object_id.domain().context(location_info!())?;
-
-  // if its a local object, return it directly from the database
-  if Settings::get().hostname == domain {
-    let object = blocking(context.pool(), move |conn| {
-      To::read_from_apub_id(conn, &object_id.into())
-    })
-    .await??;
-    Ok(object)
-  }
-  // otherwise parse and insert, assuring that it comes from the right domain
-  else {
-    let to_form = ToForm::from_apub(
-      from,
-      context,
-      expected_domain,
-      request_counter,
-      is_mod_action,
-    )
-    .await?;
-
-    let to = blocking(context.pool(), move |conn| To::upsert(conn, &to_form)).await??;
-    Ok(to)
-  }
-}
index cbc80b505a7053124b79c33af35199f5af6f3065..ec14a943edcbc18b7005c0f202cdecae5cff6167 100644 (file)
 use crate::{
-  extensions::{context::lemmy_context, person_extension::PersonExtension},
-  objects::{
-    check_object_domain,
-    get_source_markdown_value,
-    set_content_and_source,
-    FromApub,
-    FromApubToForm,
-    ToApub,
-  },
+  check_is_apub_id_valid,
+  extensions::{context::lemmy_context, signatures::PublicKey},
+  objects::{FromApub, ImageObject, Source, ToApub},
   ActorType,
-  PersonExt,
-  UserTypes,
 };
 use activitystreams::{
-  actor::{Actor, ApActor, ApActorExt, Endpoints},
-  base::{BaseExt, ExtendsExt},
-  object::{ApObject, Image, Object, ObjectExt, Tombstone},
+  actor::Endpoints,
+  base::AnyBase,
+  chrono::{DateTime, FixedOffset},
+  object::{kind::ImageType, Tombstone},
+  primitives::OneOrMany,
+  unparsed::Unparsed,
 };
-use activitystreams_ext::Ext2;
-use anyhow::Context;
 use lemmy_api_common::blocking;
+use lemmy_apub_lib::{
+  values::{MediaTypeHtml, MediaTypeMarkdown},
+  verify_domains_match,
+};
 use lemmy_db_queries::{ApubObject, DbPool};
 use lemmy_db_schema::{
   naive_now,
   source::person::{Person as DbPerson, PersonForm},
 };
 use lemmy_utils::{
-  location_info,
-  settings::structs::Settings,
-  utils::{check_slurs, check_slurs_opt, convert_datetime},
+  utils::{check_slurs, check_slurs_opt, convert_datetime, markdown_to_html},
   LemmyError,
 };
 use lemmy_websocket::LemmyContext;
+use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use url::Url;
 
+#[derive(Clone, Copy, Debug, Deserialize, Serialize, PartialEq)]
+pub enum UserTypes {
+  Person,
+  Service,
+}
+
+#[skip_serializing_none]
+#[derive(Clone, Debug, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct Person {
+  #[serde(rename = "@context")]
+  context: OneOrMany<AnyBase>,
+  #[serde(rename = "type")]
+  kind: UserTypes,
+  id: Url,
+  /// username, set at account creation and can never be changed
+  preferred_username: String,
+  /// displayname (can be changed at any time)
+  name: Option<String>,
+  content: Option<String>,
+  media_type: Option<MediaTypeHtml>,
+  source: Option<Source>,
+  /// user avatar
+  icon: Option<ImageObject>,
+  /// user banner
+  image: Option<ImageObject>,
+  matrix_user_id: Option<String>,
+  inbox: Url,
+  /// mandatory field in activitypub, currently empty in lemmy
+  outbox: Url,
+  endpoints: Endpoints<Url>,
+  public_key: PublicKey,
+  published: DateTime<FixedOffset>,
+  updated: Option<DateTime<FixedOffset>>,
+  #[serde(flatten)]
+  unparsed: Unparsed,
+}
+
+// TODO: can generate this with a derive macro
+impl Person {
+  pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
+    verify_domains_match(&self.id, expected_domain)?;
+    Ok(&self.id)
+  }
+}
+
 #[async_trait::async_trait(?Send)]
 impl ToApub for DbPerson {
-  type ApubType = PersonExt;
+  type ApubType = Person;
 
-  async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
-    let object = Object::<UserTypes>::new_none_type();
-    let mut actor = Actor(object);
+  async fn to_apub(&self, _pool: &DbPool) -> Result<Person, LemmyError> {
     let kind = if self.bot_account {
       UserTypes::Service
     } else {
       UserTypes::Person
     };
-    actor.set_kind(kind);
-    let mut person = ApObject::new(actor);
-
-    person
-      .set_many_contexts(lemmy_context())
-      .set_id(self.actor_id.to_owned().into_inner())
-      .set_published(convert_datetime(self.published));
-
-    if let Some(u) = self.updated {
-      person.set_updated(convert_datetime(u));
-    }
-
-    if let Some(avatar_url) = &self.avatar {
-      let mut image = Image::new();
-      image.set_url::<Url>(avatar_url.to_owned().into());
-      person.set_icon(image.into_any_base()?);
-    }
-
-    if let Some(banner_url) = &self.banner {
-      let mut image = Image::new();
-      image.set_url::<Url>(banner_url.to_owned().into());
-      person.set_image(image.into_any_base()?);
-    }
-
-    if let Some(bio) = &self.bio {
-      set_content_and_source(&mut person, bio)?;
-    }
-
-    // In apub, the "name" is a display name
-    if let Some(i) = self.display_name.to_owned() {
-      person.set_name(i);
-    }
-
-    let mut ap_actor = ApActor::new(self.inbox_url.clone().into(), person);
-    ap_actor
-      .set_preferred_username(self.name.to_owned())
-      .set_outbox(self.get_outbox_url()?)
-      .set_endpoints(Endpoints {
-        shared_inbox: Some(self.get_shared_inbox_or_inbox_url()),
+    let source = self.bio.clone().map(|bio| Source {
+      content: bio,
+      media_type: MediaTypeMarkdown::Markdown,
+    });
+    let icon = self.avatar.clone().map(|url| ImageObject {
+      kind: ImageType::Image,
+      url: url.into(),
+    });
+    let image = self.banner.clone().map(|url| ImageObject {
+      kind: ImageType::Image,
+      url: url.into(),
+    });
+
+    let person = Person {
+      context: lemmy_context(),
+      kind,
+      id: self.actor_id.to_owned().into_inner(),
+      preferred_username: self.name.clone(),
+      name: self.display_name.clone(),
+      content: self.bio.as_ref().map(|b| markdown_to_html(b)),
+      media_type: self.bio.as_ref().map(|_| MediaTypeHtml::Html),
+      source,
+      icon,
+      image,
+      matrix_user_id: self.matrix_user_id.clone(),
+      published: convert_datetime(self.published),
+      outbox: self.get_outbox_url()?,
+      endpoints: Endpoints {
+        shared_inbox: self.shared_inbox_url.clone().map(|s| s.into()),
         ..Default::default()
-      });
-
-    let person_ext = PersonExtension::new(self.matrix_user_id.to_owned())?;
-    Ok(Ext2::new(ap_actor, person_ext, self.get_public_key_ext()?))
+      },
+      public_key: self.get_public_key()?,
+      updated: self.updated.map(convert_datetime),
+      unparsed: Default::default(),
+      inbox: self.inbox_url.clone().into(),
+    };
+    Ok(person)
   }
   fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
     unimplemented!()
@@ -98,118 +132,54 @@ impl ToApub for DbPerson {
 
 #[async_trait::async_trait(?Send)]
 impl FromApub for DbPerson {
-  type ApubType = PersonExt;
+  type ApubType = Person;
 
   async fn from_apub(
-    person: &PersonExt,
+    person: &Person,
     context: &LemmyContext,
-    expected_domain: Url,
-    request_counter: &mut i32,
-    mod_action_allowed: bool,
-  ) -> Result<DbPerson, LemmyError> {
-    let person_id = person.id_unchecked().context(location_info!())?.to_owned();
-    let domain = person_id.domain().context(location_info!())?;
-    if domain == Settings::get().hostname {
-      let person = blocking(context.pool(), move |conn| {
-        DbPerson::read_from_apub_id(conn, &person_id.into())
-      })
-      .await??;
-      Ok(person)
-    } else {
-      let person_form = PersonForm::from_apub(
-        person,
-        context,
-        expected_domain,
-        request_counter,
-        mod_action_allowed,
-      )
-      .await?;
-      let person = blocking(context.pool(), move |conn| {
-        DbPerson::upsert(conn, &person_form)
-      })
-      .await??;
-      Ok(person)
-    }
-  }
-}
-
-#[async_trait::async_trait(?Send)]
-impl FromApubToForm<PersonExt> for PersonForm {
-  async fn from_apub(
-    person: &PersonExt,
-    _context: &LemmyContext,
-    expected_domain: Url,
+    expected_domain: &Url,
     _request_counter: &mut i32,
-    _mod_action_allowed: bool,
-  ) -> Result<Self, LemmyError> {
-    let avatar = match person.icon() {
-      Some(any_image) => Some(
-        Image::from_any_base(any_image.as_one().context(location_info!())?.clone())?
-          .context(location_info!())?
-          .url()
-          .context(location_info!())?
-          .as_single_xsd_any_uri()
-          .map(|url| url.to_owned()),
-      ),
-      None => None,
-    };
-
-    let banner = match person.image() {
-      Some(any_image) => Some(
-        Image::from_any_base(any_image.as_one().context(location_info!())?.clone())
-          .context(location_info!())?
-          .context(location_info!())?
-          .url()
-          .context(location_info!())?
-          .as_single_xsd_any_uri()
-          .map(|url| url.to_owned()),
-      ),
-      None => None,
+  ) -> Result<DbPerson, LemmyError> {
+    let actor_id = Some(person.id(expected_domain)?.clone().into());
+    let name = person.preferred_username.clone();
+    let display_name: Option<String> = person.name.clone();
+    let bio = person.source.clone().map(|s| s.content);
+    let shared_inbox = person.endpoints.shared_inbox.clone().map(|s| s.into());
+    let bot_account = match person.kind {
+      UserTypes::Person => false,
+      UserTypes::Service => true,
     };
 
-    let name: String = person
-      .inner
-      .preferred_username()
-      .context(location_info!())?
-      .to_string();
-    let display_name: Option<String> = person
-      .name()
-      .map(|n| n.one())
-      .flatten()
-      .map(|n| n.to_owned().xsd_string())
-      .flatten();
-    let bio = get_source_markdown_value(person)?;
-    let shared_inbox = person
-      .inner
-      .endpoints()?
-      .map(|e| e.shared_inbox)
-      .flatten()
-      .map(|s| s.to_owned().into());
-
     check_slurs(&name)?;
     check_slurs_opt(&display_name)?;
     check_slurs_opt(&bio)?;
+    check_is_apub_id_valid(&person.id, false)?;
 
-    Ok(PersonForm {
+    let person_form = PersonForm {
       name,
       display_name: Some(display_name),
       banned: None,
       deleted: None,
-      avatar: avatar.map(|o| o.map(|i| i.into())),
-      banner: banner.map(|o| o.map(|i| i.into())),
-      published: person.inner.published().map(|u| u.to_owned().naive_local()),
-      updated: person.updated().map(|u| u.to_owned().naive_local()),
-      actor_id: Some(check_object_domain(person, expected_domain, false)?),
+      avatar: Some(person.icon.clone().map(|i| i.url.into())),
+      banner: Some(person.image.clone().map(|i| i.url.into())),
+      published: Some(person.published.naive_local()),
+      updated: person.updated.map(|u| u.clone().naive_local()),
+      actor_id,
       bio: Some(bio),
       local: Some(false),
       admin: Some(false),
-      bot_account: Some(person.inner.is_kind(&UserTypes::Service)),
+      bot_account: Some(bot_account),
       private_key: None,
-      public_key: Some(Some(person.ext_two.public_key.to_owned().public_key_pem)),
+      public_key: Some(Some(person.public_key.public_key_pem.clone())),
       last_refreshed_at: Some(naive_now()),
-      inbox_url: Some(person.inner.inbox()?.to_owned().into()),
+      inbox_url: Some(person.inbox.to_owned().into()),
       shared_inbox_url: Some(shared_inbox),
-      matrix_user_id: Some(person.ext_one.matrix_user_id.to_owned()),
+      matrix_user_id: Some(person.matrix_user_id.clone()),
+    };
+    let person = blocking(context.pool(), move |conn| {
+      DbPerson::upsert(conn, &person_form)
     })
+    .await??;
+    Ok(person)
   }
 }
index 2fdf8d67b5ce99f696438f17e281873816da70ee..43a019d16c70a046c658fcb8de10caa3fa39a1f7 100644 (file)
@@ -2,7 +2,7 @@ use crate::{
   activities::{extract_community, verify_person_in_community},
   extensions::context::lemmy_context,
   fetcher::person::get_or_fetch_and_upsert_person,
-  objects::{create_tombstone, FromApub, Source, ToApub},
+  objects::{create_tombstone, FromApub, ImageObject, Source, ToApub},
   ActorType,
 };
 use activitystreams::{
@@ -37,15 +37,17 @@ use lemmy_utils::{
 };
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use url::Url;
 
+#[skip_serializing_none]
 #[derive(Clone, Debug, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Page {
   #[serde(rename = "@context")]
   context: OneOrMany<AnyBase>,
   r#type: PageType,
-  pub(crate) id: Url,
+  id: Url,
   pub(crate) attributed_to: Url,
   to: [Url; 2],
   name: String,
@@ -63,14 +65,15 @@ pub struct Page {
   unparsed: Unparsed,
 }
 
-#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct ImageObject {
-  content: ImageType,
-  url: Url,
-}
-
 impl Page {
+  pub(crate) fn id_unchecked(&self) -> &Url {
+    &self.id
+  }
+  pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
+    verify_domains_match(&self.id, expected_domain)?;
+    Ok(&self.id)
+  }
+
   /// Only mods can change the post's stickied/locked status. So if either of these is changed from
   /// the current value, it is a mod action and needs to be verified as such.
   ///
@@ -126,7 +129,7 @@ impl ToApub for Post {
       media_type: MediaTypeMarkdown::Markdown,
     });
     let image = self.thumbnail_url.clone().map(|thumb| ImageObject {
-      content: ImageType::Image,
+      kind: ImageType::Image,
       url: thumb.into(),
     });
 
@@ -169,10 +172,17 @@ impl FromApub for Post {
   async fn from_apub(
     page: &Page,
     context: &LemmyContext,
-    _expected_domain: Url,
+    expected_domain: &Url,
     request_counter: &mut i32,
-    _mod_action_allowed: bool,
   ) -> Result<Post, LemmyError> {
+    // We can't verify the domain in case of mod action, because the mod may be on a different
+    // instance from the post author.
+    let ap_id = if page.is_mod_action(context.pool()).await? {
+      page.id_unchecked()
+    } else {
+      page.id(expected_domain)?
+    };
+    let ap_id = Some(ap_id.clone().into());
     let creator =
       get_or_fetch_and_upsert_person(&page.attributed_to, context, request_counter).await?;
     let community = extract_community(&page.to, context, request_counter).await?;
@@ -205,7 +215,7 @@ impl FromApub for Post {
       embed_description,
       embed_html,
       thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
-      ap_id: Some(page.id.clone().into()),
+      ap_id,
       local: Some(false),
     };
     Ok(blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??)
index 2b284a2da03c31d3e45c6b016238025020e754cf..02cf12ebac248af62191134aeb99f0eff504c49a 100644 (file)
@@ -24,15 +24,17 @@ use lemmy_db_schema::source::{
 use lemmy_utils::{utils::convert_datetime, LemmyError};
 use lemmy_websocket::LemmyContext;
 use serde::{Deserialize, Serialize};
+use serde_with::skip_serializing_none;
 use url::Url;
 
+#[skip_serializing_none]
 #[derive(Clone, Debug, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct Note {
   #[serde(rename = "@context")]
   context: OneOrMany<AnyBase>,
   r#type: NoteType,
-  pub(crate) id: Url,
+  id: Url,
   pub(crate) attributed_to: Url,
   to: Url,
   content: String,
@@ -45,6 +47,14 @@ pub struct Note {
 }
 
 impl Note {
+  pub(crate) fn id_unchecked(&self) -> &Url {
+    &self.id
+  }
+  pub(crate) fn id(&self, expected_domain: &Url) -> Result<&Url, LemmyError> {
+    verify_domains_match(&self.id, expected_domain)?;
+    Ok(&self.id)
+  }
+
   pub(crate) async fn verify(
     &self,
     context: &LemmyContext,
@@ -107,10 +117,10 @@ impl FromApub for PrivateMessage {
   async fn from_apub(
     note: &Note,
     context: &LemmyContext,
-    _expected_domain: Url,
+    expected_domain: &Url,
     request_counter: &mut i32,
-    _mod_action_allowed: bool,
   ) -> Result<PrivateMessage, LemmyError> {
+    let ap_id = Some(note.id(expected_domain)?.clone().into());
     let creator =
       get_or_fetch_and_upsert_person(&note.attributed_to, context, request_counter).await?;
     let recipient = get_or_fetch_and_upsert_person(&note.to, context, request_counter).await?;
@@ -123,7 +133,7 @@ impl FromApub for PrivateMessage {
       updated: note.updated.map(|u| u.to_owned().naive_local()),
       deleted: None,
       read: None,
-      ap_id: Some(note.id.clone().into()),
+      ap_id,
       local: Some(false),
     };
     Ok(
index 13fc4025d8047cd96a42fe052705a1c428de929b..df73d5e8f0f2d1f72d2e3f116042bb8105043f4c 100644 (file)
@@ -39,7 +39,7 @@ pub trait ActivityHandler {
   ) -> Result<(), LemmyError>;
 
   async fn receive(
-    &self,
+    self,
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError>;
index f8750680a297f2c6cbf5d9ccccedf381b76e1964..d35454a37a76f106426d74f090bd8a7c3d55e0e6 100644 (file)
@@ -92,7 +92,7 @@ pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::To
             }
           }
           async fn receive(
-            &self,
+            self,
             context: &LemmyContext,
             request_counter: &mut i32,
           ) -> Result<(), LemmyError> {