3 comment::{receive_create_comment, receive_update_comment},
5 receive_delete_community,
6 receive_remove_community,
7 receive_undo_delete_community,
8 receive_undo_remove_community,
11 receive_create_private_message,
12 receive_delete_private_message,
13 receive_undo_delete_private_message,
14 receive_update_private_message,
16 receive_unhandled_activity,
17 verify_activity_domains_valid,
19 check_is_apub_id_valid,
20 fetcher::get_or_fetch_and_upsert_community,
23 get_activity_to_and_cc,
24 inbox_verify_http_signature,
25 is_activity_already_known,
26 is_addressed_to_public,
27 receive_for_community::{
28 receive_create_for_community,
29 receive_delete_for_community,
30 receive_dislike_for_community,
31 receive_like_for_community,
32 receive_remove_for_community,
33 receive_undo_for_community,
34 receive_update_for_community,
40 use activitystreams::{
41 activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Undo, Update},
45 use actix_web::{web, HttpRequest, HttpResponse};
46 use anyhow::{anyhow, Context};
49 community::{Community, CommunityFollower, CommunityFollowerForm},
50 private_message::PrivateMessage,
54 use lemmy_structs::blocking;
55 use lemmy_utils::{location_info, LemmyError};
56 use lemmy_websocket::LemmyContext;
58 use serde::{Deserialize, Serialize};
62 /// Allowed activities for user inbox.
63 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
64 #[serde(rename_all = "PascalCase")]
65 pub enum UserValidTypes {
66 Accept, // community accepted our follow request
67 Create, // create private message
68 Update, // edit private message
69 Delete, // private message or community deleted by creator
70 Undo, // private message or community restored
71 Remove, // community removed by admin
72 Announce, // post, comment or vote in community
75 pub type UserAcceptedActivities = ActorAndObject<UserValidTypes>;
77 /// Handler for all incoming activities to user inboxes.
78 pub async fn user_inbox(
80 input: web::Json<UserAcceptedActivities>,
81 path: web::Path<String>,
82 context: web::Data<LemmyContext>,
83 ) -> Result<HttpResponse, LemmyError> {
84 let activity = input.into_inner();
85 // First of all check the http signature
86 let request_counter = &mut 0;
87 let actor = inbox_verify_http_signature(&activity, &context, request, request_counter).await?;
89 // Do nothing if we received the same activity before
90 let activity_id = get_activity_id(&activity, &actor.actor_id()?)?;
91 if is_activity_already_known(context.pool(), &activity_id).await? {
92 return Ok(HttpResponse::Ok().finish());
95 // Check if the activity is actually meant for us
96 let username = path.into_inner();
97 let user = blocking(&context.pool(), move |conn| {
98 User_::read_from_name(&conn, &username)
101 let to_and_cc = get_activity_to_and_cc(&activity)?;
102 if !to_and_cc.contains(&&user.actor_id()?) {
103 return Err(anyhow!("Activity delivered to wrong user").into());
106 insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
109 "User {} received activity {:?} from {}",
111 &activity.id_unchecked(),
112 &actor.actor_id_str()
115 user_receive_message(
125 /// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
126 pub(crate) async fn user_receive_message(
127 activity: UserAcceptedActivities,
128 to_user: Option<User_>,
129 actor: &dyn ActorType,
130 context: &LemmyContext,
131 request_counter: &mut i32,
132 ) -> Result<HttpResponse, LemmyError> {
133 // TODO: must be addressed to one or more local users, or to followers of a remote community
135 // TODO: if it is addressed to community followers, check that at least one local user is following it
137 let any_base = activity.clone().into_any_base()?;
138 let kind = activity.kind().context(location_info!())?;
139 let actor_url = actor.actor_id()?;
141 UserValidTypes::Accept => {
142 receive_accept(&context, any_base, actor, to_user.unwrap(), request_counter).await?;
144 UserValidTypes::Announce => {
145 receive_announce(&context, any_base, actor, request_counter).await?
147 UserValidTypes::Create => {
148 receive_create(&context, any_base, actor_url, request_counter).await?
150 UserValidTypes::Update => {
151 receive_update(&context, any_base, actor_url, request_counter).await?
153 UserValidTypes::Delete => {
154 receive_delete(context, any_base, &actor_url, request_counter).await?
156 UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
157 UserValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?,
160 // TODO: would be logical to move websocket notification code here
162 Ok(HttpResponse::Ok().finish())
165 /// Handle accepted follows.
166 async fn receive_accept(
167 context: &LemmyContext,
169 actor: &dyn ActorType,
171 request_counter: &mut i32,
172 ) -> Result<(), LemmyError> {
173 let accept = Accept::from_any_base(activity)?.context(location_info!())?;
174 verify_activity_domains_valid(&accept, &actor.actor_id()?, false)?;
176 // TODO: we should check that we actually sent this activity, because the remote instance
177 // could just put a fake Follow
178 let object = accept.object().to_owned().one().context(location_info!())?;
179 let follow = Follow::from_any_base(object)?.context(location_info!())?;
180 verify_activity_domains_valid(&follow, &user.actor_id()?, false)?;
182 let community_uri = accept
185 .single_xsd_any_uri()
186 .context(location_info!())?;
189 get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
191 // Now you need to add this to the community follower
192 let community_follower_form = CommunityFollowerForm {
193 community_id: community.id,
197 // This will fail if they're already a follower
198 blocking(&context.pool(), move |conn| {
199 CommunityFollower::follow(conn, &community_follower_form).ok()
206 /// Takes an announce and passes the inner activity to the appropriate handler.
207 async fn receive_announce(
208 context: &LemmyContext,
210 actor: &dyn ActorType,
211 request_counter: &mut i32,
212 ) -> Result<(), LemmyError> {
213 let announce = Announce::from_any_base(activity)?.context(location_info!())?;
214 verify_activity_domains_valid(&announce, &actor.actor_id()?, false)?;
215 is_addressed_to_public(&announce)?;
217 let kind = announce.object().as_single_kind_str();
218 let inner_activity = announce
222 .context(location_info!())?;
224 let inner_id = inner_activity.id().context(location_info!())?.to_owned();
225 check_is_apub_id_valid(&inner_id)?;
226 if is_activity_already_known(context.pool(), &inner_id).await? {
232 receive_create_for_community(context, inner_activity, &inner_id, request_counter).await
235 receive_update_for_community(context, inner_activity, &inner_id, request_counter).await
238 receive_like_for_community(context, inner_activity, &inner_id, request_counter).await
241 receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
243 Some("Delete") => receive_delete_for_community(context, inner_activity, &inner_id).await,
244 Some("Remove") => receive_remove_for_community(context, inner_activity, &inner_id).await,
246 receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
248 _ => receive_unhandled_activity(inner_activity),
252 async fn receive_create(
253 context: &LemmyContext,
255 expected_domain: Url,
256 request_counter: &mut i32,
257 ) -> Result<(), LemmyError> {
258 let create = Create::from_any_base(activity)?.context(location_info!())?;
259 verify_activity_domains_valid(&create, &expected_domain, true)?;
260 if is_addressed_to_public(&create).is_ok() {
261 receive_create_comment(create, context, request_counter).await
263 receive_create_private_message(&context, create, expected_domain, request_counter).await
267 async fn receive_update(
268 context: &LemmyContext,
270 expected_domain: Url,
271 request_counter: &mut i32,
272 ) -> Result<(), LemmyError> {
273 let update = Update::from_any_base(activity)?.context(location_info!())?;
274 verify_activity_domains_valid(&update, &expected_domain, true)?;
275 if is_addressed_to_public(&update).is_ok() {
276 receive_update_comment(update, context, request_counter).await
278 receive_update_private_message(&context, update, expected_domain, request_counter).await
282 async fn receive_delete(
283 context: &LemmyContext,
285 expected_domain: &Url,
286 request_counter: &mut i32,
287 ) -> Result<(), LemmyError> {
288 use CommunityOrPrivateMessage::*;
290 let delete = Delete::from_any_base(any_base.clone())?.context(location_info!())?;
291 verify_activity_domains_valid(&delete, expected_domain, true)?;
292 let object_uri = delete
295 .single_xsd_any_uri()
296 .context(location_info!())?;
298 match find_community_or_private_message_by_id(context, object_uri).await? {
299 Community(c) => receive_delete_community(context, c).await,
300 PrivateMessage(p) => receive_delete_private_message(context, delete, p, request_counter).await,
304 async fn receive_undo(
305 context: &LemmyContext,
307 expected_domain: &Url,
308 request_counter: &mut i32,
309 ) -> Result<(), LemmyError> {
310 use CommunityOrPrivateMessage::*;
311 let undo = Undo::from_any_base(any_base)?.context(location_info!())?;
312 verify_activity_domains_valid(&undo, expected_domain, true)?;
314 let inner_activity = undo.object().to_owned().one().context(location_info!())?;
315 let kind = inner_activity.kind_str();
318 let delete = Delete::from_any_base(inner_activity)?.context(location_info!())?;
319 verify_activity_domains_valid(&delete, expected_domain, true)?;
320 let object_uri = delete
323 .single_xsd_any_uri()
324 .context(location_info!())?;
325 match find_community_or_private_message_by_id(context, object_uri).await? {
326 Community(c) => receive_undo_delete_community(context, undo, c, expected_domain).await,
327 PrivateMessage(p) => {
328 receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter)
333 Some("Remove") => receive_undo_remove_community(context, undo, expected_domain).await,
334 _ => receive_unhandled_activity(undo),
337 enum CommunityOrPrivateMessage {
338 Community(Community),
339 PrivateMessage(PrivateMessage),
342 async fn find_community_or_private_message_by_id(
343 context: &LemmyContext,
345 ) -> Result<CommunityOrPrivateMessage, LemmyError> {
346 let ap_id = apub_id.to_string();
347 let community = blocking(context.pool(), move |conn| {
348 Community::read_from_actor_id(conn, &ap_id)
351 if let Ok(c) = community {
352 return Ok(CommunityOrPrivateMessage::Community(c));
355 let ap_id = apub_id.to_string();
356 let private_message = blocking(context.pool(), move |conn| {
357 PrivateMessage::read_from_apub_id(conn, &ap_id)
360 if let Ok(p) = private_message {
361 return Ok(CommunityOrPrivateMessage::PrivateMessage(p));
364 return Err(NotFound.into());