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