2 activities::receive::verify_activity_domains_valid,
3 check_is_apub_id_valid,
4 extensions::signatures::verify_signature,
5 fetcher::get_or_fetch_and_upsert_user,
6 inbox::{get_activity_id, is_activity_already_known},
10 use activitystreams::{
11 activity::{ActorAndObject, Follow, Undo},
15 use actix_web::{web, HttpRequest, HttpResponse};
16 use anyhow::{anyhow, Context};
18 community::{Community, CommunityFollower, CommunityFollowerForm},
22 use lemmy_structs::blocking;
23 use lemmy_utils::{location_info, LemmyError};
24 use lemmy_websocket::LemmyContext;
26 use serde::{Deserialize, Serialize};
29 /// Allowed activities for community inbox.
30 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
31 #[serde(rename_all = "PascalCase")]
37 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
39 /// Handler for all incoming receive to community inboxes.
40 pub async fn community_inbox(
42 input: web::Json<AcceptedActivities>,
43 path: web::Path<String>,
44 context: web::Data<LemmyContext>,
45 ) -> Result<HttpResponse, LemmyError> {
46 let activity = input.into_inner();
48 let path = path.into_inner();
49 let community = blocking(&context.pool(), move |conn| {
50 Community::read_from_name(&conn, &path)
56 .context(location_info!())?
58 .single_xsd_any_uri();
59 if Some(community.actor_id()?) != to {
60 return Err(anyhow!("Activity delivered to wrong community").into());
64 "Community {} received activity {:?}",
65 &community.name, &activity
67 let user_uri = activity
69 .as_single_xsd_any_uri()
70 .context(location_info!())?;
72 "Community {} inbox received activity {:?} from {}",
74 &activity.id_unchecked(),
77 check_is_apub_id_valid(user_uri)?;
79 let request_counter = &mut 0;
80 let user = get_or_fetch_and_upsert_user(&user_uri, &context, request_counter).await?;
82 verify_signature(&request, &user)?;
84 let activity_id = get_activity_id(&activity, user_uri)?;
85 if is_activity_already_known(context.pool(), &activity_id).await? {
86 return Ok(HttpResponse::Ok().finish());
89 let any_base = activity.clone().into_any_base()?;
90 let kind = activity.kind().context(location_info!())?;
91 let res = match kind {
92 ValidTypes::Follow => handle_follow(any_base, user, community, &context).await,
93 ValidTypes::Undo => handle_undo_follow(any_base, user, community, &context).await,
96 insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
100 /// Handle a follow request from a remote user, adding the user as follower and returning an
102 async fn handle_follow(
105 community: Community,
106 context: &LemmyContext,
107 ) -> Result<HttpResponse, LemmyError> {
108 let follow = Follow::from_any_base(activity)?.context(location_info!())?;
109 verify_activity_domains_valid(&follow, user.actor_id()?, false)?;
111 let community_follower_form = CommunityFollowerForm {
112 community_id: community.id,
116 // This will fail if they're already a follower, but ignore the error.
117 blocking(&context.pool(), move |conn| {
118 CommunityFollower::follow(&conn, &community_follower_form).ok()
122 community.send_accept_follow(follow, context).await?;
124 Ok(HttpResponse::Ok().finish())
127 /// Handle `Undo/Follow` from a user, removing the user from followers list.
128 async fn handle_undo_follow(
131 community: Community,
132 context: &LemmyContext,
133 ) -> Result<HttpResponse, LemmyError> {
134 let undo = Undo::from_any_base(activity)?.context(location_info!())?;
135 verify_activity_domains_valid(&undo, user.actor_id()?, true)?;
137 let object = undo.object().to_owned().one().context(location_info!())?;
138 let follow = Follow::from_any_base(object)?.context(location_info!())?;
139 verify_activity_domains_valid(&follow, user.actor_id()?, false)?;
141 let community_follower_form = CommunityFollowerForm {
142 community_id: community.id,
146 // This will fail if they aren't a follower, but ignore the error.
147 blocking(&context.pool(), move |conn| {
148 CommunityFollower::unfollow(&conn, &community_follower_form).ok()
152 Ok(HttpResponse::Ok().finish())