]> Untitled Git - lemmy.git/commitdiff
Added documentation for most functions
authorFelix <me@nutomic.com>
Fri, 17 Apr 2020 15:33:55 +0000 (17:33 +0200)
committerFelix <me@nutomic.com>
Fri, 17 Apr 2020 15:33:55 +0000 (17:33 +0200)
server/src/apub/activities.rs
server/src/apub/community.rs
server/src/apub/community_inbox.rs
server/src/apub/fetcher.rs
server/src/apub/mod.rs
server/src/apub/post.rs
server/src/apub/signatures.rs
server/src/apub/user.rs
server/src/apub/user_inbox.rs

index 8885f5558ef793621a2a39fb4a82bd287432d16c..f31be9db3087ddaea08abe8c0779d32ef361080a 100644 (file)
@@ -30,6 +30,7 @@ fn populate_object_props(
   Ok(())
 }
 
+/// Send an activity to a list of recipients, using the correct headers etc.
 fn send_activity<A>(activity: &A, to: Vec<String>) -> Result<(), Error>
 where
   A: Serialize + Debug,
@@ -47,7 +48,8 @@ where
   Ok(())
 }
 
-fn get_followers(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
+/// For a given community, returns the inboxes of all followers.
+fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
   Ok(
     CommunityFollowerView::for_community(conn, community.id)?
       .iter()
@@ -57,6 +59,7 @@ fn get_followers(conn: &PgConnection, community: &Community) -> Result<Vec<Strin
   )
 }
 
+/// 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> {
   let page = post.as_page(conn)?;
   let community = Community::read(conn, post.community_id)?;
@@ -70,10 +73,11 @@ pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
     .create_props
     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
     .set_object_base_box(page)?;
-  send_activity(&create, get_followers(conn, &community)?)?;
+  send_activity(&create, get_follower_inboxes(conn, &community)?)?;
   Ok(())
 }
 
+/// 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> {
   let page = post.as_page(conn)?;
   let community = Community::read(conn, post.community_id)?;
@@ -87,10 +91,11 @@ pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<
     .update_props
     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
     .set_object_base_box(page)?;
-  send_activity(&update, get_followers(conn, &community)?)?;
+  send_activity(&update, get_follower_inboxes(conn, &community)?)?;
   Ok(())
 }
 
