]> Untitled Git - lemmy.git/blobdiff - crates/api/src/lib.rs
Sanitize html (#3708)
[lemmy.git] / crates / api / src / lib.rs
index 615a8a3144fdea49e8d7bca4388a79c8c05acd23..b297f503f6f9d4abc2115255a55e8ca567bd5fff 100644 (file)
@@ -1,8 +1,13 @@
 use actix_web::web::Data;
+use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
 use captcha::Captcha;
 use lemmy_api_common::{context::LemmyContext, utils::local_site_to_slur_regex};
 use lemmy_db_schema::source::local_site::LocalSite;
-use lemmy_utils::{error::LemmyError, utils::slurs::check_slurs};
+use lemmy_utils::{
+  error::{LemmyError, LemmyErrorExt, LemmyErrorType},
+  utils::slurs::check_slurs,
+};
+use std::io::Cursor;
 
 mod comment;
 mod comment_report;
@@ -16,42 +21,64 @@ mod site;
 
 #[async_trait::async_trait(?Send)]
 pub trait Perform {
-  type Response: serde::ser::Serialize + Send;
+  type Response: serde::ser::Serialize + Send + Clone + Sync;
 
   async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError>;
 }
 
 /// Converts the captcha to a base64 encoded wav audio file
-pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
+pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyError> {
   let letters = captcha.as_wav();
 
-  let mut concat_letters: Vec<u8> = Vec::new();
-
+  // Decode each wav file, concatenate the samples
+  let mut concat_samples: Vec<i16> = Vec::new();
+  let mut any_header: Option<wav::Header> = None;
   for letter in letters {
-    let bytes = letter.unwrap_or_default();
-    concat_letters.extend(bytes);
+    let mut cursor = Cursor::new(letter.unwrap_or_default());
+    let (header, samples) = wav::read(&mut cursor)?;
+    any_header = Some(header);
+    if let Some(samples16) = samples.as_sixteen() {
+      concat_samples.extend(samples16);
+    } else {
+      return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?;
+    }
   }
 
-  // Convert to base64
-  base64::encode(concat_letters)
+  // Encode the concatenated result as a wav file
+  let mut output_buffer = Cursor::new(vec![]);
+  let header = match any_header {
+    Some(header) => header,
+    None => return Err(LemmyErrorType::CouldntCreateAudioCaptcha)?,
+  };
+  wav::write(
+    header,
+    &wav::BitDepth::Sixteen(concat_samples),
+    &mut output_buffer,
+  )
+  .with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
+
+  Ok(base64.encode(output_buffer.into_inner()))
 }
 
-/// Check size of report and remove whitespace
+/// Check size of report
 pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
   let slur_regex = &local_site_to_slur_regex(local_site);
 
   check_slurs(reason, slur_regex)?;
   if reason.is_empty() {
-    return Err(LemmyError::from_message("report_reason_required"));
+    return Err(LemmyErrorType::ReportReasonRequired)?;
   }
   if reason.chars().count() > 1000 {
-    return Err(LemmyError::from_message("report_too_long"));
+    return Err(LemmyErrorType::ReportTooLong)?;
   }
   Ok(())
 }
 
 #[cfg(test)]
 mod tests {
+  #![allow(clippy::unwrap_used)]
+  #![allow(clippy::indexing_slicing)]
+
   use lemmy_api_common::utils::check_validator_time;
   use lemmy_db_schema::{
     source::{
@@ -70,6 +97,7 @@ mod tests {
   #[serial]
   async fn test_should_not_validate_user_token_after_password_change() {
     let pool = &build_db_pool_for_tests().await;
+    let pool = &mut pool.into();
     let secret = Secret::init(pool).await.unwrap();
     let settings = &SETTINGS.to_owned();