2021-10-21 01:13:39 +00:00
|
|
|
use crate::{
|
|
|
|
config::Format, either::Either, error::Error, ffmpeg::InputFormat, magick::ValidInputType,
|
|
|
|
};
|
2021-09-04 00:53:53 +00:00
|
|
|
use actix_web::web::Bytes;
|
|
|
|
use tokio::io::AsyncRead;
|
2021-09-14 01:22:42 +00:00
|
|
|
use tracing::instrument;
|
2021-08-28 22:15:14 +00:00
|
|
|
|
|
|
|
pub(crate) fn image_webp() -> mime::Mime {
|
|
|
|
"image/webp".parse().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn video_mp4() -> mime::Mime {
|
|
|
|
"video/mp4".parse().unwrap()
|
|
|
|
}
|
|
|
|
|
2021-09-26 16:02:19 +00:00
|
|
|
struct UnvalidatedBytes {
|
|
|
|
bytes: Bytes,
|
|
|
|
written: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UnvalidatedBytes {
|
|
|
|
fn new(bytes: Bytes) -> Self {
|
|
|
|
UnvalidatedBytes { bytes, written: 0 }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsyncRead for UnvalidatedBytes {
|
|
|
|
fn poll_read(
|
|
|
|
mut self: std::pin::Pin<&mut Self>,
|
|
|
|
_cx: &mut std::task::Context<'_>,
|
|
|
|
buf: &mut tokio::io::ReadBuf<'_>,
|
|
|
|
) -> std::task::Poll<std::io::Result<()>> {
|
|
|
|
let bytes_to_write = (self.bytes.len() - self.written).min(buf.remaining());
|
|
|
|
if bytes_to_write > 0 {
|
|
|
|
let end = self.written + bytes_to_write;
|
|
|
|
buf.put_slice(&self.bytes[self.written..end]);
|
|
|
|
self.written = end;
|
|
|
|
}
|
|
|
|
std::task::Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
#[instrument(name = "Validate image", skip(bytes))]
|
2021-09-04 00:53:53 +00:00
|
|
|
pub(crate) async fn validate_image_bytes(
|
|
|
|
bytes: Bytes,
|
|
|
|
prescribed_format: Option<Format>,
|
2021-09-26 16:02:19 +00:00
|
|
|
validate: bool,
|
2021-10-21 01:13:39 +00:00
|
|
|
) -> Result<(mime::Mime, impl AsyncRead + Unpin), Error> {
|
2021-09-04 00:53:53 +00:00
|
|
|
let input_type = crate::magick::input_type_bytes(bytes.clone()).await?;
|
|
|
|
|
2021-09-26 16:02:19 +00:00
|
|
|
if !validate {
|
|
|
|
let mime_type = match input_type {
|
|
|
|
ValidInputType::Gif => video_mp4(),
|
|
|
|
ValidInputType::Mp4 => mime::IMAGE_GIF,
|
|
|
|
ValidInputType::Jpeg => mime::IMAGE_JPEG,
|
|
|
|
ValidInputType::Png => mime::IMAGE_PNG,
|
|
|
|
ValidInputType::Webp => image_webp(),
|
|
|
|
};
|
|
|
|
|
2021-10-21 01:13:39 +00:00
|
|
|
return Ok((mime_type, Either::left(UnvalidatedBytes::new(bytes))));
|
2021-09-26 16:02:19 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 00:53:53 +00:00
|
|
|
match (prescribed_format, input_type) {
|
|
|
|
(_, ValidInputType::Gif) => Ok((
|
|
|
|
video_mp4(),
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::left(crate::ffmpeg::to_mp4_bytes(
|
|
|
|
bytes,
|
|
|
|
InputFormat::Gif,
|
|
|
|
)?)),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
(_, ValidInputType::Mp4) => Ok((
|
|
|
|
video_mp4(),
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::left(crate::ffmpeg::to_mp4_bytes(
|
|
|
|
bytes,
|
|
|
|
InputFormat::Mp4,
|
|
|
|
)?)),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
(Some(Format::Jpeg) | None, ValidInputType::Jpeg) => Ok((
|
|
|
|
mime::IMAGE_JPEG,
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::right(Either::left(
|
|
|
|
crate::exiftool::clear_metadata_bytes_read(bytes)?,
|
|
|
|
))),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
(Some(Format::Png) | None, ValidInputType::Png) => Ok((
|
|
|
|
mime::IMAGE_PNG,
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::right(Either::left(
|
|
|
|
crate::exiftool::clear_metadata_bytes_read(bytes)?,
|
|
|
|
))),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
(Some(Format::Webp) | None, ValidInputType::Webp) => Ok((
|
|
|
|
image_webp(),
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::right(Either::right(Either::left(
|
|
|
|
crate::magick::clear_metadata_bytes_read(bytes)?,
|
|
|
|
)))),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
(Some(format), _) => Ok((
|
|
|
|
format.to_mime(),
|
2021-10-21 01:13:39 +00:00
|
|
|
Either::right(Either::right(Either::right(Either::right(
|
|
|
|
crate::magick::convert_bytes_read(bytes, format)?,
|
|
|
|
)))),
|
2021-09-04 00:53:53 +00:00
|
|
|
)),
|
|
|
|
}
|
|
|
|
}
|