2021-03-30 20:23:50 +00:00
|
|
|
use crate::inbox::{
|
|
|
|
assert_activity_not_local,
|
|
|
|
community_inbox::{community_receive_message, CommunityAcceptedActivities},
|
|
|
|
get_activity_id,
|
|
|
|
inbox_verify_http_signature,
|
|
|
|
is_activity_already_known,
|
|
|
|
is_addressed_to_community_followers,
|
|
|
|
is_addressed_to_local_person,
|
|
|
|
person_inbox::{person_receive_message, PersonAcceptedActivities},
|
2020-07-23 14:36:45 +00:00
|
|
|
};
|
2020-10-28 16:14:18 +00:00
|
|
|
use activitystreams::{activity::ActorAndObject, prelude::*};
|
2020-08-18 13:43:50 +00:00
|
|
|
use actix_web::{web, HttpRequest, HttpResponse};
|
2020-10-28 16:14:18 +00:00
|
|
|
use anyhow::Context;
|
2021-03-25 19:19:40 +00:00
|
|
|
use lemmy_api_common::blocking;
|
2021-03-30 20:23:50 +00:00
|
|
|
use lemmy_apub::{get_activity_to_and_cc, insert_activity};
|
2020-12-21 23:27:42 +00:00
|
|
|
use lemmy_db_queries::{ApubObject, DbPool};
|
2020-12-21 12:28:12 +00:00
|
|
|
use lemmy_db_schema::source::community::Community;
|
2020-09-01 14:25:34 +00:00
|
|
|
use lemmy_utils::{location_info, LemmyError};
|
2020-09-24 13:53:21 +00:00
|
|
|
use lemmy_websocket::LemmyContext;
|
2020-08-04 14:39:55 +00:00
|
|
|
use serde::{Deserialize, Serialize};
|
2020-07-28 16:08:28 +00:00
|
|
|
use std::fmt::Debug;
|
2020-10-21 17:37:50 +00:00
|
|
|
use url::Url;
|
2020-07-23 14:36:45 +00:00
|
|
|
|
2020-10-19 14:29:35 +00:00
|
|
|
/// Allowed activity types for shared inbox.
|
2020-08-04 14:39:55 +00:00
|
|
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
|
2020-07-23 14:36:45 +00:00
|
|
|
#[serde(rename_all = "PascalCase")]
|
|
|
|
pub enum ValidTypes {
|
|
|
|
Create,
|
|
|
|
Update,
|
|
|
|
Like,
|
|
|
|
Dislike,
|
|
|
|
Delete,
|
|
|
|
Undo,
|
|
|
|
Remove,
|
|
|
|
Announce,
|
2021-03-09 17:13:08 +00:00
|
|
|
Add,
|
2021-04-09 15:01:26 +00:00
|
|
|
Block,
|
2020-07-23 14:36:45 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 14:10:09 +00:00
|
|
|
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,
|
2020-10-28 16:14:18 +00:00
|
|
|
// but it still works due to the anybase conversion
|
2020-07-23 14:36:45 +00:00
|
|
|
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
|
|
|
|
|
2020-10-19 14:29:35 +00:00
|
|
|
/// Handler for all incoming requests to shared inbox.
|
2020-07-23 14:36:45 +00:00
|
|
|
pub async fn shared_inbox(
|
|
|
|
request: HttpRequest,
|
|
|
|
input: web::Json<AcceptedActivities>,
|
2020-08-18 13:43:50 +00:00
|
|
|
context: web::Data<LemmyContext>,
|
2020-07-23 14:36:45 +00:00
|
|
|
) -> Result<HttpResponse, LemmyError> {
|
|
|
|
let activity = input.into_inner();
|
2020-10-28 16:14:18 +00:00
|
|
|
// First of all check the http signature
|
2020-10-22 18:27:32 +00:00
|
|
|
let request_counter = &mut 0;
|
2020-10-28 16:14:18 +00:00
|
|
|
let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
|
2020-07-23 14:36:45 +00:00
|
|
|
|
2020-10-28 16:14:18 +00:00
|
|
|
// Do nothing if we received the same activity before
|
2021-01-27 16:42:23 +00:00
|
|
|
let actor_id = actor.actor_id();
|
2020-10-23 12:29:56 +00:00
|
|
|
let activity_id = get_activity_id(&activity, &actor_id)?;
|
|
|
|
if is_activity_already_known(context.pool(), &activity_id).await? {
|
|
|
|
return Ok(HttpResponse::Ok().finish());
|
|
|
|
}
|
|
|
|
|
2020-12-01 18:30:15 +00:00
|
|
|
assert_activity_not_local(&activity)?;
|
2020-10-28 16:14:18 +00:00
|
|
|
// Log the activity, so we avoid receiving and parsing it twice. Note that this could still happen
|
|
|
|
// if we receive the same activity twice in very quick succession.
|
2020-11-06 13:06:47 +00:00
|
|
|
insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
|
2020-10-21 17:37:50 +00:00
|
|
|
|
2020-10-28 16:14:18 +00:00
|
|
|
let activity_any_base = activity.clone().into_any_base()?;
|
|
|
|
let mut res: Option<HttpResponse> = None;
|
2020-12-03 03:39:31 +00:00
|
|
|
let to_and_cc = get_activity_to_and_cc(&activity);
|
2020-10-28 16:14:18 +00:00
|
|
|
// Handle community first, so in case the sender is banned by the community, it will error out.
|
2021-03-10 22:33:55 +00:00
|
|
|
// If we handled the person receive first, the activity would be inserted to the database before the
|
2020-10-28 16:14:18 +00:00
|
|
|
// community could check for bans.
|
2021-03-10 22:33:55 +00:00
|
|
|
// Note that an activity can be addressed to a community and to a person (or multiple persons) at the
|
2020-11-16 20:41:47 +00:00
|
|
|
// same time. In this case we still only handle it once, to avoid duplicate websocket
|
|
|
|
// notifications.
|
2020-10-28 16:14:18 +00:00
|
|
|
let community = extract_local_community_from_destinations(&to_and_cc, context.pool()).await?;
|
|
|
|
if let Some(community) = community {
|
|
|
|
let community_activity = CommunityAcceptedActivities::from_any_base(activity_any_base.clone())?
|
|
|
|
.context(location_info!())?;
|
|
|
|
res = Some(
|
2021-03-23 16:03:14 +00:00
|
|
|
Box::pin(community_receive_message(
|
2020-10-28 16:14:18 +00:00
|
|
|
community_activity,
|
|
|
|
community,
|
|
|
|
actor.as_ref(),
|
|
|
|
&context,
|
|
|
|
request_counter,
|
2021-03-23 16:03:14 +00:00
|
|
|
))
|
2020-10-28 16:14:18 +00:00
|
|
|
.await?,
|
|
|
|
);
|
2021-03-10 22:33:55 +00:00
|
|
|
} else if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
|
|
|
|
let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
|
2020-10-28 16:14:18 +00:00
|
|
|
.context(location_info!())?;
|
2021-03-10 22:33:55 +00:00
|
|
|
// `to_person` is only used for follow activities (which we dont receive here), so no need to pass
|
2020-10-28 16:14:18 +00:00
|
|
|
// it in
|
2021-03-23 16:03:14 +00:00
|
|
|
Box::pin(person_receive_message(
|
2021-03-10 22:33:55 +00:00
|
|
|
person_activity,
|
2020-10-28 16:14:18 +00:00
|
|
|
None,
|
|
|
|
actor.as_ref(),
|
|
|
|
&context,
|
|
|
|
request_counter,
|
2021-03-23 16:03:14 +00:00
|
|
|
))
|
2020-10-28 16:14:18 +00:00
|
|
|
.await?;
|
2020-11-16 20:41:47 +00:00
|
|
|
} else if is_addressed_to_community_followers(&to_and_cc, context.pool())
|
2020-11-11 16:40:45 +00:00
|
|
|
.await?
|
|
|
|
.is_some()
|
|
|
|
{
|
2021-03-10 22:33:55 +00:00
|
|
|
let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
|
2020-10-28 16:14:18 +00:00
|
|
|
.context(location_info!())?;
|
|
|
|
res = Some(
|
2021-03-23 16:03:14 +00:00
|
|
|
Box::pin(person_receive_message(
|
2021-03-10 22:33:55 +00:00
|
|
|
person_activity,
|
2020-10-28 16:14:18 +00:00
|
|
|
None,
|
|
|
|
actor.as_ref(),
|
|
|
|
&context,
|
|
|
|
request_counter,
|
2021-03-23 16:03:14 +00:00
|
|
|
))
|
2020-10-28 16:14:18 +00:00
|
|
|
.await?,
|
|
|
|
);
|
2020-10-21 17:37:50 +00:00
|
|
|
}
|
|
|
|
|
2020-10-28 16:14:18 +00:00
|
|
|
// If none of those, throw an error
|
|
|
|
if let Some(r) = res {
|
|
|
|
Ok(r)
|
|
|
|
} else {
|
|
|
|
Ok(HttpResponse::NotImplemented().finish())
|
2020-10-21 17:37:50 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 16:14:18 +00:00
|
|
|
/// If `to_and_cc` contains the ID of a local community, return that community, otherwise return
|
|
|
|
/// None.
|
|
|
|
///
|
|
|
|
/// This doesnt handle the case where an activity is addressed to multiple communities (because
|
|
|
|
/// Lemmy doesnt generate such activities).
|
|
|
|
async fn extract_local_community_from_destinations(
|
|
|
|
to_and_cc: &[Url],
|
|
|
|
pool: &DbPool,
|
|
|
|
) -> Result<Option<Community>, LemmyError> {
|
|
|
|
for url in to_and_cc {
|
2021-01-27 16:42:23 +00:00
|
|
|
let url = url.to_owned();
|
|
|
|
let community = blocking(&pool, move |conn| {
|
|
|
|
Community::read_from_apub_id(&conn, &url.into())
|
|
|
|
})
|
|
|
|
.await?;
|
2020-10-28 16:14:18 +00:00
|
|
|
if let Ok(c) = community {
|
|
|
|
if c.local {
|
|
|
|
return Ok(Some(c));
|
|
|
|
}
|
2020-10-22 18:27:32 +00:00
|
|
|
}
|
2020-10-21 17:37:50 +00:00
|
|
|
}
|
2020-10-28 16:14:18 +00:00
|
|
|
Ok(None)
|
2020-10-21 17:37:50 +00:00
|
|
|
}
|