]> Untitled Git - lemmy.git/blobdiff - crates/api_crud/src/post/update.rs
Sanitize html (#3708)
[lemmy.git] / crates / api_crud / src / post / update.rs
index ac4910cd81adaad50ab4f7ca75ff326c74e0ccc2..f3be5f6af903a830a756d0e85a3c09e6d34cfb6d 100644 (file)
+use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
+  build_response::build_post_response,
+  context::LemmyContext,
   post::{EditPost, PostResponse},
   request::fetch_site_data,
   utils::{
-    blocking,
     check_community_ban,
-    check_community_deleted_or_removed,
-    get_local_user_view_from_jwt,
+    local_site_to_slur_regex,
+    local_user_view_from_jwt,
+    sanitize_html_opt,
   },
 };
-use lemmy_apub::protocol::activities::{
-  create_or_update::post::CreateOrUpdatePost,
-  CreateOrUpdateType,
-};
 use lemmy_db_schema::{
-  source::post::{Post, PostForm},
+  source::{
+    actor_language::CommunityLanguage,
+    local_site::LocalSite,
+    post::{Post, PostUpdateForm},
+  },
   traits::Crud,
-  utils::naive_now,
+  utils::{diesel_option_overwrite, naive_now},
 };
 use lemmy_utils::{
-  error::LemmyError,
-  utils::{check_slurs_opt, clean_optional_text, clean_url_params, is_valid_post_title},
-  ConnectionId,
+  error::{LemmyError, LemmyErrorExt, LemmyErrorType},
+  utils::{
+    slurs::check_slurs_opt,
+    validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
+  },
 };
-use lemmy_websocket::{send::send_post_ws_message, LemmyContext, UserOperationCrud};
-
-use crate::PerformCrud;
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for EditPost {
   type Response = PostResponse;
 
-  #[tracing::instrument(skip(context, websocket_id))]
-  async fn perform(
-    &self,
-    context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
-  ) -> Result<PostResponse, LemmyError> {
+  #[tracing::instrument(skip(context))]
+  async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
     let data: &EditPost = self;
-    let local_user_view =
-      get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
+    let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
+    let local_site = LocalSite::read(&mut context.pool()).await?;
+
+    let data_url = data.url.as_ref();
+
+    // TODO No good way to handle a clear.
+    // Issue link: https://github.com/LemmyNet/lemmy/issues/2287
+    let url = Some(data_url.map(clean_url_params).map(Into::into));
 
-    let slur_regex = &context.settings().slur_regex();
-    check_slurs_opt(&data.name, slur_regex)?;
-    check_slurs_opt(&data.body, slur_regex)?;
+    let slur_regex = local_site_to_slur_regex(&local_site);
+    check_slurs_opt(&data.name, &slur_regex)?;
+    check_slurs_opt(&data.body, &slur_regex)?;
 
     if let Some(name) = &data.name {
-      if !is_valid_post_title(name) {
-        return Err(LemmyError::from_message("invalid_post_title"));
-      }
+      is_valid_post_title(name)?;
     }
 
+    is_valid_body_field(&data.body, true)?;
+    check_url_scheme(&data.url)?;
+
     let post_id = data.post_id;
-    let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
+    let orig_post = Post::read(&mut context.pool(), post_id).await?;
 
     check_community_ban(
       local_user_view.person.id,
       orig_post.community_id,
-      context.pool(),
+      &mut context.pool(),
     )
     .await?;
-    check_community_deleted_or_removed(orig_post.community_id, context.pool()).await?;
 
     // Verify that only the creator can edit
     if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
-      return Err(LemmyError::from_message("no_post_edit_allowed"));
+      return Err(LemmyErrorType::NoPostEditAllowed)?;
     }
 
     // Fetch post links and Pictrs cached image
     let data_url = data.url.as_ref();
     let (metadata_res, thumbnail_url) =
-      fetch_site_data(context.client(), context.settings(), data_url).await;
+      fetch_site_data(context.client(), context.settings(), data_url, true).await;
     let (embed_title, embed_description, embed_video_url) = metadata_res
-      .map(|u| (u.title, u.description, u.embed_video_url))
+      .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
       .unwrap_or_default();
 
-    let post_form = PostForm {
-      creator_id: orig_post.creator_id.to_owned(),
-      community_id: orig_post.community_id,
-      name: data.name.to_owned().unwrap_or(orig_post.name),
-      url: data_url.map(|u| clean_url_params(u.to_owned()).into()),
-      body: clean_optional_text(&data.body),
-      nsfw: data.nsfw,
-      updated: Some(naive_now()),
-      embed_title,
-      embed_description,
-      embed_video_url,
-      thumbnail_url,
-      ..PostForm::default()
-    };
+    let name = sanitize_html_opt(&data.name);
+    let body = sanitize_html_opt(&data.body);
+    let body = diesel_option_overwrite(body);
+    let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
+    let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
 
-    let post_id = data.post_id;
-    let res = blocking(context.pool(), move |conn| {
-      Post::update(conn, post_id, &post_form)
-    })
-    .await?;
-    let updated_post: Post = match res {
-      Ok(post) => post,
-      Err(e) => {
-        let err_type = if e.to_string() == "value too long for type character varying(200)" {
-          "post_title_too_long"
-        } else {
-          "couldnt_update_post"
-        };
-
-        return Err(LemmyError::from_error_message(e, err_type));
-      }
-    };
-
-    // Send apub update
-    CreateOrUpdatePost::send(
-      updated_post.into(),
-      &local_user_view.person.clone().into(),
-      CreateOrUpdateType::Update,
-      context,
+    let language_id = self.language_id;
+    CommunityLanguage::is_allowed_community_language(
+      &mut context.pool(),
+      language_id,
+      orig_post.community_id,
     )
     .await?;
 
-    send_post_ws_message(
-      data.post_id,
-      UserOperationCrud::EditPost,
-      websocket_id,
-      Some(local_user_view.person.id),
+    let post_form = PostUpdateForm::builder()
+      .name(name)
+      .url(url)
+      .body(body)
+      .nsfw(data.nsfw)
+      .embed_title(embed_title)
+      .embed_description(embed_description)
+      .embed_video_url(embed_video_url)
+      .language_id(data.language_id)
+      .thumbnail_url(Some(thumbnail_url))
+      .updated(Some(Some(naive_now())))
+      .build();
+
+    let post_id = data.post_id;
+    Post::update(&mut context.pool(), post_id, &post_form)
+      .await
+      .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
+
+    build_post_response(
       context,
+      orig_post.community_id,
+      local_user_view.person.id,
+      post_id,
     )
     .await
   }