Upstream commit: 474dd0229cb20cf513036619781ed97bd8073c3f Enterprise-only files removed or emptied: 63 Enterprise-only snippets removed: 117 in 50 files Dangling module declarations removed: 5 Cargo edits turning enterprise off: 14 Verification: clean Enterprise feature gates left for rebuilt features: 19 in 18 files Produced by tools/fork/strip.py. The full report is in docs/fork/strip-reports/ on main.
249 lines
7.0 KiB
Rust
249 lines
7.0 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use super::Request;
|
|
use crate::{
|
|
error::request::{RequestError, RequestErrorType, RequestLimitError},
|
|
object::AnyId,
|
|
request::{Call, deserialize::DeserializeArguments},
|
|
response::{Response, ResponseMethod, serialize::serialize_hex, status::PushObject},
|
|
};
|
|
use serde::{
|
|
Deserialize, Deserializer,
|
|
de::{self, MapAccess, Visitor},
|
|
};
|
|
use std::{borrow::Cow, collections::HashMap, fmt};
|
|
use types::type_state::DataType;
|
|
|
|
#[derive(Debug)]
|
|
pub struct WebSocketRequest<'x> {
|
|
pub id: Option<String>,
|
|
pub request: Request<'x>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Serialize)]
|
|
pub struct WebSocketResponse<'x> {
|
|
#[serde(rename = "@type")]
|
|
_type: WebSocketResponseType,
|
|
|
|
#[serde(rename = "methodResponses")]
|
|
method_responses: Vec<Call<ResponseMethod<'x>>>,
|
|
|
|
#[serde(rename = "sessionState")]
|
|
#[serde(serialize_with = "serialize_hex")]
|
|
session_state: u32,
|
|
|
|
#[serde(rename(deserialize = "createdIds"))]
|
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
|
created_ids: HashMap<String, AnyId>,
|
|
|
|
#[serde(rename = "requestId")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
request_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
|
|
pub enum WebSocketResponseType {
|
|
Response,
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq)]
|
|
pub struct WebSocketPushEnable {
|
|
pub data_types: Vec<DataType>,
|
|
pub push_state: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum WebSocketMessage<'x> {
|
|
Request(WebSocketRequest<'x>),
|
|
PushEnable(WebSocketPushEnable),
|
|
PushDisable,
|
|
}
|
|
|
|
#[derive(serde::Serialize, Debug)]
|
|
pub struct WebSocketPushObject {
|
|
#[serde(flatten)]
|
|
pub push: PushObject,
|
|
|
|
#[serde(rename = "pushState")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub push_state: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, serde::Serialize)]
|
|
pub struct WebSocketRequestError<'x> {
|
|
#[serde(rename = "@type")]
|
|
pub type_: WebSocketRequestErrorType,
|
|
|
|
#[serde(rename = "type")]
|
|
p_type: RequestErrorType,
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
limit: Option<RequestLimitError>,
|
|
status: u16,
|
|
detail: Cow<'x, str>,
|
|
|
|
#[serde(rename = "requestId")]
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub request_id: Option<String>,
|
|
}
|
|
|
|
#[derive(serde::Serialize, Debug)]
|
|
pub enum WebSocketRequestErrorType {
|
|
RequestError,
|
|
}
|
|
|
|
enum MessageType {
|
|
Request,
|
|
PushEnable,
|
|
PushDisable,
|
|
None,
|
|
}
|
|
|
|
impl<'x> WebSocketMessage<'x> {
|
|
pub fn parse(json: &'x [u8], max_calls: usize, max_size: usize) -> trc::Result<Self> {
|
|
if json.len() <= max_size {
|
|
match serde_json::from_slice::<Self>(json) {
|
|
Ok(WebSocketMessage::Request(req))
|
|
if req.request.method_calls.len() > max_calls =>
|
|
{
|
|
Err(trc::LimitEvent::CallsIn.into_err())
|
|
}
|
|
Ok(msg) => Ok(msg),
|
|
Err(err) => Err(trc::JmapEvent::NotRequest
|
|
.into_err()
|
|
.details(format!("Invalid WebSocket JMAP request {err}"))),
|
|
}
|
|
} else {
|
|
Err(trc::LimitEvent::SizeRequest.into_err())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'de: 'x, 'x: 'de> Deserialize<'de> for WebSocketMessage<'x> {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
deserializer.deserialize_map(WebSocketMessageVisitor)
|
|
}
|
|
}
|
|
|
|
struct WebSocketMessageVisitor;
|
|
|
|
impl<'de> Visitor<'de> for WebSocketMessageVisitor {
|
|
type Value = WebSocketMessage<'de>;
|
|
|
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
|
formatter.write_str("a WebSocketMessage as a map")
|
|
}
|
|
|
|
fn visit_map<V>(self, mut map: V) -> Result<WebSocketMessage<'de>, V::Error>
|
|
where
|
|
V: MapAccess<'de>,
|
|
{
|
|
let mut message_type = MessageType::None;
|
|
let mut request = WebSocketRequest {
|
|
id: None,
|
|
request: Request::default(),
|
|
};
|
|
let mut push_enable = WebSocketPushEnable::default();
|
|
|
|
let mut found_request_keys = false;
|
|
let mut found_push_keys = false;
|
|
|
|
while let Some(key) = map.next_key::<&str>()? {
|
|
hashify::fnc_map!(key.as_bytes(),
|
|
b"@type" => {
|
|
message_type = MessageType::parse(map.next_value()?);
|
|
},
|
|
b"dataTypes" => {
|
|
push_enable.data_types = map.next_value::<Option<Vec<DataType>>>()?.unwrap_or_default();
|
|
found_push_keys = true;
|
|
},
|
|
b"pushState" => {
|
|
push_enable.push_state = map.next_value()?;
|
|
found_push_keys = true;
|
|
},
|
|
b"id" => {
|
|
request.id = map.next_value()?;
|
|
},
|
|
_ => {
|
|
request.request.deserialize_argument(key, &mut map)?;
|
|
found_request_keys = true;
|
|
}
|
|
);
|
|
}
|
|
|
|
match message_type {
|
|
MessageType::Request if found_request_keys => Ok(WebSocketMessage::Request(request)),
|
|
MessageType::PushEnable if found_push_keys => {
|
|
Ok(WebSocketMessage::PushEnable(push_enable))
|
|
}
|
|
MessageType::PushDisable if !found_request_keys && !found_push_keys => {
|
|
Ok(WebSocketMessage::PushDisable)
|
|
}
|
|
_ => Err(de::Error::custom("Invalid WebSocket JMAP request")),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl MessageType {
|
|
fn parse(s: &str) -> Self {
|
|
hashify::tiny_map!(s.as_bytes(),
|
|
b"Request" => MessageType::Request,
|
|
b"WebSocketPushEnable" => MessageType::PushEnable,
|
|
b"WebSocketPushDisable" => MessageType::PushDisable,
|
|
)
|
|
.unwrap_or(MessageType::None)
|
|
}
|
|
}
|
|
|
|
impl<'x> WebSocketRequestError<'x> {
|
|
pub fn from_error(error: RequestError<'x>, request_id: Option<String>) -> Self {
|
|
Self {
|
|
type_: WebSocketRequestErrorType::RequestError,
|
|
p_type: error.p_type,
|
|
limit: error.limit,
|
|
status: error.status,
|
|
detail: error.detail,
|
|
request_id,
|
|
}
|
|
}
|
|
|
|
pub fn to_json(&self) -> String {
|
|
serde_json::to_string(self).unwrap()
|
|
}
|
|
}
|
|
|
|
impl<'x> From<RequestError<'x>> for WebSocketRequestError<'x> {
|
|
fn from(value: RequestError<'x>) -> Self {
|
|
Self::from_error(value, None)
|
|
}
|
|
}
|
|
|
|
impl<'x> WebSocketResponse<'x> {
|
|
pub fn from_response(response: Response<'x>, request_id: Option<String>) -> Self {
|
|
Self {
|
|
_type: WebSocketResponseType::Response,
|
|
method_responses: response.method_responses,
|
|
session_state: response.session_state,
|
|
created_ids: response.created_ids,
|
|
request_id,
|
|
}
|
|
}
|
|
|
|
pub fn to_json(&self) -> String {
|
|
serde_json::to_string(self).unwrap()
|
|
}
|
|
}
|
|
|
|
impl WebSocketPushObject {
|
|
pub fn to_json(&self) -> String {
|
|
serde_json::to_string(self).unwrap()
|
|
}
|
|
}
|