+/// As a given local user, send out a follow request to a remote community.
 pub fn follow_community(
   community: &Community,
   user: &User_,
@@ -111,6 +116,7 @@ pub fn follow_community(
   Ok(())
 }
 
+/// As a local community, accept the follow request from a remote user.
 pub fn accept_follow(follow: &Follow) -> Result<(), Error> {
   let mut accept = Accept::new();
   accept
index 0bea47055c110712dbf694fcf8f949bb8220d9a7..a49357a867518b75a90b2566c0bcd330cd958f0d 100644 (file)
@@ -7,7 +7,6 @@ use crate::db::establish_unpooled_connection;
 use crate::db::post::Post;
 use crate::db::user::User_;
 use crate::db::Crud;
-use crate::settings::Settings;
 use crate::{convert_datetime, naive_now};
 use activitystreams::actor::properties::ApActorProperties;
 use activitystreams::collection::OrderedCollection;
@@ -30,30 +29,8 @@ pub struct CommunityQuery {
   community_name: String,
 }
 
-pub async fn get_apub_community_list(
-  db: web::Data<Pool<ConnectionManager<PgConnection>>>,
-) -> Result<HttpResponse<Body>, Error> {
-  // TODO: implement pagination
-  let communities = Community::list_local(&db.get().unwrap())?
-    .iter()
-    .map(|c| c.as_group(&db.get().unwrap()))
-    .collect::<Result<Vec<GroupExt>, Error>>()?;
-  let mut collection = UnorderedCollection::default();
-  let oprops: &mut ObjectProperties = collection.as_mut();
-  oprops.set_context_xsd_any_uri(context())?.set_id(format!(
-    "{}://{}/federation/communities",
-    get_apub_protocol_string(),
-    Settings::get().hostname
-  ))?;
-
-  collection
-    .collection_props
-    .set_total_items(communities.len() as u64)?
-    .set_many_items_base_boxes(communities)?;
-  Ok(create_apub_response(&collection))
-}
-
 impl Community {
+  // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
   fn as_group(&self, conn: &PgConnection) -> Result<GroupExt, Error> {
     let mut group = Group::default();
     let oprops: &mut ObjectProperties = group.as_mut();
@@ -104,6 +81,7 @@ impl Community {
 }
 
 impl CommunityForm {
+  /// Parse an ActivityPub group received from another instance into a Lemmy community.
   pub fn from_group(group: &GroupExt, conn: &PgConnection) -> Result<Self, Error> {
     let oprops = &group.base.base.object_props;
     let aprops = &group.base.extension;
@@ -142,6 +120,7 @@ impl CommunityForm {
   }
 }
 
+/// Return the community json over HTTP.
 pub async fn get_apub_community_http(
   info: Path<CommunityQuery>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
@@ -151,6 +130,7 @@ pub async fn get_apub_community_http(
   Ok(create_apub_response(&c))
 }
 
+/// Returns an empty followers collection, only populating the siz (for privacy).
 pub async fn get_apub_community_followers(
   info: Path<CommunityQuery>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
@@ -173,6 +153,7 @@ pub async fn get_apub_community_followers(
   Ok(create_apub_response(&collection))
 }
 
+/// Returns an UnorderedCollection with the latest posts from the community.
 pub async fn get_apub_community_outbox(
   info: Path<CommunityQuery>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
index caadecf132765e1996b8707bcf16d9938e196237..ea0d9105443f443e774279bc9a7670f359fb07af 100644 (file)
@@ -22,6 +22,7 @@ pub struct Params {
   community_name: String,
 }
 
+/// Handler for all incoming activities to community inboxes.
 pub async fn community_inbox(
   input: web::Json<CommunityAcceptedObjects>,
   params: web::Query<Params>,
@@ -38,6 +39,8 @@ pub async fn community_inbox(
   }
 }
 
+/// Handle a follow request from a remote user, adding it to the local database and returning an
+/// Accept activity.
 fn handle_follow(follow: &Follow, conn: &PgConnection) -> Result<HttpResponse, Error> {
   // TODO: make sure this is a local community
   let community_uri = follow
index 3be53ea7264082c84769989d18af8d5c9365dcb6..368aa4dc4d84116c6fdf1ccb9cbebbc3eee7093f 100644 (file)
@@ -20,6 +20,7 @@ use serde::Deserialize;
 use std::time::Duration;
 use url::Url;
 
+// Fetch nodeinfo metadata from a remote instance.
 fn _fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
   let well_known_uri = Url::parse(&format!(
     "{}://{}/.well-known/nodeinfo",
@@ -60,7 +61,9 @@ fn upsert_post(post_form: &PostForm, conn: &PgConnection) -> Result<Post, Error>
   }
 }
 
-// TODO: add an optional param last_updated and only fetch if its too old
+/// Fetch any type of ActivityPub object, handling things like HTTP headers, deserialisation,
+/// timeouts etc.
+/// TODO: add an optional param last_updated and only fetch if its too old
 pub fn fetch_remote_object<Response>(url: &Url) -> Result<Response, Error>
 where
   Response: for<'de> Deserialize<'de>,
@@ -81,6 +84,7 @@ where
   Ok(res)
 }
 
+/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
 #[serde(untagged)]
 #[derive(serde::Deserialize)]
 pub enum SearchAcceptedObjects {
@@ -89,6 +93,12 @@ pub enum SearchAcceptedObjects {
   Page(Box<Page>),
 }
 
+/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
+///
+/// Some working examples for use with the docker/federation/ setup:
+/// http://lemmy_alpha:8540/federation/c/main
+/// http://lemmy_alpha:8540/federation/u/lemmy_alpha
+/// http://lemmy_alpha:8540/federation/p/3
 pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchResponse, Error> {
   let query_url = Url::parse(&query)?;
   let mut response = SearchResponse {
@@ -98,10 +108,6 @@ pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchRespo
     communities: vec![],
     users: vec![],
   };
-  // test with:
-  // http://lemmy_alpha:8540/federation/c/main
-  // http://lemmy_alpha:8540/federation/u/lemmy_alpha
-  // http://lemmy_alpha:8540/federation/p/3
   match fetch_remote_object::<SearchAcceptedObjects>(&query_url)? {
     SearchAcceptedObjects::Person(p) => {
       let u = upsert_user(&UserForm::from_person(&p)?, conn)?;
@@ -120,6 +126,7 @@ pub fn search_by_apub_id(query: &str, conn: &PgConnection) -> Result<SearchRespo
   Ok(response)
 }
 
+/// Fetch all posts in the outbox of the given user, and insert them into the database.
 fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<Vec<Post>, Error> {
   let outbox_url = Url::parse(&community.get_outbox_url())?;
   let outbox = fetch_remote_object::<OrderedCollection>(&outbox_url)?;
@@ -137,12 +144,14 @@ fn fetch_community_outbox(community: &Community, conn: &PgConnection) -> Result<
   )
 }
 
+/// Fetch a user, insert/update it in the database and return the user.
 pub fn fetch_remote_user(apub_id: &Url, conn: &PgConnection) -> Result<User_, Error> {
   let person = fetch_remote_object::<PersonExt>(apub_id)?;
   let uf = UserForm::from_person(&person)?;
   upsert_user(&uf, conn)
 }
 
+/// Fetch a community, insert/update it in the database and return the community.
 pub fn fetch_remote_community(apub_id: &Url, conn: &PgConnection) -> Result<Community, Error> {
   let group = fetch_remote_object::<GroupExt>(apub_id)?;
   let cf = CommunityForm::from_group(&group, conn)?;
index 04b462bff9267a1d8a34236ab471a59a47d7b205..a7f0668a35dcc6cbe527436f3402666fdb79cd1c 100644 (file)
@@ -13,6 +13,7 @@ use activitystreams::ext::Ext;
 use actix_web::body::Body;
 use actix_web::HttpResponse;
 use openssl::{pkey::PKey, rsa::Rsa};
+use serde::ser::Serialize;
 use url::Url;
 
 type GroupExt = Ext<Ext<Group, ApActorProperties>, PublicKeyExtension>;
@@ -27,18 +28,22 @@ pub enum EndpointType {
   Comment,
 }
 
-fn create_apub_response<T>(json: &T) -> HttpResponse<Body>
+/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
+/// headers.
+fn create_apub_response<T>(data: &T) -> HttpResponse<Body>
 where
-  T: serde::ser::Serialize,
+  T: Serialize,
 {
   HttpResponse::Ok()
     .content_type(APUB_JSON_CONTENT_TYPE)
-    .json(json)
+    .json(data)
 }
 
-// TODO: we will probably need to change apub endpoint urls so that html and activity+json content
-//       types are handled at the same endpoint, so that you can copy the url into mastodon search
-//       and have it fetch the object.
+/// Generates the ActivityPub ID for a given object type and name.
+///
+/// TODO: we will probably need to change apub endpoint urls so that html and activity+json content
+///       types are handled at the same endpoint, so that you can copy the url into mastodon search
+///       and have it fetch the object.
 pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
   let point = match endpoint_type {
     EndpointType::Community => "c",
@@ -67,21 +72,16 @@ pub fn get_apub_protocol_string() -> &'static str {
   }
 }
 
-pub fn gen_keypair() -> (Vec<u8>, Vec<u8>) {
+/// Generate the asymmetric keypair for ActivityPub HTTP signatures.
+pub fn gen_keypair_str() -> (String, String) {
   let rsa = Rsa::generate(2048).expect("sign::gen_keypair: key generation error");
   let pkey = PKey::from_rsa(rsa).expect("sign::gen_keypair: parsing error");
-  (
-    pkey
-      .public_key_to_pem()
-      .expect("sign::gen_keypair: public key encoding error"),
-    pkey
-      .private_key_to_pem_pkcs8()
-      .expect("sign::gen_keypair: private key encoding error"),
-  )
-}
-
-pub fn gen_keypair_str() -> (String, String) {
-  let (public_key, private_key) = gen_keypair();
+  let public_key = pkey
+    .public_key_to_pem()
+    .expect("sign::gen_keypair: public key encoding error");
+  let private_key = pkey
+    .private_key_to_pem_pkcs8()
+    .expect("sign::gen_keypair: private key encoding error");
   (vec_bytes_to_str(public_key), vec_bytes_to_str(private_key))
 }
 
index b574d09c0b60eb99b5b5d947d9cc57dd50be7dc9..edae92d06909599c3d1a09cef21b13b91d0cf696 100644 (file)
@@ -20,6 +20,7 @@ pub struct PostQuery {
   post_id: String,
 }
 
+/// Return the post json over HTTP.
 pub async fn get_apub_post(
   info: Path<PostQuery>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
@@ -30,6 +31,7 @@ pub async fn get_apub_post(
 }
 
 impl Post {
+  // Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
   pub fn as_page(&self, conn: &PgConnection) -> Result<Page, Error> {
     let mut page = Page::default();
     let oprops: &mut ObjectProperties = page.as_mut();
@@ -67,6 +69,7 @@ impl Post {
 }
 
 impl PostForm {
+  /// Parse an ActivityPub page received from another instance into a Lemmy post.
   pub fn from_page(page: &Page, conn: &PgConnection) -> Result<PostForm, Error> {
     let oprops = &page.object_props;
     let creator_id = Url::parse(&oprops.get_attributed_to_xsd_any_uri().unwrap().to_string())?;
index 5bb3c53443729b131aa40b953ac767f7ab83ded1..0348acb851561035907cac8821d110699cd7bbc5 100644 (file)
@@ -1,11 +1,12 @@
 // For this example, we'll use the Extensible trait, the Extension trait, the Actor trait, and
 // the Person type
 use activitystreams::{actor::Actor, ext::Extension};
+use serde::{Deserialize, Serialize};
 
 // The following is taken from here:
 // https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
 
-#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PublicKey {
   pub id: String,
@@ -13,7 +14,7 @@ pub struct PublicKey {
   pub public_key_pem: String,
 }
 
-#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
 #[serde(rename_all = "camelCase")]
 pub struct PublicKeyExtension {
   pub public_key: PublicKey,
index d10093d4cb11c3377f7768173b8d7a99c5de48fd..b5a819114f0078b1fa62d1ccccb1750bade198f7 100644 (file)
@@ -22,6 +22,7 @@ pub struct UserQuery {
   user_name: String,
 }
 
+// Turn a Lemmy user into an ActivityPub person and return it as json.
 pub async fn get_apub_user(
   info: Path<UserQuery>,
   db: web::Data<Pool<ConnectionManager<PgConnection>>>,
@@ -64,6 +65,7 @@ pub async fn get_apub_user(
 }
 
 impl UserForm {
+  /// Parse an ActivityPub person received from another instance into a Lemmy user.
   pub fn from_person(person: &PersonExt) -> Result<Self, Error> {
     let oprops = &person.base.base.object_props;
     let aprops = &person.base.extension;
index 3b8d1df35be7cdc8517e6c8475e16edcbd54287d..7d1463083ef2fe86e82440604938dd958772d0c5 100644 (file)
@@ -22,6 +22,7 @@ pub struct Params {
   user_name: String,
 }
 
+/// Handler for all incoming activities to user inboxes.
 pub async fn user_inbox(
   input: web::Json<UserAcceptedObjects>,
   params: web::Query<Params>,
@@ -38,6 +39,7 @@ pub async fn user_inbox(
   }
 }
 
+/// Handle create activities and insert them in the database.
 fn handle_create(create: &Create, conn: &PgConnection) -> Result<HttpResponse, Error> {
   let page = create
     .create_props
@@ -52,6 +54,7 @@ fn handle_create(create: &Create, conn: &PgConnection) -> Result<HttpResponse, E
   Ok(HttpResponse::Ok().finish())
 }
 
+/// Handle update activities and insert them in the database.
 fn handle_update(update: &Update, conn: &PgConnection) -> Result<HttpResponse, Error> {
   let page = update
     .update_props
@@ -67,6 +70,7 @@ fn handle_update(update: &Update, conn: &PgConnection) -> Result<HttpResponse, E
   Ok(HttpResponse::Ok().finish())
 }
 
+/// Handle accepted follows.
 fn handle_accept(_accept: &Accept, _conn: &PgConnection) -> Result<HttpResponse, Error> {
   // TODO: make sure that we actually requested a follow
   // TODO: at this point, indicate to the user that they are following the community