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