lemmy/server/src/apub/activities.rs

181 lines
5.2 KiB
Rust
Raw Normal View History

2020-04-17 17:34:18 +00:00
use crate::apub::is_apub_id_valid;
2020-04-19 11:44:44 +00:00
use crate::apub::signatures::sign;
2020-04-10 11:26:06 +00:00
use crate::db::community::Community;
use crate::db::community_view::CommunityFollowerView;
2020-04-09 19:04:31 +00:00
use crate::db::post::Post;
use crate::db::user::User_;
2020-04-10 11:26:06 +00:00
use crate::db::Crud;
2020-04-14 15:37:23 +00:00
use activitystreams::activity::{Accept, Create, Follow, Update};
2020-04-13 13:06:41 +00:00
use activitystreams::object::properties::ObjectProperties;
2020-04-14 15:37:23 +00:00
use activitystreams::BaseBox;
2020-04-13 13:06:41 +00:00
use activitystreams::{context, public};
2020-04-09 19:04:31 +00:00
use diesel::PgConnection;
use failure::Error;
2020-04-13 13:06:41 +00:00
use failure::_core::fmt::Debug;
2020-04-09 19:04:31 +00:00
use isahc::prelude::*;
2020-04-17 14:55:28 +00:00
use log::debug;
2020-04-13 13:06:41 +00:00
use serde::Serialize;
use url::Url;
2020-04-09 19:04:31 +00:00
2020-04-13 13:06:41 +00:00
fn populate_object_props(
props: &mut ObjectProperties,
addressed_to: &str,
object_id: &str,
) -> Result<(), Error> {
props
.set_context_xsd_any_uri(context())?
// TODO: the activity needs a seperate id from the object
.set_id(object_id)?
2020-04-10 11:26:06 +00:00
// TODO: should to/cc go on the Create, or on the Post? or on both?
// TODO: handle privacy on the receiving side (at least ignore anything thats not public)
2020-04-13 13:06:41 +00:00
.set_to_xsd_any_uri(public())?
.set_cc_xsd_any_uri(addressed_to)?;
Ok(())
}
2020-04-17 15:33:55 +00:00
/// Send an activity to a list of recipients, using the correct headers etc.
fn send_activity<A>(
activity: &A,
2020-04-19 11:44:44 +00:00
private_key: &str,
sender_id: &str,
to: Vec<String>,
) -> Result<(), Error>
2020-04-13 13:06:41 +00:00
where
A: Serialize + Debug,
{
let json = serde_json::to_string(&activity)?;
2020-04-17 17:34:18 +00:00
debug!("Sending activitypub activity {} to {:?}", json, to);
2020-04-14 15:37:23 +00:00
for t in to {
let to_url = Url::parse(&t)?;
if !is_apub_id_valid(&to_url) {
2020-04-17 17:34:18 +00:00
debug!("Not sending activity to {} (invalid or blacklisted)", t);
continue;
}
let request = Request::post(t).header("Host", to_url.domain().unwrap());
2020-04-19 11:44:44 +00:00
let signature = sign(&request, private_key, sender_id)?;
let res = request
.header("Signature", signature)
2020-04-09 19:04:31 +00:00
.header("Content-Type", "application/json")
.body(json.to_owned())?
.send()?;
2020-04-17 14:55:28 +00:00
debug!("Result for activity send: {:?}", res);
2020-04-09 19:04:31 +00:00
}
Ok(())
}
2020-04-13 13:06:41 +00:00
2020-04-17 15:33:55 +00:00
/// 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()
.filter(|c| !c.user_local)
.map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
.collect(),
)
2020-04-14 15:37:23 +00:00
}
2020-04-17 15:33:55 +00:00
/// Send out information about a newly created post, to the followers of the community.
2020-04-13 13:06:41 +00:00
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)?;
let mut create = Create::new();
populate_object_props(
&mut create.object_props,
&community.get_followers_url(),
&post.ap_id,
)?;
create
.create_props
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(page)?;
send_activity(
&create,
2020-04-19 11:44:44 +00:00
&creator.private_key.as_ref().unwrap(),
&creator.actor_id,
get_follower_inboxes(conn, &community)?,
)?;
2020-04-13 13:06:41 +00:00
Ok(())
}
2020-04-17 15:33:55 +00:00
/// Send out information about an edited post, to the followers of the community.
2020-04-13 13:06:41 +00:00
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)?;
let mut update = Update::new();
populate_object_props(
&mut update.object_props,
&community.get_followers_url(),
&post.ap_id,
)?;
update
.update_props
.set_actor_xsd_any_uri(creator.actor_id.to_owned())?
.set_object_base_box(page)?;
send_activity(
&update,
2020-04-19 11:44:44 +00:00
&creator.private_key.as_ref().unwrap(),
&creator.actor_id,
get_follower_inboxes(conn, &community)?,
)?;
2020-04-14 15:37:23 +00:00
Ok(())
}
2020-04-17 15:33:55 +00:00
/// As a given local user, send out a follow request to a remote community.
2020-04-14 15:37:23 +00:00
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())?;
let to = format!("{}/inbox", community.actor_id);
send_activity(
&follow,
2020-04-19 17:35:40 +00:00
&user.private_key.as_ref().unwrap(),
&community.actor_id,
vec![to],
)?;
2020-04-14 15:37:23 +00:00
Ok(())
}
2020-04-17 15:33:55 +00:00
/// 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
2020-04-19 17:35:40 +00:00
.get_object_xsd_any_uri()
.unwrap()
.to_string();
let community = Community::read_from_actor_id(conn, &community_uri)?;
2020-04-14 15:37:23 +00:00
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_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
let to = format!("{}/inbox", community_uri);
send_activity(
&accept,
2020-04-19 11:44:44 +00:00
&community.private_key.unwrap(),
&community.actor_id,
vec![to],
)?;
2020-04-13 13:06:41 +00:00
Ok(())
}