]> Untitled Git - lemmy.git/commitdiff
Making a trait function for follow and accept.
authorDessalines <tyhou13@gmx.com>
Sun, 26 Apr 2020 17:20:42 +0000 (13:20 -0400)
committerDessalines <tyhou13@gmx.com>
Sun, 26 Apr 2020 17:20:42 +0000 (13:20 -0400)
server/Cargo.lock
server/Cargo.toml
server/src/api/community.rs
server/src/api/mod.rs
server/src/api/post.rs
server/src/apub/activities.rs
server/src/apub/community.rs
server/src/apub/community_inbox.rs
server/src/apub/mod.rs
server/src/apub/shared_inbox.rs [new file with mode: 0644]
server/src/apub/user.rs

index cbb6248bcda7a33c72ff7d3c4e9272e58ef59e65..fe54cfb4e87a6010abcf05eaf26d725a173981f9 100644 (file)
@@ -1455,6 +1455,15 @@ dependencies = [
  "sluice",
 ]
 
+[[package]]
+name = "itertools"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
+dependencies = [
+ "either",
+]
+
 [[package]]
 name = "itoa"
 version = "0.1.1"
@@ -1544,6 +1553,7 @@ dependencies = [
  "http",
  "http-signature-normalization",
  "isahc",
+ "itertools",
  "jsonwebtoken",
  "lazy_static 1.4.0",
  "lettre",
index 5f4d24cecdf412bc2ced99c53d8d2b464ae78f59..a521a9665db9e8272e65475314cd715a72247888 100644 (file)
@@ -44,3 +44,4 @@ http-signature-normalization = "0.4.1"
 base64 = "0.12.0"
 tokio = "0.2.18"
 futures = "0.3.4"
+itertools = "0.9.0"
index ace5b353e31a3b6ced07a3e185c5499bae428846..174a91c84e6c745d5e392141919f2f9d8f21e59d 100644 (file)
@@ -488,7 +488,7 @@ impl Perform for Oper<FollowCommunity> {
     } else {
       // TODO: still have to implement unfollow
       let user = User_::read(&conn, user_id)?;
-      follow_community(&community, &user, &conn)?;
+      user.send_follow(&community.actor_id)?;
       // TODO: this needs to return a "pending" state, until Accept is received from the remote server
     }
 
index 04d6900143ba7dd35d5681c98f10d979bdba7417..0595f2a40d4b90b4b3b9cc804360d2e43a3a45c4 100644 (file)
@@ -23,10 +23,10 @@ use crate::{
 };
 
 use crate::apub::{
-  activities::{follow_community, post_create, post_update},
+  activities::{send_post_create, send_post_update},
   fetcher::search_by_apub_id,
   signatures::generate_actor_keypair,
-  {make_apub_endpoint, EndpointType},
+  {make_apub_endpoint, ActorType, EndpointType},
 };
 use crate::settings::Settings;
 use crate::websocket::UserOperation;
index 32eb5470a2a290f2d87d726b3e94217c0310d99b..89f1dd1d359237a3ba717193fdf9904c4627e51f 100644 (file)
@@ -160,7 +160,7 @@ impl Perform for Oper<CreatePost> {
       Err(_e) => return Err(APIError::err("couldnt_create_post").into()),
     };
 
-    post_create(&updated_post, &user, &conn)?;
+    send_post_create(&updated_post, &user, &conn)?;
 
     // They like their own post by default
     let like_form = PostLikeForm {
@@ -531,7 +531,7 @@ impl Perform for Oper<EditPost> {
       ModStickyPost::create(&conn, &form)?;
     }
 
-    post_update(&updated_post, &user, &conn)?;
+    send_post_update(&updated_post, &user, &conn)?;
 
     let post_view = PostView::read(&conn, data.edit_id, Some(user_id))?;
 
index 24631a352c51099b6d455e33496d0fa77cddfb8a..cb98e973475bb8b039ce953d437e816f81ff1111 100644 (file)
@@ -17,7 +17,7 @@ fn populate_object_props(
 }
 
 /// Send an activity to a list of recipients, using the correct headers etc.
-fn send_activity<A>(
+pub fn send_activity<A>(
   activity: &A,
   private_key: &str,
   sender_id: &str,
@@ -52,15 +52,18 @@ where
 fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
   Ok(
     CommunityFollowerView::for_community(conn, community.id)?
-      .iter()
+      .into_iter()
       .filter(|c| !c.user_local)
+      // TODO eventually this will have to use the inbox or shared_inbox column, meaning that view
+      // will have to change
       .map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
+      .unique()
       .collect(),
   )
 }
 
 /// Send out information about a newly created post, to the followers of the community.
-pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+pub fn send_post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
   let page = post.to_apub(conn)?;
   let community = Community::read(conn, post.community_id)?;
   let mut create = Create::new();
@@ -83,7 +86,7 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
 }
 
 /// Send out information about an edited post, to the followers of the community.
-pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
+pub fn send_post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
   let page = post.to_apub(conn)?;
   let community = Community::read(conn, post.community_id)?;
   let mut update = Update::new();
@@ -104,70 +107,3 @@ pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
   )?;
   Ok(())
 }
