]> Untitled Git - lemmy.git/blob - src/apub/inbox/shared_inbox.rs
Isomorphic docker (#1124)
[lemmy.git] / src / apub / inbox / shared_inbox.rs
1 use crate::{
2   apub::{
3     check_is_apub_id_valid,
4     community::do_announce,
5     extensions::signatures::verify,
6     fetcher::{
7       get_or_fetch_and_upsert_actor,
8       get_or_fetch_and_upsert_community,
9       get_or_fetch_and_upsert_user,
10     },
11     inbox::activities::{
12       announce::receive_announce,
13       create::receive_create,
14       delete::receive_delete,
15       dislike::receive_dislike,
16       like::receive_like,
17       remove::receive_remove,
18       undo::receive_undo,
19       update::receive_update,
20     },
21     insert_activity,
22   },
23   LemmyContext,
24 };
25 use activitystreams::{
26   activity::{ActorAndObject, ActorAndObjectRef},
27   base::{AsBase, Extends},
28   object::AsObject,
29   prelude::*,
30 };
31 use actix_web::{web, HttpRequest, HttpResponse};
32 use anyhow::Context;
33 use lemmy_db::user::User_;
34 use lemmy_utils::{location_info, LemmyError};
35 use log::debug;
36 use serde::{Deserialize, Serialize};
37 use std::fmt::Debug;
38 use url::Url;
39
40 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
41 #[serde(rename_all = "PascalCase")]
42 pub enum ValidTypes {
43   Create,
44   Update,
45   Like,
46   Dislike,
47   Delete,
48   Undo,
49   Remove,
50   Announce,
51 }
52
53 // TODO: this isnt entirely correct, cause some of these activities are not ActorAndObject,
54 //       but it might still work due to the anybase conversion
55 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
56
57 /// Handler for all incoming activities to user inboxes.
58 pub async fn shared_inbox(
59   request: HttpRequest,
60   input: web::Json<AcceptedActivities>,
61   context: web::Data<LemmyContext>,
62 ) -> Result<HttpResponse, LemmyError> {
63   let activity = input.into_inner();
64
65   let json = serde_json::to_string(&activity)?;
66   debug!("Shared inbox received activity: {}", json);
67
68   // TODO: if we already received an activity with identical ID, then ignore this (same in other inboxes)
69
70   let sender = &activity
71     .actor()?
72     .to_owned()
73     .single_xsd_any_uri()
74     .context(location_info!())?;
75   let community = get_community_id_from_activity(&activity)?;
76
77   check_is_apub_id_valid(sender)?;
78   check_is_apub_id_valid(&community)?;
79
80   let actor = get_or_fetch_and_upsert_actor(sender, &context).await?;
81   verify(&request, actor.as_ref())?;
82
83   let any_base = activity.clone().into_any_base()?;
84   let kind = activity.kind().context(location_info!())?;
85   let res = match kind {
86     ValidTypes::Announce => receive_announce(any_base, &context).await,
87     ValidTypes::Create => receive_create(any_base, &context).await,
88     ValidTypes::Update => receive_update(any_base, &context).await,
89     ValidTypes::Like => receive_like(any_base, &context).await,
90     ValidTypes::Dislike => receive_dislike(any_base, &context).await,
91     ValidTypes::Remove => receive_remove(any_base, &context).await,
92     ValidTypes::Delete => receive_delete(any_base, &context).await,
93     ValidTypes::Undo => receive_undo(any_base, &context).await,
94   };
95
96   insert_activity(actor.user_id(), activity.clone(), false, context.pool()).await?;
97   res
98 }
99
100 pub(in crate::apub::inbox) fn receive_unhandled_activity<A>(
101   activity: A,
102 ) -> Result<HttpResponse, LemmyError>
103 where
104   A: Debug,
105 {
106   debug!("received unhandled activity type: {:?}", activity);
107   Ok(HttpResponse::NotImplemented().finish())
108 }
109
110 pub(in crate::apub::inbox) async fn get_user_from_activity<T, A>(
111   activity: &T,
112   context: &LemmyContext,
113 ) -> Result<User_, LemmyError>
114 where
115   T: AsBase<A> + ActorAndObjectRef,
116 {
117   let actor = activity.actor()?;
118   let user_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
119   get_or_fetch_and_upsert_user(&user_uri, context).await
120 }
121
122 pub(in crate::apub::inbox) fn get_community_id_from_activity<T, A>(
123   activity: &T,
124 ) -> Result<Url, LemmyError>
125 where
126   T: AsBase<A> + ActorAndObjectRef + AsObject<A>,
127 {
128   let cc = activity.cc().context(location_info!())?;
129   let cc = cc.as_many().context(location_info!())?;
130   Ok(
131     cc.first()
132       .context(location_info!())?
133       .as_xsd_any_uri()
134       .context(location_info!())?
135       .to_owned(),
136   )
137 }
138
139 pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
140   activity: T,
141   user: &User_,
142   context: &LemmyContext,
143 ) -> Result<(), LemmyError>
144 where
145   T: AsObject<Kind>,
146   T: Extends<Kind>,
147   Kind: Serialize,
148   <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
149 {
150   let cc = activity.cc().context(location_info!())?;
151   let cc = cc.as_many().context(location_info!())?;
152   let community_followers_uri = cc
153     .first()
154     .context(location_info!())?
155     .as_xsd_any_uri()
156     .context(location_info!())?;
157   // TODO: this is hacky but seems to be the only way to get the community ID
158   let community_uri = community_followers_uri
159     .to_string()
160     .replace("/followers", "");
161   let community = get_or_fetch_and_upsert_community(&Url::parse(&community_uri)?, context).await?;
162
163   if community.local {
164     do_announce(activity.into_any_base()?, &community, &user, context).await?;
165   }
166   Ok(())
167 }