4 receive_delete_community,
5 receive_remove_community,
6 receive_undo_delete_community,
7 receive_undo_remove_community,
10 receive_create_private_message,
11 receive_delete_private_message,
12 receive_undo_delete_private_message,
13 receive_update_private_message,
15 receive_unhandled_activity,
16 verify_activity_domains_valid,
18 check_is_apub_id_valid,
19 fetcher::community::get_or_fetch_and_upsert_community,
21 assert_activity_not_local,
23 get_activity_to_and_cc,
24 inbox_verify_http_signature,
25 is_activity_already_known,
26 is_addressed_to_community_followers,
27 is_addressed_to_local_user,
28 receive_for_community::{
29 receive_add_for_community,
30 receive_create_for_community,
31 receive_delete_for_community,
32 receive_dislike_for_community,
33 receive_like_for_community,
34 receive_remove_for_community,
35 receive_undo_for_community,
36 receive_update_for_community,
38 verify_is_addressed_to_public,
43 use activitystreams::{
44 activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Remove, Undo, Update},
48 use actix_web::{web, HttpRequest, HttpResponse};
49 use anyhow::{anyhow, Context};
51 use lemmy_api_structs::blocking;
52 use lemmy_db_queries::{source::user::User, ApubObject, Followable};
53 use lemmy_db_schema::source::{
54 community::{Community, CommunityFollower},
55 private_message::PrivateMessage,
58 use lemmy_utils::{location_info, LemmyError};
59 use lemmy_websocket::LemmyContext;
61 use serde::{Deserialize, Serialize};
63 use strum_macros::EnumString;
66 /// Allowed activities for user inbox.
67 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
68 #[serde(rename_all = "PascalCase")]
69 pub enum UserValidTypes {
70 Accept, // community accepted our follow request
71 Create, // create private message
72 Update, // edit private message
73 Delete, // private message or community deleted by creator
74 Undo, // private message or community restored
75 Remove, // community removed by admin
76 Announce, // post, comment or vote in community
79 pub type UserAcceptedActivities = ActorAndObject<UserValidTypes>;
81 /// Handler for all incoming activities to user inboxes.
82 pub async fn user_inbox(
84 input: web::Json<UserAcceptedActivities>,
85 path: web::Path<String>,
86 context: web::Data<LemmyContext>,
87 ) -> Result<HttpResponse, LemmyError> {
88 let activity = input.into_inner();
89 // First of all check the http signature
90 let request_counter = &mut 0;
91 let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
93 // Do nothing if we received the same activity before
94 let activity_id = get_activity_id(&activity, &actor.actor_id())?;
95 if is_activity_already_known(context.pool(), &activity_id).await? {
96 return Ok(HttpResponse::Ok().finish());
99 // Check if the activity is actually meant for us
100 let username = path.into_inner();
101 let user = blocking(&context.pool(), move |conn| {
102 User_::read_from_name(&conn, &username)
105 let to_and_cc = get_activity_to_and_cc(&activity);
106 // TODO: we should also accept activities that are sent to community followers
107 if !to_and_cc.contains(&&user.actor_id()) {
108 return Err(anyhow!("Activity delivered to wrong user").into());
111 assert_activity_not_local(&activity)?;
112 insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
115 "User {} received activity {:?} from {}",
117 &activity.id_unchecked(),
121 user_receive_message(
131 /// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
132 pub(crate) async fn user_receive_message(
133 activity: UserAcceptedActivities,
134 to_user: Option<User_>,
135 actor: &dyn ActorType,
136 context: &LemmyContext,
137 request_counter: &mut i32,
138 ) -> Result<HttpResponse, LemmyError> {
139 is_for_user_inbox(context, &activity).await?;
141 let any_base = activity.clone().into_any_base()?;
142 let kind = activity.kind().context(location_info!())?;
143 let actor_url = actor.actor_id();
145 UserValidTypes::Accept => {
150 to_user.expect("user provided"),
155 UserValidTypes::Announce => {
156 receive_announce(&context, any_base, actor, request_counter).await?
158 UserValidTypes::Create => {
159 receive_create(&context, any_base, actor_url, request_counter).await?
161 UserValidTypes::Update => {
162 receive_update(&context, any_base, actor_url, request_counter).await?
164 UserValidTypes::Delete => {
165 receive_delete(context, any_base, &actor_url, request_counter).await?
167 UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
168 UserValidTypes::Remove => receive_remove(context, any_base, &actor_url).await?,
171 // TODO: would be logical to move websocket notification code here
173 Ok(HttpResponse::Ok().finish())
176 /// Returns true if the activity is addressed directly to one or more local users, or if it is
177 /// addressed to the followers collection of a remote community, and at least one local user follows
179 async fn is_for_user_inbox(
180 context: &LemmyContext,
181 activity: &UserAcceptedActivities,
182 ) -> Result<(), LemmyError> {
183 let to_and_cc = get_activity_to_and_cc(activity);
184 // Check if it is addressed directly to any local user
185 if is_addressed_to_local_user(&to_and_cc, context.pool()).await? {
189 // Check if it is addressed to any followers collection of a remote community, and that the
190 // community has local followers.
191 let community = is_addressed_to_community_followers(&to_and_cc, context.pool()).await?;
192 if let Some(c) = community {
193 let community_id = c.id;
194 let has_local_followers = blocking(&context.pool(), move |conn| {
195 CommunityFollower::has_local_followers(conn, community_id)
200 anyhow!("Remote activity cant be addressed to followers of local community").into(),
203 if has_local_followers {
208 Err(anyhow!("Not addressed for any local user").into())
211 /// Handle accepted follows.
212 async fn receive_accept(
213 context: &LemmyContext,
215 actor: &dyn ActorType,
217 request_counter: &mut i32,
218 ) -> Result<(), LemmyError> {
219 let accept = Accept::from_any_base(activity)?.context(location_info!())?;
220 verify_activity_domains_valid(&accept, &actor.actor_id(), false)?;
222 let object = accept.object().to_owned().one().context(location_info!())?;
223 let follow = Follow::from_any_base(object)?.context(location_info!())?;
224 verify_activity_domains_valid(&follow, &user.actor_id(), false)?;
226 let community_uri = accept
229 .single_xsd_any_uri()
230 .context(location_info!())?;
233 get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
235 let community_id = community.id;
236 let user_id = user.id;
237 // This will throw an error if no follow was requested
238 blocking(&context.pool(), move |conn| {
239 CommunityFollower::follow_accepted(conn, community_id, user_id)
246 #[derive(EnumString)]
247 enum AnnouncableActivities {
258 /// Takes an announce and passes the inner activity to the appropriate handler.
259 pub async fn receive_announce(
260 context: &LemmyContext,
262 actor: &dyn ActorType,
263 request_counter: &mut i32,
264 ) -> Result<(), LemmyError> {
265 let announce = Announce::from_any_base(activity)?.context(location_info!())?;
266 verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
267 verify_is_addressed_to_public(&announce)?;
271 .as_single_kind_str()
272 .and_then(|s| s.parse().ok());
273 let inner_activity = announce
277 .context(location_info!())?;
279 let inner_id = inner_activity.id().context(location_info!())?.to_owned();
280 check_is_apub_id_valid(&inner_id)?;
281 if is_activity_already_known(context.pool(), &inner_id).await? {
285 use AnnouncableActivities::*;
288 receive_create_for_community(context, inner_activity, &inner_id, request_counter).await
291 receive_update_for_community(
301 receive_like_for_community(context, inner_activity, &inner_id, request_counter).await
304 receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
307 receive_delete_for_community(context, inner_activity, Some(announce), &inner_id).await
310 receive_remove_for_community(context, inner_activity, Some(announce), request_counter).await
313 receive_undo_for_community(
323 receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
325 _ => receive_unhandled_activity(inner_activity),
329 async fn receive_create(
330 context: &LemmyContext,
332 expected_domain: Url,
333 request_counter: &mut i32,
334 ) -> Result<(), LemmyError> {
335 let create = Create::from_any_base(activity)?.context(location_info!())?;
336 verify_activity_domains_valid(&create, &expected_domain, true)?;
337 receive_create_private_message(&context, create, expected_domain, request_counter).await
340 async fn receive_update(
341 context: &LemmyContext,
343 expected_domain: Url,
344 request_counter: &mut i32,
345 ) -> Result<(), LemmyError> {
346 let update = Update::from_any_base(activity)?.context(location_info!())?;
347 verify_activity_domains_valid(&update, &expected_domain, true)?;
348 receive_update_private_message(&context, update, expected_domain, request_counter).await
351 async fn receive_delete(
352 context: &LemmyContext,
354 expected_domain: &Url,
355 request_counter: &mut i32,
356 ) -> Result<(), LemmyError> {
357 use CommunityOrPrivateMessage::*;
359 let delete = Delete::from_any_base(any_base.clone())?.context(location_info!())?;
360 verify_activity_domains_valid(&delete, expected_domain, true)?;
361 let object_uri = delete
364 .single_xsd_any_uri()
365 .context(location_info!())?;
367 match find_community_or_private_message_by_id(context, object_uri).await? {
368 Community(c) => receive_delete_community(context, c).await,
369 PrivateMessage(p) => receive_delete_private_message(context, delete, p, request_counter).await,
373 async fn receive_remove(
374 context: &LemmyContext,
376 expected_domain: &Url,
377 ) -> Result<(), LemmyError> {
378 let remove = Remove::from_any_base(any_base.clone())?.context(location_info!())?;
379 verify_activity_domains_valid(&remove, expected_domain, true)?;
380 let object_uri = remove
383 .single_xsd_any_uri()
384 .context(location_info!())?;
385 let community = blocking(context.pool(), move |conn| {
386 Community::read_from_apub_id(conn, &object_uri.into())
389 receive_remove_community(&context, community).await
392 async fn receive_undo(
393 context: &LemmyContext,
395 expected_domain: &Url,
396 request_counter: &mut i32,
397 ) -> Result<(), LemmyError> {
398 let undo = Undo::from_any_base(any_base)?.context(location_info!())?;
399 verify_activity_domains_valid(&undo, expected_domain, true)?;
401 let inner_activity = undo.object().to_owned().one().context(location_info!())?;
402 let kind = inner_activity.kind_str();
405 let delete = Delete::from_any_base(inner_activity)?.context(location_info!())?;
406 verify_activity_domains_valid(&delete, expected_domain, true)?;
407 let object_uri = delete
410 .single_xsd_any_uri()
411 .context(location_info!())?;
412 use CommunityOrPrivateMessage::*;
413 match find_community_or_private_message_by_id(context, object_uri).await? {
414 Community(c) => receive_undo_delete_community(context, c).await,
415 PrivateMessage(p) => {
416 receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter)
422 let remove = Remove::from_any_base(inner_activity)?.context(location_info!())?;
423 let object_uri = remove
426 .single_xsd_any_uri()
427 .context(location_info!())?;
428 let community = blocking(context.pool(), move |conn| {
429 Community::read_from_apub_id(conn, &object_uri.into())
432 receive_undo_remove_community(context, community).await
434 _ => receive_unhandled_activity(undo),
437 enum CommunityOrPrivateMessage {
438 Community(Community),
439 PrivateMessage(PrivateMessage),
442 async fn find_community_or_private_message_by_id(
443 context: &LemmyContext,
445 ) -> Result<CommunityOrPrivateMessage, LemmyError> {
446 let ap_id = apub_id.to_owned();
447 let community = blocking(context.pool(), move |conn| {
448 Community::read_from_apub_id(conn, &ap_id.into())
451 if let Ok(c) = community {
452 return Ok(CommunityOrPrivateMessage::Community(c));
455 let ap_id = apub_id.to_owned();
456 let private_message = blocking(context.pool(), move |conn| {
457 PrivateMessage::read_from_apub_id(conn, &ap_id.into())
460 if let Ok(p) = private_message {
461 return Ok(CommunityOrPrivateMessage::PrivateMessage(p));