-
-/// As a given local user, send out a follow request to a remote community.
-pub fn follow_community(
-  community: &Community,
-  user: &User_,
-  _conn: &PgConnection,
-) -> Result<(), Error> {
-  let mut follow = Follow::new();
-  follow
-    .object_props
-    .set_context_xsd_any_uri(context())?
-    // TODO: needs proper id
-    .set_id(user.actor_id.clone())?;
-  follow
-    .follow_props
-    .set_actor_xsd_any_uri(user.actor_id.clone())?
-    .set_object_xsd_any_uri(community.actor_id.clone())?;
-  // TODO this is incorrect, the to field should not be the inbox, but the followers url
-  let to = format!("{}/inbox", community.actor_id);
-  send_activity(
-    &follow,
-    &user.private_key.as_ref().unwrap(),
-    &community.actor_id,
-    vec![to],
-  )?;
-  Ok(())
-}
-
-/// As a local community, accept the follow request from a remote user.
-pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
-  let community_uri = follow
-    .follow_props
-    .get_object_xsd_any_uri()
-    .unwrap()
-    .to_string();
-  let actor_uri = follow
-    .follow_props
-    .get_actor_xsd_any_uri()
-    .unwrap()
-    .to_string();
-  let community = Community::read_from_actor_id(conn, &community_uri)?;
-  let mut accept = Accept::new();
-  accept
-    .object_props
-    .set_context_xsd_any_uri(context())?
-    // TODO: needs proper id
-    .set_id(
-      follow
-        .follow_props
-        .get_actor_xsd_any_uri()
-        .unwrap()
-        .to_string(),
-    )?;
-  accept
-    .accept_props
-    .set_actor_xsd_any_uri(community.actor_id.clone())?
-    .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
-  // TODO this is incorrect, the to field should not be the inbox, but the followers url
-  let to = format!("{}/inbox", actor_uri);
-  send_activity(
-    &accept,
-    &community.private_key.unwrap(),
-    &community.actor_id,
-    vec![to],
-  )?;
-  Ok(())
-}
index e74a5fd14faecf157bb45a4066673ec508369619..bc984b250c7dcf6ae633d0609ceb6801f362391b 100644 (file)
@@ -30,12 +30,17 @@ impl ToApub for Community {
       oprops.set_summary_xsd_string(d)?;
     }
 
+    let mut endpoint_props = EndpointProperties::default();
+
+    endpoint_props.set_shared_inbox(self.get_shared_inbox_url())?;
+
     let mut actor_props = ApActorProperties::default();
 
     actor_props
       .set_preferred_username(self.title.to_owned())?
       .set_inbox(self.get_inbox_url())?
       .set_outbox(self.get_outbox_url())?
+      .set_endpoints(endpoint_props)?
       .set_followers(self.get_followers_url())?;
 
     Ok(group.extend(actor_props).extend(self.get_public_key_ext()))
