From 1ba570092e0043b7516cccbbb5c27451eb1f0e6a Mon Sep 17 00:00:00 2001
From: Dessalines <tyhou13@gmx.com>
Date: Thu, 1 Apr 2021 13:30:24 -0400
Subject: [PATCH] Adding more rust captcha features. Fixes #1248

---
 crates/api/src/lib.rs              | 60 +++++-------------------------
 crates/api/src/local_user.rs       |  8 ++--
 crates/api_common/src/person.rs    |  4 +-
 docker/dev/Dockerfile              |  3 --
 docker/dev/volume_mount.dockerfile |  4 +-
 docker/prod/Dockerfile             |  3 --
 docker/prod/Dockerfile.arm         |  4 +-
 7 files changed, 18 insertions(+), 68 deletions(-)

diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs
index 5dc678be..c9de749e 100644
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@ -1,9 +1,9 @@
 use actix_web::{web, web::Data};
+use captcha::Captcha;
 use lemmy_api_common::{comment::*, community::*, person::*, post::*, site::*, websocket::*};
 use lemmy_utils::{ConnectionId, LemmyError};
 use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
 use serde::Deserialize;
-use std::{env, process::Command};
 
 mod comment;
 mod comment_report;
@@ -158,60 +158,23 @@ where
   serialize_websocket_message(&op, &res)
 }
 
-pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyError> {
-  let mut built_text = String::new();
+/// Converts the captcha to a base64 encoded wav audio file
+pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
+  let letters = captcha.as_wav();
 
-  // Building proper speech text for espeak
-  for mut c in captcha.chars() {
-    let new_str = if c.is_alphabetic() {
-      if c.is_lowercase() {
-        c.make_ascii_uppercase();
-        format!("lower case {} ... ", c)
-      } else {
-        c.make_ascii_uppercase();
-        format!("capital {} ... ", c)
-      }
-    } else {
-      format!("{} ...", c)
-    };
+  let mut concat_letters: Vec<u8> = Vec::new();
 
-    built_text.push_str(&new_str);
+  for letter in letters {
+    let bytes = letter.unwrap_or_default();
+    concat_letters.extend(bytes);
   }
 
-  espeak_wav_base64(&built_text)
-}
-
-pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
-  // Make a temp file path
-  let uuid = uuid::Uuid::new_v4().to_string();
-  let file_path = format!(
-    "{}/lemmy_espeak_{}.wav",
-    env::temp_dir().to_string_lossy(),
-    &uuid
-  );
-
-  // Write the wav file
-  Command::new("espeak")
-    .arg("-w")
-    .arg(&file_path)
-    .arg(text)
-    .status()?;
-
-  // Read the wav file bytes
-  let bytes = std::fs::read(&file_path)?;
-
-  // Delete the file
-  std::fs::remove_file(file_path)?;
-
   // Convert to base64
-  let base64 = base64::encode(bytes);
-
-  Ok(base64)
+  base64::encode(concat_letters)
 }
 
 #[cfg(test)]
 mod tests {
-  use crate::captcha_espeak_wav_base64;
   use lemmy_api_common::check_validator_time;
   use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
   use lemmy_db_schema::source::{
@@ -253,9 +216,4 @@ mod tests {
     let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
     assert_eq!(1, num_deleted);
   }
-
-  #[test]
-  fn test_espeak() {
-    assert!(captcha_espeak_wav_base64("WxRt2l").is_ok())
-  }
 }
diff --git a/crates/api/src/local_user.rs b/crates/api/src/local_user.rs
index 656ddf97..831e38a1 100644
--- a/crates/api/src/local_user.rs
+++ b/crates/api/src/local_user.rs
@@ -1,4 +1,4 @@
-use crate::{captcha_espeak_wav_base64, Perform};
+use crate::{captcha_as_wav_base64, Perform};
 use actix_web::web::Data;
 use anyhow::Context;
 use bcrypt::verify;
@@ -135,13 +135,11 @@ impl Perform for GetCaptcha {
 
     let answer = captcha.chars_as_string();
 
-    let png_byte_array = captcha.as_png().expect("failed to generate captcha");
-
-    let png = base64::encode(png_byte_array);
+    let png = captcha.as_base64().expect("failed to generate captcha");
 
     let uuid = uuid::Uuid::new_v4().to_string();
 
-    let wav = captcha_espeak_wav_base64(&answer).ok();
+    let wav = captcha_as_wav_base64(&captcha);
 
     let captcha_item = CaptchaItem {
       answer,
diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs
index 7767da46..7b732412 100644
--- a/crates/api_common/src/person.rs
+++ b/crates/api_common/src/person.rs
@@ -39,8 +39,8 @@ pub struct GetCaptchaResponse {
 
 #[derive(Serialize)]
 pub struct CaptchaResponse {
-  pub png: String,         // A Base64 encoded png
-  pub wav: Option<String>, // A Base64 encoded wav audio
+  pub png: String, // A Base64 encoded png
+  pub wav: String, // A Base64 encoded wav audio
   pub uuid: String,
 }
 
diff --git a/docker/dev/Dockerfile b/docker/dev/Dockerfile
index 954c85c8..5d2acec7 100644
--- a/docker/dev/Dockerfile
+++ b/docker/dev/Dockerfile
@@ -49,9 +49,6 @@ FROM alpine:3.12 as lemmy
 # Install libpq for postgres
 RUN apk add libpq
 
-# Install Espeak for captchas
-RUN apk add espeak
-
 RUN addgroup -g 1000 lemmy
 RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
 
diff --git a/docker/dev/volume_mount.dockerfile b/docker/dev/volume_mount.dockerfile
index 0cb03624..d848e0ea 100644
--- a/docker/dev/volume_mount.dockerfile
+++ b/docker/dev/volume_mount.dockerfile
@@ -19,9 +19,9 @@ RUN --mount=type=cache,target=/app/target \
 
 FROM ubuntu:20.10
 
-# Install libpq for postgres and espeak
+# Install libpq for postgres
 RUN apt-get update -y
-RUN apt-get install -y libpq-dev espeak 
+RUN apt-get install -y libpq-dev
 
 # Copy resources
 COPY config/defaults.hjson /config/defaults.hjson
diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile
index 2ad601ae..88a05fb2 100644
--- a/docker/prod/Dockerfile
+++ b/docker/prod/Dockerfile
@@ -49,9 +49,6 @@ FROM alpine:3.12 as lemmy
 # Install libpq for postgres
 RUN apk add libpq
 
-# Install Espeak for captchas
-RUN apk add espeak
-
 RUN addgroup -g 1000 lemmy
 RUN adduser -D -s /bin/sh -u 1000 -G lemmy lemmy
 
diff --git a/docker/prod/Dockerfile.arm b/docker/prod/Dockerfile.arm
index 61d1f86c..b3eacb4f 100644
--- a/docker/prod/Dockerfile.arm
+++ b/docker/prod/Dockerfile.arm
@@ -22,9 +22,9 @@ RUN cp ./target/release/lemmy_server /app/lemmy_server
 # The Debian runner
 FROM debian:buster-slim as lemmy
 
-# Install libpq for postgres and espeak for captchas
+# Install libpq for postgres
 RUN apt-get update \
- && apt-get -y install --no-install-recommends espeak postgresql-client libc6 libssl1.1 \
+ && apt-get -y install --no-install-recommends postgresql-client libc6 libssl1.1 \
  && rm -rf /var/lib/apt/lists/*
 
 RUN addgroup --gid 1000 lemmy
-- 
2.44.1