]> Untitled Git - lemmy.git/blob - crates/apub/src/inbox/mod.rs
Trying to upgrade lemmys deps.
[lemmy.git] / crates / apub / src / inbox / mod.rs
1 use crate::{
2   check_is_apub_id_valid,
3   extensions::signatures::verify_signature,
4   fetcher::get_or_fetch_and_upsert_actor,
5   ActorType,
6 };
7 use activitystreams::{
8   activity::ActorAndObjectRefExt,
9   base::{AsBase, BaseExt, Extends},
10   object::{AsObject, ObjectExt},
11   public,
12 };
13 use actix_web::HttpRequest;
14 use anyhow::{anyhow, Context};
15 use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool};
16 use lemmy_db_schema::source::{activity::Activity, community::Community, user::User_};
17 use lemmy_structs::blocking;
18 use lemmy_utils::{location_info, settings::Settings, LemmyError};
19 use lemmy_websocket::LemmyContext;
20 use serde::Serialize;
21 use std::fmt::Debug;
22 use url::Url;
23
24 pub mod community_inbox;
25 mod receive_for_community;
26 pub mod shared_inbox;
27 pub mod user_inbox;
28
29 pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
30 where
31   T: BaseExt<Kind> + Extends<Kind> + Debug,
32   Kind: Serialize,
33   <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
34 {
35   let creator_domain = creator_uri.host_str().context(location_info!())?;
36   let activity_id = activity.id(creator_domain)?;
37   Ok(activity_id.context(location_info!())?.to_owned())
38 }
39
40 pub(crate) async fn is_activity_already_known(
41   pool: &DbPool,
42   activity_id: &Url,
43 ) -> Result<bool, LemmyError> {
44   let activity_id = activity_id.to_string();
45   let existing = blocking(pool, move |conn| {
46     Activity::read_from_apub_id(&conn, &activity_id)
47   })
48   .await?;
49   match existing {
50     Ok(_) => Ok(true),
51     Err(_) => Ok(false),
52   }
53 }
54
55 pub(crate) fn get_activity_to_and_cc<T, Kind>(activity: &T) -> Vec<Url>
56 where
57   T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
58 {
59   let mut to_and_cc = vec![];
60   if let Some(to) = activity.to() {
61     let to = to.to_owned().unwrap_to_vec();
62     let mut to = to
63       .iter()
64       .map(|t| t.as_xsd_any_uri())
65       .flatten()
66       .map(|t| t.to_owned())
67       .collect();
68     to_and_cc.append(&mut to);
69   }
70   if let Some(cc) = activity.cc() {
71     let cc = cc.to_owned().unwrap_to_vec();
72     let mut cc = cc
73       .iter()
74       .map(|c| c.as_xsd_any_uri())
75       .flatten()
76       .map(|c| c.to_owned())
77       .collect();
78     to_and_cc.append(&mut cc);
79   }
80   to_and_cc
81 }
82
83 pub(crate) fn is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
84 where
85   T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
86 {
87   let to_and_cc = get_activity_to_and_cc(activity);
88   if to_and_cc.contains(&public()) {
89     Ok(())
90   } else {
91     Err(anyhow!("Activity is not addressed to public").into())
92   }
93 }
94
95 pub(crate) async fn inbox_verify_http_signature<T, Kind>(
96   activity: &T,
97   context: &LemmyContext,
98   request: HttpRequest,
99   request_counter: &mut i32,
100 ) -> Result<Box<dyn ActorType>, LemmyError>
101 where
102   T: AsObject<Kind> + ActorAndObjectRefExt + Extends<Kind> + AsBase<Kind>,
103   Kind: Serialize,
104   <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
105 {
106   let actor_id = activity
107     .actor()?
108     .to_owned()
109     .single_xsd_any_uri()
110     .context(location_info!())?;
111   check_is_apub_id_valid(&actor_id)?;
112   let actor = get_or_fetch_and_upsert_actor(&actor_id, &context, request_counter).await?;
113   verify_signature(&request, actor.as_ref())?;
114   Ok(actor)
115 }
116
117 /// Returns true if `to_and_cc` contains at least one local user.
118 pub(crate) async fn is_addressed_to_local_user(
119   to_and_cc: &[Url],
120   pool: &DbPool,
121 ) -> Result<bool, LemmyError> {
122   for url in to_and_cc {
123     let url = url.to_owned();
124     let user = blocking(&pool, move |conn| {
125       User_::read_from_apub_id(&conn, &url.into())
126     })
127     .await?;
128     if let Ok(u) = user {
129       if u.local {
130         return Ok(true);
131       }
132     }
133   }
134   Ok(false)
135 }
136
137 /// If `to_and_cc` contains the followers collection of a remote community, returns this community
138 /// (like `https://example.com/c/main/followers`)
139 pub(crate) async fn is_addressed_to_community_followers(
140   to_and_cc: &[Url],
141   pool: &DbPool,
142 ) -> Result<Option<Community>, LemmyError> {
143   for url in to_and_cc {
144     let url = url.to_string();
145     // TODO: extremely hacky, we should just store the followers url for each community in the db
146     if url.ends_with("/followers") {
147       let community_url = Url::parse(&url.replace("/followers", ""))?;
148       let community = blocking(&pool, move |conn| {
149         Community::read_from_apub_id(&conn, &community_url.into())
150       })
151       .await??;
152       if !community.local {
153         return Ok(Some(community));
154       }
155     }
156   }
157   Ok(None)
158 }
159
160 pub(in crate::inbox) fn assert_activity_not_local<T, Kind>(activity: &T) -> Result<(), LemmyError>
161 where
162   T: BaseExt<Kind> + Debug,
163 {
164   let id = activity.id_unchecked().context(location_info!())?;
165   let activity_domain = id.domain().context(location_info!())?;
166
167   if activity_domain == Settings::get().hostname {
168     return Err(
169       anyhow!(
170         "Error: received activity which was sent by local instance: {:?}",
171         activity
172       )
173       .into(),
174     );
175   }
176   Ok(())
177 }