@@ -50,6 +55,40 @@ impl ActorType for Community {
   fn public_key(&self) -> String {
     self.public_key.to_owned().unwrap()
   }
+
+  /// As a local community, accept the follow request from a remote user.
+  fn send_accept_follow(&self, follow: &Follow) -> Result<(), Error> {
+    let actor_uri = follow
+      .follow_props
+      .get_actor_xsd_any_uri()
+      .unwrap()
+      .to_string();
+
+    let mut accept = Accept::new();
+    accept
+      .object_props
+      .set_context_xsd_any_uri(context())?
+      // TODO: needs proper id
+      .set_id(
+        follow
+          .follow_props
+          .get_actor_xsd_any_uri()
+          .unwrap()
+          .to_string(),
+      )?;
+    accept
+      .accept_props
+      .set_actor_xsd_any_uri(self.actor_id.to_owned())?
+      .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
+    let to = format!("{}/inbox", actor_uri);
+    send_activity(
+      &accept,
+      &self.private_key.to_owned().unwrap(),
+      &self.actor_id,
+      vec![to],
+    )?;
+    Ok(())
+  }
 }
 
 impl FromApub for CommunityForm {
index 6931cdf1a7fe40b9c43b9736b39dde834c66bbc3..04a64b69a791032380fa1f12d4d5d96dbe8815f8 100644 (file)
@@ -63,6 +63,7 @@ fn handle_follow(
   // This will fail if they're already a follower, but ignore the error.
   CommunityFollower::follow(&conn, &community_follower_form).ok();
 
-  accept_follow(&follow, &conn)?;
+  community.send_accept_follow(&follow)?;
+
   Ok(HttpResponse::Ok().finish())
 }
index 671f8c5ac9fb1933595eccc98359a48ac5001e98..5c5852991102f76d0b5b12271a8b4db041942aa6 100644 (file)
@@ -3,6 +3,7 @@ pub mod community;
 pub mod community_inbox;
 pub mod fetcher;
 pub mod post;
+pub mod shared_inbox;
 pub mod signatures;
 pub mod user;
 pub mod user_inbox;
@@ -12,6 +13,7 @@ use activitystreams::{
   actor::{properties::ApActorProperties, Actor, Group, Person},
   collection::UnorderedCollection,
   context,
+  endpoint::EndpointProperties,
   ext::{Ext, Extensible, Extension},
   object::{properties::ObjectProperties, Page},
   public, BaseBox,
@@ -26,6 +28,7 @@ use failure::_core::fmt::Debug;
 use http::request::Builder;
 use http_signature_normalization::Config;
 use isahc::prelude::*;
+use itertools::Itertools;
 use log::debug;
 use openssl::hash::MessageDigest;
 use openssl::sign::{Signer, Verifier};
@@ -47,7 +50,7 @@ use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
 use crate::routes::{ChatServerParam, DbPoolParam};
 use crate::{convert_datetime, naive_now, Settings};
 
-use activities::accept_follow;
+use activities::send_activity;
 use fetcher::{get_or_fetch_and_upsert_remote_community, get_or_fetch_and_upsert_remote_user};
 use signatures::verify;
 use signatures::{sign, PublicKey, PublicKeyExtension};
@@ -144,9 +147,38 @@ pub trait ActorType {
 
   fn public_key(&self) -> String;
 
+  // These two have default impls, since currently a community can't follow anything,
+  // and a user can't be followed (yet)
+  #[allow(unused_variables)]
+  fn send_follow(&self, follow_actor_id: &str) -> Result<(), Error> {
+    Ok(())
+  }
+
+  #[allow(unused_variables)]
+  fn send_accept_follow(&self, follow: &Follow) -> Result<(), Error> {
+    Ok(())
+  }
+
+  // TODO move these to the db rows
   fn get_inbox_url(&self) -> String {
     format!("{}/inbox", &self.actor_id())
   }
+
+  fn get_shared_inbox_url(&self) -> String {
+    let url = Url::parse(&self.actor_id()).unwrap();
+    let url_str = format!(
+      "{}://{}{}/inbox",
+      &url.scheme(),
+      &url.host_str().unwrap(),
+      if let Some(port) = url.port() {
+        format!(":{}", port)
+      } else {
+        "".to_string()
+      },
+    );
+    format!("{}/inbox", &url_str)
+  }
+
   fn get_outbox_url(&self) -> String {
     format!("{}/outbox", &self.actor_id())
   }
@@ -154,9 +186,11 @@ pub trait ActorType {
   fn get_followers_url(&self) -> String {
     format!("{}/followers", &self.actor_id())
   }
+
   fn get_following_url(&self) -> String {
     format!("{}/following", &self.actor_id())
   }
+
   fn get_liked_url(&self) -> String {
     format!("{}/liked", &self.actor_id())
   }
diff --git a/server/src/apub/shared_inbox.rs b/server/src/apub/shared_inbox.rs
new file mode 100644 (file)
index 0000000..35ba390
--- /dev/null
@@ -0,0 +1 @@
+// use super::*;
index 88238b5d48fb1e022eb8811c05aa7f457a1ed01a..b4b3b35b6c6b08491a6edc256ed0fb161f18288b 100644 (file)
@@ -27,11 +27,16 @@ impl ToApub for User_ {
       oprops.set_name_xsd_string(i.to_owned())?;
     }
 
+    let mut endpoint_props = EndpointProperties::default();
+
+    endpoint_props.set_shared_inbox(self.get_shared_inbox_url())?;
+
     let mut actor_props = ApActorProperties::default();
 
     actor_props
       .set_inbox(self.get_inbox_url())?
       .set_outbox(self.get_outbox_url())?
+      .set_endpoints(endpoint_props)?
       .set_followers(self.get_followers_url())?
       .set_following(self.get_following_url())?
       .set_liked(self.get_liked_url())?;
@@ -48,6 +53,29 @@ impl ActorType for User_ {
   fn public_key(&self) -> String {
     self.public_key.to_owned().unwrap()
   }
+
+  // TODO might be able to move this to a default trait fn
+  /// As a given local user, send out a follow request to a remote community.
+  fn send_follow(&self, follow_actor_id: &str) -> Result<(), Error> {
+    let mut follow = Follow::new();
+    follow
+      .object_props
+      .set_context_xsd_any_uri(context())?
+      // TODO: needs proper id
+      .set_id(self.actor_id.to_owned())?;
+    follow
+      .follow_props
+      .set_actor_xsd_any_uri(self.actor_id.to_owned())?
+      .set_object_xsd_any_uri(follow_actor_id)?;
+    let to = format!("{}/inbox", follow_actor_id);
+    send_activity(
+      &follow,
+      &self.private_key.as_ref().unwrap(),
+      &follow_actor_id,
+      vec![to],
+    )?;
+    Ok(())
+  }
 }
 
 impl FromApub for UserForm {