From 3ae62573b700d21c99e3347b393bfdeee0ef5d56 Mon Sep 17 00:00:00 2001
From: Felix Ableitner <me@nutomic.com>
Date: Wed, 24 Feb 2021 20:37:27 +0100
Subject: [PATCH] Better type safety for activity parsing

---
 .../apub/src/inbox/receive_for_community.rs   | 50 +++++++++++++++----
 crates/apub/src/inbox/user_inbox.rs           | 32 +++++++++---
 2 files changed, 63 insertions(+), 19 deletions(-)

diff --git a/crates/apub/src/inbox/receive_for_community.rs b/crates/apub/src/inbox/receive_for_community.rs
index e0248cb6..e3704d97 100644
--- a/crates/apub/src/inbox/receive_for_community.rs
+++ b/crates/apub/src/inbox/receive_for_community.rs
@@ -48,8 +48,15 @@ use lemmy_db_schema::source::site::Site;
 use lemmy_structs::blocking;
 use lemmy_utils::{location_info, LemmyError};
 use lemmy_websocket::LemmyContext;
+use strum_macros::EnumString;
 use url::Url;
 
+#[derive(EnumString)]
+enum PageOrNote {
+  Page,
+  Note,
+}
+
 /// This file is for post/comment activities received by the community, and for post/comment
 ///       activities announced by the community and received by the user.
 
@@ -64,9 +71,13 @@ pub(in crate::inbox) async fn receive_create_for_community(
   verify_activity_domains_valid(&create, &expected_domain, true)?;
   is_addressed_to_public(&create)?;
 
-  match create.object().as_single_kind_str() {
-    Some("Page") => receive_create_post(create, context, request_counter).await,
-    Some("Note") => receive_create_comment(create, context, request_counter).await,
+  let kind = create
+    .object()
+    .as_single_kind_str()
+    .and_then(|s| s.parse().ok());
+  match kind {
+    Some(PageOrNote::Page) => receive_create_post(create, context, request_counter).await,
+    Some(PageOrNote::Note) => receive_create_comment(create, context, request_counter).await,
     _ => receive_unhandled_activity(create),
   }
 }
@@ -82,9 +93,13 @@ pub(in crate::inbox) async fn receive_update_for_community(
   verify_activity_domains_valid(&update, &expected_domain, true)?;
   is_addressed_to_public(&update)?;
 
-  match update.object().as_single_kind_str() {
-    Some("Page") => receive_update_post(update, context, request_counter).await,
-    Some("Note") => receive_update_comment(update, context, request_counter).await,
+  let kind = update
+    .object()
+    .as_single_kind_str()
+    .and_then(|s| s.parse().ok());
+  match kind {
+    Some(PageOrNote::Page) => receive_update_post(update, context, request_counter).await,
+    Some(PageOrNote::Note) => receive_update_comment(update, context, request_counter).await,
     _ => receive_unhandled_activity(update),
   }
 }
@@ -201,6 +216,14 @@ pub(in crate::inbox) async fn receive_remove_for_community(
   }
 }
 
+#[derive(EnumString)]
+enum UndoableActivities {
+  Delete,
+  Remove,
+  Like,
+  Dislike,
+}
+
 /// A post/comment action being reverted (either a delete, remove, upvote or downvote)
 pub(in crate::inbox) async fn receive_undo_for_community(
   context: &LemmyContext,
@@ -212,13 +235,18 @@ pub(in crate::inbox) async fn receive_undo_for_community(
   verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
   is_addressed_to_public(&undo)?;
 
-  match undo.object().as_single_kind_str() {
-    Some("Delete") => receive_undo_delete_for_community(context, undo, expected_domain).await,
-    Some("Remove") => receive_undo_remove_for_community(context, undo, expected_domain).await,
-    Some("Like") => {
+  use UndoableActivities::*;
+  match undo
+    .object()
+    .as_single_kind_str()
+    .and_then(|s| s.parse().ok())
+  {
+    Some(Delete) => receive_undo_delete_for_community(context, undo, expected_domain).await,
+    Some(Remove) => receive_undo_remove_for_community(context, undo, expected_domain).await,
+    Some(Like) => {
       receive_undo_like_for_community(context, undo, expected_domain, request_counter).await
     }
-    Some("Dislike") => {
+    Some(Dislike) => {
       receive_undo_dislike_for_community(context, undo, expected_domain, request_counter).await
     }
     _ => receive_unhandled_activity(undo),
diff --git a/crates/apub/src/inbox/user_inbox.rs b/crates/apub/src/inbox/user_inbox.rs
index 7b90fafa..5657faf1 100644
--- a/crates/apub/src/inbox/user_inbox.rs
+++ b/crates/apub/src/inbox/user_inbox.rs
@@ -60,6 +60,7 @@ use lemmy_websocket::LemmyContext;
 use log::debug;
 use serde::{Deserialize, Serialize};
 use std::fmt::Debug;
+use strum_macros::EnumString;
 use url::Url;
 
 /// Allowed activities for user inbox.
@@ -235,6 +236,17 @@ async fn receive_accept(
   Ok(())
 }
 
+#[derive(EnumString)]
+enum AnnouncableActivities {
+  Create,
+  Update,
+  Like,
+  Dislike,
+  Delete,
+  Remove,
+  Undo,
+}
+
 /// Takes an announce and passes the inner activity to the appropriate handler.
 pub async fn receive_announce(
   context: &LemmyContext,
@@ -246,7 +258,10 @@ pub async fn receive_announce(
   verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
   is_addressed_to_public(&announce)?;
 
-  let kind = announce.object().as_single_kind_str();
+  let kind = announce
+    .object()
+    .as_single_kind_str()
+    .and_then(|s| s.parse().ok());
   let inner_activity = announce
     .object()
     .to_owned()
@@ -259,22 +274,23 @@ pub async fn receive_announce(
     return Ok(());
   }
 
+  use AnnouncableActivities::*;
   match kind {
-    Some("Create") => {
+    Some(Create) => {
       receive_create_for_community(context, inner_activity, &inner_id, request_counter).await
     }
-    Some("Update") => {
+    Some(Update) => {
       receive_update_for_community(context, inner_activity, &inner_id, request_counter).await
     }
-    Some("Like") => {
+    Some(Like) => {
       receive_like_for_community(context, inner_activity, &inner_id, request_counter).await
     }
-    Some("Dislike") => {
+    Some(Dislike) => {
       receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
     }
-    Some("Delete") => receive_delete_for_community(context, inner_activity, &inner_id).await,
-    Some("Remove") => receive_remove_for_community(context, inner_activity, &inner_id).await,
-    Some("Undo") => {
+    Some(Delete) => receive_delete_for_community(context, inner_activity, &inner_id).await,
+    Some(Remove) => receive_remove_for_community(context, inner_activity, &inner_id).await,
+    Some(Undo) => {
       receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
     }
     _ => receive_unhandled_activity(inner_activity),
-- 
2.44.1