Files
Stalwart/crates/jmap-proto/src/method/upload.rs
2024-07-17 18:33:22 +02:00

216 lines
6.6 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use ahash::AHashMap;
use mail_parser::decoders::base64::base64_decode;
use utils::map::vec_map::VecMap;
use crate::{
error::set::SetError,
parser::{json::Parser, Ignore, JsonObjectParser, Token},
request::{reference::MaybeReference, RequestProperty},
response::Response,
types::{blob::BlobId, id::Id},
};
use super::ahash_is_empty;
#[derive(Debug, Clone)]
pub struct BlobUploadRequest {
pub account_id: Id,
pub create: VecMap<String, UploadObject>,
}
#[derive(Debug, Clone)]
pub struct UploadObject {
pub type_: Option<String>,
pub data: Vec<DataSourceObject>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DataSourceObject {
Id {
id: MaybeReference<BlobId, String>,
length: Option<usize>,
offset: Option<usize>,
},
Value(Vec<u8>),
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct BlobUploadResponse {
#[serde(rename = "accountId")]
pub account_id: Id,
#[serde(rename = "created")]
#[serde(skip_serializing_if = "ahash_is_empty")]
pub created: AHashMap<String, BlobUploadResponseObject>,
#[serde(rename = "notCreated")]
#[serde(skip_serializing_if = "VecMap::is_empty")]
pub not_created: VecMap<String, SetError>,
}
#[derive(Debug, Clone, Default, serde::Serialize)]
pub struct BlobUploadResponseObject {
pub id: BlobId,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub type_: Option<String>,
pub size: usize,
}
impl JsonObjectParser for BlobUploadRequest {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut request = BlobUploadRequest {
account_id: Id::default(),
create: VecMap::new(),
};
parser
.next_token::<String>()?
.assert_jmap(Token::DictStart)?;
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
match &key.hash[0] {
0x0064_4974_6e75_6f63_6361 if !key.is_ref => {
request.account_id = parser.next_token::<Id>()?.unwrap_string("accountId")?;
}
0x6574_6165_7263 if !key.is_ref => {
request.create = <VecMap<String, UploadObject>>::parse(parser)?;
}
_ => {
parser.skip_token(parser.depth_array, parser.depth_dict)?;
}
}
}
Ok(request)
}
}
impl JsonObjectParser for UploadObject {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut request = UploadObject {
type_: None,
data: Vec::new(),
};
parser
.next_token::<String>()?
.assert_jmap(Token::DictStart)?;
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
match &key.hash[0] {
0x6570_7974 if !key.is_ref => {
request.type_ = parser
.next_token::<String>()?
.unwrap_string_or_null("type")?;
}
0x6174_6164 if !key.is_ref => {
parser.next_token::<Ignore>()?.assert(Token::ArrayStart)?;
loop {
match parser.next_token::<Ignore>()? {
Token::Comma => (),
Token::ArrayEnd => break,
Token::DictStart => {
request.data.push(DataSourceObject::parse(parser)?);
}
token => return Err(token.error("", "DataSourceObject")),
}
}
}
_ => {
parser.skip_token(parser.depth_array, parser.depth_dict)?;
}
}
}
Ok(request)
}
}
impl JsonObjectParser for DataSourceObject {
fn parse(parser: &mut Parser<'_>) -> trc::Result<Self>
where
Self: Sized,
{
let mut data: Option<Vec<u8>> = None;
let mut blob_id: Option<MaybeReference<BlobId, String>> = None;
let mut offset: Option<usize> = None;
let mut length: Option<usize> = None;
while let Some(key) = parser.next_dict_key::<RequestProperty>()? {
match &key.hash[0] {
0x0074_7865_5473_613a_6174_6164 if !key.is_ref => {
data = parser
.next_token::<String>()?
.unwrap_string("data:asText")?
.into_bytes()
.into();
}
0x0034_3665_7361_4273_613a_6174_6164 if !key.is_ref => {
data = base64_decode(
parser
.next_token::<String>()?
.unwrap_string("data:asBase64")?
.as_bytes(),
)
.ok_or_else(|| parser.error("Failed to decode data:asBase64"))?
.into();
}
0x6449_626f_6c62 if !key.is_ref => {
blob_id = parser
.next_token::<MaybeReference<BlobId, String>>()?
.unwrap_string("blobId")?
.into();
}
0x6874_676e_656c if !key.is_ref => {
length = parser
.next_token::<Ignore>()?
.unwrap_usize_or_null("length")?;
}
0x7465_7366_666f if !key.is_ref => {
offset = parser
.next_token::<Ignore>()?
.unwrap_usize_or_null("offset")?;
}
_ => {
parser.skip_token(parser.depth_array, parser.depth_dict)?;
}
}
}
if let Some(data) = data {
Ok(DataSourceObject::Value(data))
} else if let Some(blob_id) = blob_id {
Ok(DataSourceObject::Id {
id: blob_id,
length,
offset,
})
} else {
Err(parser.error("Missing data or blobId in DataSourceObject"))
}
}
}
impl BlobUploadResponse {
pub fn update_created_ids(&self, response: &mut Response) {
for (user_id, obj) in &self.created {
response
.created_ids
.insert(user_id.clone(), obj.id.clone().into());
}
}
}