-use actix_web::{web, web::Data};
+use actix_web::web::Data;
+use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
-use lemmy_api_common::{
- comment::{
- CreateCommentLike,
- CreateCommentReport,
- ListCommentReports,
- ResolveCommentReport,
- SaveComment,
- },
- community::{
- AddModToCommunity,
- BanFromCommunity,
- BlockCommunity,
- FollowCommunity,
- TransferCommunity,
- },
- person::{
- AddAdmin,
- BanPerson,
- BlockPerson,
- ChangePassword,
- GetBannedPersons,
- GetCaptcha,
- GetPersonMentions,
- GetReplies,
- GetReportCount,
- GetUnreadCount,
- Login,
- MarkAllAsRead,
- MarkCommentReplyAsRead,
- MarkPersonMentionAsRead,
- PasswordChangeAfterReset,
- PasswordReset,
- SaveUserSettings,
- VerifyEmail,
- },
- post::{
- CreatePostLike,
- CreatePostReport,
- GetSiteMetadata,
- ListPostReports,
- LockPost,
- MarkPostAsRead,
- ResolvePostReport,
- SavePost,
- StickyPost,
- },
- private_message::{
- CreatePrivateMessageReport,
- ListPrivateMessageReports,
- MarkPrivateMessageAsRead,
- ResolvePrivateMessageReport,
- },
- site::{
- ApproveRegistrationApplication,
- GetModlog,
- GetUnreadRegistrationApplicationCount,
- LeaveAdmin,
- ListRegistrationApplications,
- PurgeComment,
- PurgeCommunity,
- PurgePerson,
- PurgePost,
- ResolveObject,
- Search,
- },
- utils::local_site_to_slur_regex,
- websocket::{CommunityJoin, ModJoin, PostJoin, UserJoin},
-};
+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::check_slurs, ConnectionId};
-use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
-use serde::Deserialize;
+use lemmy_utils::{
+ error::{LemmyError, LemmyErrorExt, LemmyErrorType},
+ utils::slurs::check_slurs,
+};
+use std::io::Cursor;
mod comment;
mod comment_report;
mod private_message;
mod private_message_report;
mod site;
-mod websocket;
#[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>,
- websocket_id: Option<ConnectionId>,
- ) -> Result<Self::Response, LemmyError>;
-}
-
-pub async fn match_websocket_operation(
- context: LemmyContext,
- id: ConnectionId,
- op: UserOperation,
- data: &str,
-) -> Result<String, LemmyError> {
- match op {
- // User ops
- UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
- UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
- UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
- UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
- UserOperation::GetUnreadRegistrationApplicationCount => {
- do_websocket_operation::<GetUnreadRegistrationApplicationCount>(context, id, op, data).await
- }
- UserOperation::ListRegistrationApplications => {
- do_websocket_operation::<ListRegistrationApplications>(context, id, op, data).await
- }
- UserOperation::ApproveRegistrationApplication => {
- do_websocket_operation::<ApproveRegistrationApplication>(context, id, op, data).await
- }
- UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
- UserOperation::GetBannedPersons => {
- do_websocket_operation::<GetBannedPersons>(context, id, op, data).await
- }
- UserOperation::BlockPerson => {
- do_websocket_operation::<BlockPerson>(context, id, op, data).await
- }
- UserOperation::GetPersonMentions => {
- do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
- }
- UserOperation::MarkPersonMentionAsRead => {
- do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
- }
- UserOperation::MarkCommentReplyAsRead => {
- do_websocket_operation::<MarkCommentReplyAsRead>(context, id, op, data).await
- }
- UserOperation::MarkAllAsRead => {
- do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
- }
- UserOperation::PasswordReset => {
- do_websocket_operation::<PasswordReset>(context, id, op, data).await
- }
- UserOperation::PasswordChange => {
- do_websocket_operation::<PasswordChangeAfterReset>(context, id, op, data).await
- }
- UserOperation::UserJoin => do_websocket_operation::<UserJoin>(context, id, op, data).await,
- UserOperation::PostJoin => do_websocket_operation::<PostJoin>(context, id, op, data).await,
- UserOperation::CommunityJoin => {
- do_websocket_operation::<CommunityJoin>(context, id, op, data).await
- }
- UserOperation::ModJoin => do_websocket_operation::<ModJoin>(context, id, op, data).await,
- UserOperation::SaveUserSettings => {
- do_websocket_operation::<SaveUserSettings>(context, id, op, data).await
- }
- UserOperation::ChangePassword => {
- do_websocket_operation::<ChangePassword>(context, id, op, data).await
- }
- UserOperation::GetReportCount => {
- do_websocket_operation::<GetReportCount>(context, id, op, data).await
- }
- UserOperation::GetUnreadCount => {
- do_websocket_operation::<GetUnreadCount>(context, id, op, data).await
- }
- UserOperation::VerifyEmail => {
- do_websocket_operation::<VerifyEmail>(context, id, op, data).await
- }
-
- // Private Message ops
- UserOperation::MarkPrivateMessageAsRead => {
- do_websocket_operation::<MarkPrivateMessageAsRead>(context, id, op, data).await
- }
- UserOperation::CreatePrivateMessageReport => {
- do_websocket_operation::<CreatePrivateMessageReport>(context, id, op, data).await
- }
- UserOperation::ResolvePrivateMessageReport => {
- do_websocket_operation::<ResolvePrivateMessageReport>(context, id, op, data).await
- }
- UserOperation::ListPrivateMessageReports => {
- do_websocket_operation::<ListPrivateMessageReports>(context, id, op, data).await
- }
-
- // Site ops
- UserOperation::GetModlog => do_websocket_operation::<GetModlog>(context, id, op, data).await,
- UserOperation::PurgePerson => {
- do_websocket_operation::<PurgePerson>(context, id, op, data).await
- }
- UserOperation::PurgeCommunity => {
- do_websocket_operation::<PurgeCommunity>(context, id, op, data).await
- }
- UserOperation::PurgePost => do_websocket_operation::<PurgePost>(context, id, op, data).await,
- UserOperation::PurgeComment => {
- do_websocket_operation::<PurgeComment>(context, id, op, data).await
- }
- UserOperation::Search => do_websocket_operation::<Search>(context, id, op, data).await,
- UserOperation::ResolveObject => {
- do_websocket_operation::<ResolveObject>(context, id, op, data).await
- }
- UserOperation::TransferCommunity => {
- do_websocket_operation::<TransferCommunity>(context, id, op, data).await
- }
- UserOperation::LeaveAdmin => do_websocket_operation::<LeaveAdmin>(context, id, op, data).await,
-
- // Community ops
- UserOperation::FollowCommunity => {
- do_websocket_operation::<FollowCommunity>(context, id, op, data).await
- }
- UserOperation::BlockCommunity => {
- do_websocket_operation::<BlockCommunity>(context, id, op, data).await
- }
- UserOperation::BanFromCommunity => {
- do_websocket_operation::<BanFromCommunity>(context, id, op, data).await
- }
- UserOperation::AddModToCommunity => {
- do_websocket_operation::<AddModToCommunity>(context, id, op, data).await
- }
-
- // Post ops
- UserOperation::LockPost => do_websocket_operation::<LockPost>(context, id, op, data).await,
- UserOperation::StickyPost => do_websocket_operation::<StickyPost>(context, id, op, data).await,
- UserOperation::CreatePostLike => {
- do_websocket_operation::<CreatePostLike>(context, id, op, data).await
- }
- UserOperation::MarkPostAsRead => {
- do_websocket_operation::<MarkPostAsRead>(context, id, op, data).await
- }
- UserOperation::SavePost => do_websocket_operation::<SavePost>(context, id, op, data).await,
- UserOperation::CreatePostReport => {
- do_websocket_operation::<CreatePostReport>(context, id, op, data).await
- }
- UserOperation::ListPostReports => {
- do_websocket_operation::<ListPostReports>(context, id, op, data).await
- }
- UserOperation::ResolvePostReport => {
- do_websocket_operation::<ResolvePostReport>(context, id, op, data).await
- }
- UserOperation::GetSiteMetadata => {
- do_websocket_operation::<GetSiteMetadata>(context, id, op, data).await
- }
-
- // Comment ops
- UserOperation::SaveComment => {
- do_websocket_operation::<SaveComment>(context, id, op, data).await
- }
- UserOperation::CreateCommentLike => {
- do_websocket_operation::<CreateCommentLike>(context, id, op, data).await
- }
- UserOperation::CreateCommentReport => {
- do_websocket_operation::<CreateCommentReport>(context, id, op, data).await
- }
- UserOperation::ListCommentReports => {
- do_websocket_operation::<ListCommentReports>(context, id, op, data).await
- }
- UserOperation::ResolveCommentReport => {
- do_websocket_operation::<ResolveCommentReport>(context, id, op, data).await
- }
- }
-}
-
-async fn do_websocket_operation<'a, 'b, Data>(
- context: LemmyContext,
- id: ConnectionId,
- op: UserOperation,
- data: &str,
-) -> Result<String, LemmyError>
-where
- for<'de> Data: Deserialize<'de> + 'a,
- Data: Perform,
-{
- let parsed_data: Data = serde_json::from_str(data)?;
- let res = parsed_data
- .perform(&web::Data::new(context), Some(id))
- .await?;
- serialize_websocket_message(&op, &res)
+ 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::{
#[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();
- let inserted_instance = Instance::create(pool, "my_domain.tld").await.unwrap();
+ let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
+ .await
+ .unwrap();
let new_person = PersonInsertForm::builder()
.name("Gerry9812".into())