]> Untitled Git - lemmy.git/blob - server/src/apub/activities.rs
Some more cleanup.
[lemmy.git] / server / src / apub / activities.rs
1 use super::*;
2
3 fn populate_object_props(
4   props: &mut ObjectProperties,
5   addressed_to: &str,
6   object_id: &str,
7 ) -> Result<(), Error> {
8   props
9     .set_context_xsd_any_uri(context())?
10     // TODO: the activity needs a seperate id from the object
11     .set_id(object_id)?
12     // TODO: should to/cc go on the Create, or on the Post? or on both?
13     // TODO: handle privacy on the receiving side (at least ignore anything thats not public)
14     .set_to_xsd_any_uri(public())?
15     .set_cc_xsd_any_uri(addressed_to)?;
16   Ok(())
17 }
18
19 /// Send an activity to a list of recipients, using the correct headers etc.
20 fn send_activity<A>(
21   activity: &A,
22   private_key: &str,
23   sender_id: &str,
24   to: Vec<String>,
25 ) -> Result<(), Error>
26 where
27   A: Serialize + Debug,
28 {
29   let json = serde_json::to_string(&activity)?;
30   debug!("Sending activitypub activity {} to {:?}", json, to);
31   // TODO it needs to expand, the to field needs to expand and dedup the followers urls
32   // The inbox is determined by first retrieving the target actor's JSON-LD representation and then looking up the inbox property. If a recipient is a Collection or OrderedCollection, then the server MUST dereference the collection (with the user's credentials) and discover inboxes for each item in the collection. Servers MUST limit the number of layers of indirections through collections which will be performed, which MAY be one.
33   for t in to {
34     let to_url = Url::parse(&t)?;
35     if !is_apub_id_valid(&to_url) {
36       debug!("Not sending activity to {} (invalid or blacklisted)", t);
37       continue;
38     }
39     let request = Request::post(t).header("Host", to_url.domain().unwrap());
40     let signature = sign(&request, private_key, sender_id)?;
41     let res = request
42       .header("Signature", signature)
43       .header("Content-Type", "application/json")
44       .body(json.to_owned())?
45       .send()?;
46     debug!("Result for activity send: {:?}", res);
47   }
48   Ok(())
49 }
50
51 /// For a given community, returns the inboxes of all followers.
52 fn get_follower_inboxes(conn: &PgConnection, community: &Community) -> Result<Vec<String>, Error> {
53   Ok(
54     CommunityFollowerView::for_community(conn, community.id)?
55       .iter()
56       .filter(|c| !c.user_local)
57       .map(|c| format!("{}/inbox", c.user_actor_id.to_owned()))
58       .collect(),
59   )
60 }
61
62 /// Send out information about a newly created post, to the followers of the community.
63 pub fn post_create(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
64   let page = post.to_apub(conn)?;
65   let community = Community::read(conn, post.community_id)?;
66   let mut create = Create::new();
67   populate_object_props(
68     &mut create.object_props,
69     &community.get_followers_url(),
70     &post.ap_id,
71   )?;
72   create
73     .create_props
74     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
75     .set_object_base_box(page)?;
76   send_activity(
77     &create,
78     &creator.private_key.as_ref().unwrap(),
79     &creator.actor_id,
80     get_follower_inboxes(conn, &community)?,
81   )?;
82   Ok(())
83 }
84
85 /// Send out information about an edited post, to the followers of the community.
86 pub fn post_update(post: &Post, creator: &User_, conn: &PgConnection) -> Result<(), Error> {
87   let page = post.to_apub(conn)?;
88   let community = Community::read(conn, post.community_id)?;
89   let mut update = Update::new();
90   populate_object_props(
91     &mut update.object_props,
92     &community.get_followers_url(),
93     &post.ap_id,
94   )?;
95   update
96     .update_props
97     .set_actor_xsd_any_uri(creator.actor_id.to_owned())?
98     .set_object_base_box(page)?;
99   send_activity(
100     &update,
101     &creator.private_key.as_ref().unwrap(),
102     &creator.actor_id,
103     get_follower_inboxes(conn, &community)?,
104   )?;
105   Ok(())
106 }
107
108 /// As a given local user, send out a follow request to a remote community.
109 pub fn follow_community(
110   community: &Community,
111   user: &User_,
112   _conn: &PgConnection,
113 ) -> Result<(), Error> {
114   let mut follow = Follow::new();
115   follow
116     .object_props
117     .set_context_xsd_any_uri(context())?
118     // TODO: needs proper id
119     .set_id(user.actor_id.clone())?;
120   follow
121     .follow_props
122     .set_actor_xsd_any_uri(user.actor_id.clone())?
123     .set_object_xsd_any_uri(community.actor_id.clone())?;
124   // TODO this is incorrect, the to field should not be the inbox, but the followers url
125   let to = format!("{}/inbox", community.actor_id);
126   send_activity(
127     &follow,
128     &user.private_key.as_ref().unwrap(),
129     &community.actor_id,
130     vec![to],
131   )?;
132   Ok(())
133 }
134
135 /// As a local community, accept the follow request from a remote user.
136 pub fn accept_follow(follow: &Follow, conn: &PgConnection) -> Result<(), Error> {
137   let community_uri = follow
138     .follow_props
139     .get_object_xsd_any_uri()
140     .unwrap()
141     .to_string();
142   let actor_uri = follow
143     .follow_props
144     .get_actor_xsd_any_uri()
145     .unwrap()
146     .to_string();
147   let community = Community::read_from_actor_id(conn, &community_uri)?;
148   let mut accept = Accept::new();
149   accept
150     .object_props
151     .set_context_xsd_any_uri(context())?
152     // TODO: needs proper id
153     .set_id(
154       follow
155         .follow_props
156         .get_actor_xsd_any_uri()
157         .unwrap()
158         .to_string(),
159     )?;
160   accept
161     .accept_props
162     .set_actor_xsd_any_uri(community.actor_id.clone())?
163     .set_object_base_box(BaseBox::from_concrete(follow.clone())?)?;
164   // TODO this is incorrect, the to field should not be the inbox, but the followers url
165   let to = format!("{}/inbox", actor_uri);
166   send_activity(
167     &accept,
168     &community.private_key.unwrap(),
169     &community.actor_id,
170     vec![to],
171   )?;
172   Ok(())
173 }