]> Untitled Git - lemmy.git/blob - lemmy_apub/src/inbox/activities/undo.rs
Fix nginx config for local federation setup (#104)
[lemmy.git] / lemmy_apub / src / inbox / activities / undo.rs
1 use crate::{
2   fetcher::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
3   inbox::shared_inbox::{
4     announce_if_community_is_local,
5     get_user_from_activity,
6     receive_unhandled_activity,
7   },
8   ActorType,
9   FromApub,
10   GroupExt,
11   PageExt,
12 };
13 use activitystreams::{
14   activity::*,
15   base::{AnyBase, AsBase},
16   object::Note,
17   prelude::*,
18 };
19 use actix_web::HttpResponse;
20 use anyhow::{anyhow, Context};
21 use lemmy_db::{
22   comment::{Comment, CommentForm, CommentLike},
23   comment_view::CommentView,
24   community::{Community, CommunityForm},
25   community_view::CommunityView,
26   naive_now,
27   post::{Post, PostForm, PostLike},
28   post_view::PostView,
29   Crud,
30   Likeable,
31 };
32 use lemmy_structs::{
33   blocking,
34   comment::CommentResponse,
35   community::CommunityResponse,
36   post::PostResponse,
37 };
38 use lemmy_utils::{location_info, LemmyError};
39 use lemmy_websocket::{
40   messages::{SendComment, SendCommunityRoomMessage, SendPost},
41   LemmyContext,
42   UserOperation,
43 };
44
45 pub async fn receive_undo(
46   activity: AnyBase,
47   context: &LemmyContext,
48 ) -> Result<HttpResponse, LemmyError> {
49   let undo = Undo::from_any_base(activity)?.context(location_info!())?;
50   match undo.object().as_single_kind_str() {
51     Some("Delete") => receive_undo_delete(undo, context).await,
52     Some("Remove") => receive_undo_remove(undo, context).await,
53     Some("Like") => receive_undo_like(undo, context).await,
54     Some("Dislike") => receive_undo_dislike(undo, context).await,
55     _ => receive_unhandled_activity(undo),
56   }
57 }
58
59 fn check_is_undo_valid<T, A>(outer_activity: &Undo, inner_activity: &T) -> Result<(), LemmyError>
60 where
61   T: AsBase<A> + ActorAndObjectRef,
62 {
63   let outer_actor = outer_activity.actor()?;
64   let outer_actor_uri = outer_actor
65     .as_single_xsd_any_uri()
66     .context(location_info!())?;
67
68   let inner_actor = inner_activity.actor()?;
69   let inner_actor_uri = inner_actor
70     .as_single_xsd_any_uri()
71     .context(location_info!())?;
72
73   if outer_actor_uri.domain() != inner_actor_uri.domain() {
74     Err(anyhow!("Cant undo activities from a different instance").into())
75   } else {
76     Ok(())
77   }
78 }
79
80 async fn receive_undo_delete(
81   undo: Undo,
82   context: &LemmyContext,
83 ) -> Result<HttpResponse, LemmyError> {
84   let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
85     .context(location_info!())?;
86   check_is_undo_valid(&undo, &delete)?;
87   let type_ = delete
88     .object()
89     .as_single_kind_str()
90     .context(location_info!())?;
91   match type_ {
92     "Note" => receive_undo_delete_comment(undo, &delete, context).await,
93     "Page" => receive_undo_delete_post(undo, &delete, context).await,
94     "Group" => receive_undo_delete_community(undo, &delete, context).await,
95     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
96   }
97 }
98
99 async fn receive_undo_remove(
100   undo: Undo,
101   context: &LemmyContext,
102 ) -> Result<HttpResponse, LemmyError> {
103   let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
104     .context(location_info!())?;
105   check_is_undo_valid(&undo, &remove)?;
106
107   let type_ = remove
108     .object()
109     .as_single_kind_str()
110     .context(location_info!())?;
111   match type_ {
112     "Note" => receive_undo_remove_comment(undo, &remove, context).await,
113     "Page" => receive_undo_remove_post(undo, &remove, context).await,
114     "Group" => receive_undo_remove_community(undo, &remove, context).await,
115     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
116   }
117 }
118
119 async fn receive_undo_like(undo: Undo, context: &LemmyContext) -> Result<HttpResponse, LemmyError> {
120   let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
121     .context(location_info!())?;
122   check_is_undo_valid(&undo, &like)?;
123
124   let type_ = like
125     .object()
126     .as_single_kind_str()
127     .context(location_info!())?;
128   match type_ {
129     "Note" => receive_undo_like_comment(undo, &like, context).await,
130     "Page" => receive_undo_like_post(undo, &like, context).await,
131     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
132   }
133 }
134
135 async fn receive_undo_dislike(
136   undo: Undo,
137   context: &LemmyContext,
138 ) -> Result<HttpResponse, LemmyError> {
139   let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
140     .context(location_info!())?;
141   check_is_undo_valid(&undo, &dislike)?;
142
143   let type_ = dislike
144     .object()
145     .as_single_kind_str()
146     .context(location_info!())?;
147   match type_ {
148     "Note" => receive_undo_dislike_comment(undo, &dislike, context).await,
149     "Page" => receive_undo_dislike_post(undo, &dislike, context).await,
150     d => Err(anyhow!("Undo Delete type {} not supported", d).into()),
151   }
152 }
153
154 async fn receive_undo_delete_comment(
155   undo: Undo,
156   delete: &Delete,
157   context: &LemmyContext,
158 ) -> Result<HttpResponse, LemmyError> {
159   let user = get_user_from_activity(delete, context).await?;
160   let note = Note::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
161     .context(location_info!())?;
162
163   let comment_ap_id = CommentForm::from_apub(&note, context, Some(user.actor_id()?))
164     .await?
165     .get_ap_id()?;
166
167   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
168
169   let comment_form = CommentForm {
170     content: comment.content.to_owned(),
171     parent_id: comment.parent_id,
172     post_id: comment.post_id,
173     creator_id: comment.creator_id,
174     removed: None,
175     deleted: Some(false),
176     read: None,
177     published: None,
178     updated: Some(naive_now()),
179     ap_id: Some(comment.ap_id),
180     local: comment.local,
181   };
182   let comment_id = comment.id;
183   blocking(context.pool(), move |conn| {
184     Comment::update(conn, comment_id, &comment_form)
185   })
186   .await??;
187
188   // Refetch the view
189   let comment_id = comment.id;
190   let comment_view = blocking(context.pool(), move |conn| {
191     CommentView::read(conn, comment_id, None)
192   })
193   .await??;
194
195   // TODO get those recipient actor ids from somewhere
196   let recipient_ids = vec![];
197   let res = CommentResponse {
198     comment: comment_view,
199     recipient_ids,
200     form_id: None,
201   };
202
203   context.chat_server().do_send(SendComment {
204     op: UserOperation::EditComment,
205     comment: res,
206     websocket_id: None,
207   });
208
209   announce_if_community_is_local(undo, &user, context).await?;
210   Ok(HttpResponse::Ok().finish())
211 }
212
213 async fn receive_undo_remove_comment(
214   undo: Undo,
215   remove: &Remove,
216   context: &LemmyContext,
217 ) -> Result<HttpResponse, LemmyError> {
218   let mod_ = get_user_from_activity(remove, context).await?;
219   let note = Note::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
220     .context(location_info!())?;
221
222   let comment_ap_id = CommentForm::from_apub(&note, context, None)
223     .await?
224     .get_ap_id()?;
225
226   let comment = get_or_fetch_and_insert_comment(&comment_ap_id, context).await?;
227
228   let comment_form = CommentForm {
229     content: comment.content.to_owned(),
230     parent_id: comment.parent_id,
231     post_id: comment.post_id,
232     creator_id: comment.creator_id,
233     removed: Some(false),
234     deleted: None,
235     read: None,
236     published: None,
237     updated: Some(naive_now()),
238     ap_id: Some(comment.ap_id),
239     local: comment.local,
240   };
241   let comment_id = comment.id;
242   blocking(context.pool(), move |conn| {
243     Comment::update(conn, comment_id, &comment_form)
244   })
245   .await??;
246
247   // Refetch the view
248   let comment_id = comment.id;
249   let comment_view = blocking(context.pool(), move |conn| {
250     CommentView::read(conn, comment_id, None)
251   })
252   .await??;
253
254   // TODO get those recipient actor ids from somewhere
255   let recipient_ids = vec![];
256   let res = CommentResponse {
257     comment: comment_view,
258     recipient_ids,
259     form_id: None,
260   };
261
262   context.chat_server().do_send(SendComment {
263     op: UserOperation::EditComment,
264     comment: res,
265     websocket_id: None,
266   });
267
268   announce_if_community_is_local(undo, &mod_, context).await?;
269   Ok(HttpResponse::Ok().finish())
270 }
271
272 async fn receive_undo_delete_post(
273   undo: Undo,
274   delete: &Delete,
275   context: &LemmyContext,
276 ) -> Result<HttpResponse, LemmyError> {
277   let user = get_user_from_activity(delete, context).await?;
278   let page = PageExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
279     .context(location_info!())?;
280
281   let post_ap_id = PostForm::from_apub(&page, context, Some(user.actor_id()?))
282     .await?
283     .get_ap_id()?;
284
285   let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
286
287   let post_form = PostForm {
288     name: post.name.to_owned(),
289     url: post.url.to_owned(),
290     body: post.body.to_owned(),
291     creator_id: post.creator_id.to_owned(),
292     community_id: post.community_id,
293     removed: None,
294     deleted: Some(false),
295     nsfw: post.nsfw,
296     locked: None,
297     stickied: None,
298     updated: Some(naive_now()),
299     embed_title: post.embed_title,
300     embed_description: post.embed_description,
301     embed_html: post.embed_html,
302     thumbnail_url: post.thumbnail_url,
303     ap_id: Some(post.ap_id),
304     local: post.local,
305     published: None,
306   };
307   let post_id = post.id;
308   blocking(context.pool(), move |conn| {
309     Post::update(conn, post_id, &post_form)
310   })
311   .await??;
312
313   // Refetch the view
314   let post_id = post.id;
315   let post_view = blocking(context.pool(), move |conn| {
316     PostView::read(conn, post_id, None)
317   })
318   .await??;
319
320   let res = PostResponse { post: post_view };
321
322   context.chat_server().do_send(SendPost {
323     op: UserOperation::EditPost,
324     post: res,
325     websocket_id: None,
326   });
327
328   announce_if_community_is_local(undo, &user, context).await?;
329   Ok(HttpResponse::Ok().finish())
330 }
331
332 async fn receive_undo_remove_post(
333   undo: Undo,
334   remove: &Remove,
335   context: &LemmyContext,
336 ) -> Result<HttpResponse, LemmyError> {
337   let mod_ = get_user_from_activity(remove, context).await?;
338   let page = PageExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
339     .context(location_info!())?;
340
341   let post_ap_id = PostForm::from_apub(&page, context, None)
342     .await?
343     .get_ap_id()?;
344
345   let post = get_or_fetch_and_insert_post(&post_ap_id, context).await?;
346
347   let post_form = PostForm {
348     name: post.name.to_owned(),
349     url: post.url.to_owned(),
350     body: post.body.to_owned(),
351     creator_id: post.creator_id.to_owned(),
352     community_id: post.community_id,
353     removed: Some(false),
354     deleted: None,
355     nsfw: post.nsfw,
356     locked: None,
357     stickied: None,
358     updated: Some(naive_now()),
359     embed_title: post.embed_title,
360     embed_description: post.embed_description,
361     embed_html: post.embed_html,
362     thumbnail_url: post.thumbnail_url,
363     ap_id: Some(post.ap_id),
364     local: post.local,
365     published: None,
366   };
367   let post_id = post.id;
368   blocking(context.pool(), move |conn| {
369     Post::update(conn, post_id, &post_form)
370   })
371   .await??;
372
373   // Refetch the view
374   let post_id = post.id;
375   let post_view = blocking(context.pool(), move |conn| {
376     PostView::read(conn, post_id, None)
377   })
378   .await??;
379
380   let res = PostResponse { post: post_view };
381
382   context.chat_server().do_send(SendPost {
383     op: UserOperation::EditPost,
384     post: res,
385     websocket_id: None,
386   });
387
388   announce_if_community_is_local(undo, &mod_, context).await?;
389   Ok(HttpResponse::Ok().finish())
390 }
391
392 async fn receive_undo_delete_community(
393   undo: Undo,
394   delete: &Delete,
395   context: &LemmyContext,
396 ) -> Result<HttpResponse, LemmyError> {
397   let user = get_user_from_activity(delete, context).await?;
398   let group = GroupExt::from_any_base(delete.object().to_owned().one().context(location_info!())?)?
399     .context(location_info!())?;
400
401   let community_actor_id = CommunityForm::from_apub(&group, context, Some(user.actor_id()?))
402     .await?
403     .actor_id
404     .context(location_info!())?;
405
406   let community = blocking(context.pool(), move |conn| {
407     Community::read_from_actor_id(conn, &community_actor_id)
408   })
409   .await??;
410
411   let community_form = CommunityForm {
412     name: community.name.to_owned(),
413     title: community.title.to_owned(),
414     description: community.description.to_owned(),
415     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
416     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
417     removed: None,
418     published: None,
419     updated: Some(naive_now()),
420     deleted: Some(false),
421     nsfw: community.nsfw,
422     actor_id: Some(community.actor_id),
423     local: community.local,
424     private_key: community.private_key,
425     public_key: community.public_key,
426     last_refreshed_at: None,
427     icon: Some(community.icon.to_owned()),
428     banner: Some(community.banner.to_owned()),
429   };
430
431   let community_id = community.id;
432   blocking(context.pool(), move |conn| {
433     Community::update(conn, community_id, &community_form)
434   })
435   .await??;
436
437   let community_id = community.id;
438   let res = CommunityResponse {
439     community: blocking(context.pool(), move |conn| {
440       CommunityView::read(conn, community_id, None)
441     })
442     .await??,
443   };
444
445   let community_id = res.community.id;
446
447   context.chat_server().do_send(SendCommunityRoomMessage {
448     op: UserOperation::EditCommunity,
449     response: res,
450     community_id,
451     websocket_id: None,
452   });
453
454   announce_if_community_is_local(undo, &user, context).await?;
455   Ok(HttpResponse::Ok().finish())
456 }
457
458 async fn receive_undo_remove_community(
459   undo: Undo,
460   remove: &Remove,
461   context: &LemmyContext,
462 ) -> Result<HttpResponse, LemmyError> {
463   let mod_ = get_user_from_activity(remove, context).await?;
464   let group = GroupExt::from_any_base(remove.object().to_owned().one().context(location_info!())?)?
465     .context(location_info!())?;
466
467   let community_actor_id = CommunityForm::from_apub(&group, context, Some(mod_.actor_id()?))
468     .await?
469     .actor_id
470     .context(location_info!())?;
471
472   let community = blocking(context.pool(), move |conn| {
473     Community::read_from_actor_id(conn, &community_actor_id)
474   })
475   .await??;
476
477   let community_form = CommunityForm {
478     name: community.name.to_owned(),
479     title: community.title.to_owned(),
480     description: community.description.to_owned(),
481     category_id: community.category_id, // Note: need to keep this due to foreign key constraint
482     creator_id: community.creator_id,   // Note: need to keep this due to foreign key constraint
483     removed: Some(false),
484     published: None,
485     updated: Some(naive_now()),
486     deleted: None,
487     nsfw: community.nsfw,
488     actor_id: Some(community.actor_id),
489     local: community.local,
490     private_key: community.private_key,
491     public_key: community.public_key,
492     last_refreshed_at: None,
493     icon: Some(community.icon.to_owned()),
494     banner: Some(community.banner.to_owned()),
495   };
496
497   let community_id = community.id;
498   blocking(context.pool(), move |conn| {
499     Community::update(conn, community_id, &community_form)
500   })
501   .await??;
502
503   let community_id = community.id;
504   let res = CommunityResponse {
505     community: blocking(context.pool(), move |conn| {
506       CommunityView::read(conn, community_id, None)
507     })
508     .await??,
509   };
510
511   let community_id = res.community.id;
512
513   context.chat_server().do_send(SendCommunityRoomMessage {
514     op: UserOperation::EditCommunity,
515     response: res,
516     community_id,
517     websocket_id: None,
518   });
519
520   announce_if_community_is_local(undo, &mod_, context).await?;
521   Ok(HttpResponse::Ok().finish())
522 }
523
524 async fn receive_undo_like_comment(
525   undo: Undo,
526   like: &Like,
527   context: &LemmyContext,
528 ) -> Result<HttpResponse, LemmyError> {
529   let user = get_user_from_activity(like, context).await?;
530   let note = Note::from_any_base(like.object().to_owned().one().context(location_info!())?)?
531     .context(location_info!())?;
532
533   let comment = CommentForm::from_apub(&note, context, None).await?;
534
535   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
536     .await?
537     .id;
538
539   let user_id = user.id;
540   blocking(context.pool(), move |conn| {
541     CommentLike::remove(conn, user_id, comment_id)
542   })
543   .await??;
544
545   // Refetch the view
546   let comment_view = blocking(context.pool(), move |conn| {
547     CommentView::read(conn, comment_id, None)
548   })
549   .await??;
550
551   // TODO get those recipient actor ids from somewhere
552   let recipient_ids = vec![];
553   let res = CommentResponse {
554     comment: comment_view,
555     recipient_ids,
556     form_id: None,
557   };
558
559   context.chat_server().do_send(SendComment {
560     op: UserOperation::CreateCommentLike,
561     comment: res,
562     websocket_id: None,
563   });
564
565   announce_if_community_is_local(undo, &user, context).await?;
566   Ok(HttpResponse::Ok().finish())
567 }
568
569 async fn receive_undo_like_post(
570   undo: Undo,
571   like: &Like,
572   context: &LemmyContext,
573 ) -> Result<HttpResponse, LemmyError> {
574   let user = get_user_from_activity(like, context).await?;
575   let page = PageExt::from_any_base(like.object().to_owned().one().context(location_info!())?)?
576     .context(location_info!())?;
577
578   let post = PostForm::from_apub(&page, context, None).await?;
579
580   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
581     .await?
582     .id;
583
584   let user_id = user.id;
585   blocking(context.pool(), move |conn| {
586     PostLike::remove(conn, user_id, post_id)
587   })
588   .await??;
589
590   // Refetch the view
591   let post_view = blocking(context.pool(), move |conn| {
592     PostView::read(conn, post_id, None)
593   })
594   .await??;
595
596   let res = PostResponse { post: post_view };
597
598   context.chat_server().do_send(SendPost {
599     op: UserOperation::CreatePostLike,
600     post: res,
601     websocket_id: None,
602   });
603
604   announce_if_community_is_local(undo, &user, context).await?;
605   Ok(HttpResponse::Ok().finish())
606 }
607
608 async fn receive_undo_dislike_comment(
609   undo: Undo,
610   dislike: &Dislike,
611   context: &LemmyContext,
612 ) -> Result<HttpResponse, LemmyError> {
613   let user = get_user_from_activity(dislike, context).await?;
614   let note = Note::from_any_base(
615     dislike
616       .object()
617       .to_owned()
618       .one()
619       .context(location_info!())?,
620   )?
621   .context(location_info!())?;
622
623   let comment = CommentForm::from_apub(&note, context, None).await?;
624
625   let comment_id = get_or_fetch_and_insert_comment(&comment.get_ap_id()?, context)
626     .await?
627     .id;
628
629   let user_id = user.id;
630   blocking(context.pool(), move |conn| {
631     CommentLike::remove(conn, user_id, comment_id)
632   })
633   .await??;
634
635   // Refetch the view
636   let comment_view = blocking(context.pool(), move |conn| {
637     CommentView::read(conn, comment_id, None)
638   })
639   .await??;
640
641   // TODO get those recipient actor ids from somewhere
642   let recipient_ids = vec![];
643   let res = CommentResponse {
644     comment: comment_view,
645     recipient_ids,
646     form_id: None,
647   };
648
649   context.chat_server().do_send(SendComment {
650     op: UserOperation::CreateCommentLike,
651     comment: res,
652     websocket_id: None,
653   });
654
655   announce_if_community_is_local(undo, &user, context).await?;
656   Ok(HttpResponse::Ok().finish())
657 }
658
659 async fn receive_undo_dislike_post(
660   undo: Undo,
661   dislike: &Dislike,
662   context: &LemmyContext,
663 ) -> Result<HttpResponse, LemmyError> {
664   let user = get_user_from_activity(dislike, context).await?;
665   let page = PageExt::from_any_base(
666     dislike
667       .object()
668       .to_owned()
669       .one()
670       .context(location_info!())?,
671   )?
672   .context(location_info!())?;
673
674   let post = PostForm::from_apub(&page, context, None).await?;
675
676   let post_id = get_or_fetch_and_insert_post(&post.get_ap_id()?, context)
677     .await?
678     .id;
679
680   let user_id = user.id;
681   blocking(context.pool(), move |conn| {
682     PostLike::remove(conn, user_id, post_id)
683   })
684   .await??;
685
686   // Refetch the view
687   let post_view = blocking(context.pool(), move |conn| {
688     PostView::read(conn, post_id, None)
689   })
690   .await??;
691
692   let res = PostResponse { post: post_view };
693
694   context.chat_server().do_send(SendPost {
695     op: UserOperation::CreatePostLike,
696     post: res,
697     websocket_id: None,
698   });
699
700   announce_if_community_is_local(undo, &user, context).await?;
701   Ok(HttpResponse::Ok().finish())
702 }