]> Untitled Git - lemmy.git/blob - server/src/apub/inbox/community_inbox.rs
b27e6b6a90f521c15ecc384d3c12a435093d5cf9
[lemmy.git] / server / src / apub / inbox / community_inbox.rs
1 use crate::{
2   apub::{
3     check_is_apub_id_valid,
4     extensions::signatures::verify,
5     fetcher::get_or_fetch_and_upsert_user,
6     insert_activity,
7     ActorType,
8   },
9   LemmyContext,
10 };
11 use activitystreams::{
12   activity::{ActorAndObject, Follow, Undo},
13   base::AnyBase,
14   prelude::*,
15 };
16 use actix_web::{web, HttpRequest, HttpResponse};
17 use anyhow::{anyhow, Context};
18 use lemmy_api_structs::blocking;
19 use lemmy_db::{
20   community::{Community, CommunityFollower, CommunityFollowerForm},
21   user::User_,
22   Followable,
23 };
24 use lemmy_utils::{location_info, LemmyError};
25 use log::debug;
26 use serde::{Deserialize, Serialize};
27 use std::fmt::Debug;
28
29 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
30 #[serde(rename_all = "PascalCase")]
31 pub enum ValidTypes {
32   Follow,
33   Undo,
34 }
35
36 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
37
38 /// Handler for all incoming activities to community inboxes.
39 pub async fn community_inbox(
40   request: HttpRequest,
41   input: web::Json<AcceptedActivities>,
42   path: web::Path<String>,
43   context: web::Data<LemmyContext>,
44 ) -> Result<HttpResponse, LemmyError> {
45   let activity = input.into_inner();
46
47   let path = path.into_inner();
48   let community = blocking(&context.pool(), move |conn| {
49     Community::read_from_name(&conn, &path)
50   })
51   .await??;
52
53   if !community.local {
54     return Err(
55       anyhow!(
56         "Received activity is addressed to remote community {}",
57         &community.actor_id
58       )
59       .into(),
60     );
61   }
62   debug!(
63     "Community {} received activity {:?}",
64     &community.name, &activity
65   );
66   let user_uri = activity
67     .actor()?
68     .as_single_xsd_any_uri()
69     .context(location_info!())?;
70   check_is_apub_id_valid(user_uri)?;
71
72   let user = get_or_fetch_and_upsert_user(&user_uri, &context).await?;
73
74   verify(&request, &user)?;
75
76   let any_base = activity.clone().into_any_base()?;
77   let kind = activity.kind().context(location_info!())?;
78   let user_id = user.id;
79   let res = match kind {
80     ValidTypes::Follow => handle_follow(any_base, user, community, &context).await,
81     ValidTypes::Undo => handle_undo_follow(any_base, user, community, &context).await,
82   };
83
84   insert_activity(user_id, activity.clone(), false, context.pool()).await?;
85   res
86 }
87
88 /// Handle a follow request from a remote user, adding it to the local database and returning an
89 /// Accept activity.
90 async fn handle_follow(
91   activity: AnyBase,
92   user: User_,
93   community: Community,
94   context: &LemmyContext,
95 ) -> Result<HttpResponse, LemmyError> {
96   let follow = Follow::from_any_base(activity)?.context(location_info!())?;
97   let community_follower_form = CommunityFollowerForm {
98     community_id: community.id,
99     user_id: user.id,
100   };
101
102   // This will fail if they're already a follower, but ignore the error.
103   blocking(&context.pool(), move |conn| {
104     CommunityFollower::follow(&conn, &community_follower_form).ok()
105   })
106   .await?;
107
108   community.send_accept_follow(follow, context).await?;
109
110   Ok(HttpResponse::Ok().finish())
111 }
112
113 async fn handle_undo_follow(
114   activity: AnyBase,
115   user: User_,
116   community: Community,
117   context: &LemmyContext,
118 ) -> Result<HttpResponse, LemmyError> {
119   let _undo = Undo::from_any_base(activity)?.context(location_info!())?;
120
121   let community_follower_form = CommunityFollowerForm {
122     community_id: community.id,
123     user_id: user.id,
124   };
125
126   // This will fail if they aren't a follower, but ignore the error.
127   blocking(&context.pool(), move |conn| {
128     CommunityFollower::unfollow(&conn, &community_follower_form).ok()
129   })
130   .await?;
131
132   Ok(HttpResponse::Ok().finish())
133 }