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