4 pub mod community_inbox;
8 pub mod private_message;
15 group_extensions::GroupExtension,
16 page_extension::PageExtension,
17 signatures::{PublicKey, PublicKeyExtension},
21 routes::webfinger::WebFingerResponse,
25 use activitystreams::{
26 actor::{properties::ApActorProperties, Group, Person},
29 use activitystreams_ext::{Ext1, Ext2, Ext3};
30 use activitystreams_new::{activity::Follow, object::Tombstone, prelude::*};
31 use actix_web::{body::Body, HttpResponse, Result};
32 use chrono::NaiveDateTime;
33 use diesel::PgConnection;
39 type GroupExt = Ext3<Group, GroupExtension, ApActorProperties, PublicKeyExtension>;
40 type PersonExt = Ext2<Person, ApActorProperties, PublicKeyExtension>;
41 type PageExt = Ext1<Page, PageExtension>;
43 pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
45 pub enum EndpointType {
53 /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
55 fn create_apub_response<T>(data: &T) -> HttpResponse<Body>
60 .content_type(APUB_JSON_CONTENT_TYPE)
64 fn create_apub_tombstone_response<T>(data: &T) -> HttpResponse<Body>
69 .content_type(APUB_JSON_CONTENT_TYPE)
73 /// Generates the ActivityPub ID for a given object type and ID.
74 pub fn make_apub_endpoint(endpoint_type: EndpointType, name: &str) -> Url {
75 let point = match endpoint_type {
76 EndpointType::Community => "c",
77 EndpointType::User => "u",
78 EndpointType::Post => "post",
79 EndpointType::Comment => "comment",
80 EndpointType::PrivateMessage => "private_message",
85 get_apub_protocol_string(),
86 Settings::get().hostname,
93 pub fn get_apub_protocol_string() -> &'static str {
94 if Settings::get().federation.tls_enabled {
101 // Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
102 fn is_apub_id_valid(apub_id: &Url) -> bool {
103 if apub_id.scheme() != get_apub_protocol_string() {
107 let allowed_instances: Vec<String> = Settings::get()
111 .map(|d| d.to_string())
113 match apub_id.domain() {
114 Some(d) => allowed_instances.contains(&d.to_owned()),
121 fn to_apub(&self, conn: &PgConnection) -> Result<Self::Response, Error>;
122 fn to_tombstone(&self) -> Result<Tombstone, Error>;
125 /// Updated is actually the deletion time
129 updated: Option<NaiveDateTime>,
131 ) -> Result<Tombstone, Error> {
133 if let Some(updated) = updated {
134 let mut tombstone = Tombstone::new();
135 tombstone.set_id(object_id.parse()?);
136 tombstone.set_former_type(former_type);
137 tombstone.set_deleted(convert_datetime(updated).into());
141 "Cant convert to tombstone because updated time was None."
146 "Cant convert object to tombstone if it wasnt deleted"
153 fn from_apub(apub: &Self::ApubType, conn: &PgConnection) -> Result<Self, Error>
158 pub trait ApubObjectType {
159 fn send_create(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
160 fn send_update(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
161 fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
162 fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
163 fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
164 fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
167 pub trait ApubLikeableType {
168 fn send_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
169 fn send_dislike(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
170 fn send_undo_like(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
173 pub fn get_shared_inbox(actor_id: &str) -> String {
174 let url = Url::parse(actor_id).unwrap();
178 &url.host_str().unwrap(),
179 if let Some(port) = url.port() {
187 pub trait ActorType {
188 fn actor_id(&self) -> String;
190 fn public_key(&self) -> String;
191 fn private_key(&self) -> String;
193 // These two have default impls, since currently a community can't follow anything,
194 // and a user can't be followed (yet)
195 #[allow(unused_variables)]
196 fn send_follow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
197 fn send_unfollow(&self, follow_actor_id: &str, conn: &PgConnection) -> Result<(), Error>;
199 #[allow(unused_variables)]
200 fn send_accept_follow(&self, follow: &Follow, conn: &PgConnection) -> Result<(), Error>;
202 fn send_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
203 fn send_undo_delete(&self, creator: &User_, conn: &PgConnection) -> Result<(), Error>;
205 fn send_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
206 fn send_undo_remove(&self, mod_: &User_, conn: &PgConnection) -> Result<(), Error>;
208 /// For a given community, returns the inboxes of all followers.
209 fn get_follower_inboxes(&self, conn: &PgConnection) -> Result<Vec<String>, Error>;
211 // TODO move these to the db rows
212 fn get_inbox_url(&self) -> String {
213 format!("{}/inbox", &self.actor_id())
216 fn get_shared_inbox_url(&self) -> String {
217 get_shared_inbox(&self.actor_id())
220 fn get_outbox_url(&self) -> String {
221 format!("{}/outbox", &self.actor_id())
224 fn get_followers_url(&self) -> String {
225 format!("{}/followers", &self.actor_id())
228 fn get_following_url(&self) -> String {
229 format!("{}/following", &self.actor_id())
232 fn get_liked_url(&self) -> String {
233 format!("{}/liked", &self.actor_id())
236 fn get_public_key_ext(&self) -> PublicKeyExtension {
238 id: format!("{}#main-key", self.actor_id()),
239 owner: self.actor_id(),
240 public_key_pem: self.public_key(),
246 pub fn fetch_webfinger_url(mention: &MentionData) -> Result<String, Error> {
247 let fetch_url = format!(
248 "{}://{}/.well-known/webfinger?resource=acct:{}@{}",
249 get_apub_protocol_string(),
254 debug!("Fetching webfinger url: {}", &fetch_url);
255 let text: String = attohttpc::get(&fetch_url).send()?.text()?;
256 let res: WebFingerResponse = serde_json::from_str(&text)?;
260 .find(|l| l.type_.eq(&Some("application/activity+json".to_string())))
261 .ok_or_else(|| format_err!("No application/activity+json link found."))?;
265 .ok_or_else(|| format_err!("No href found."))