/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use std::slice::Iter; use store::{Deserialize, U64_LEN}; use types::{acl::AclGrant, blob::BlobId, id::Id, keyword::*}; use utils::{ codec::leb128::Leb128Iterator, map::{bitmap::Bitmap, vec_map::VecMap}, }; #[derive(Debug, Clone, Default, PartialEq, Eq)] pub struct Object { pub properties: VecMap, } #[derive(Debug, PartialEq, Eq, Hash, Clone)] pub enum Property { Acl, Aliases, Attachments, Bcc, BlobId, BodyStructure, BodyValues, Capabilities, Cc, Charset, Cid, DeliveryStatus, Description, DeviceClientId, Disposition, DsnBlobIds, Email, EmailId, EmailIds, Envelope, Expires, From, FromDate, HasAttachment, Headers, HtmlBody, HtmlSignature, Id, IdentityId, InReplyTo, IsActive, IsEnabled, IsSubscribed, Keys, Keywords, Language, Location, MailboxIds, MayDelete, MdnBlobIds, Members, MessageId, MyRights, Name, ParentId, PartId, Picture, Preview, Quota, ReceivedAt, References, ReplyTo, Role, Secret, SendAt, Sender, SentAt, Size, SortOrder, Subject, SubParts, TextBody, TextSignature, ThreadId, Timezone, To, ToDate, TotalEmails, TotalThreads, Type, Types, UndoStatus, UnreadEmails, UnreadThreads, Url, VerificationCode, Addresses, P256dh, Auth, Value, SmtpReply, Delivered, Displayed, MailFrom, RcptTo, Parameters, IsEncodingProblem, IsTruncated, MayReadItems, MayAddItems, MayRemoveItems, MaySetSeen, MaySetKeywords, MayCreateChild, MayRename, MaySubmit, ResourceType, Used, HardLimit, WarnLimit, SoftLimit, Scope, _T(String), } impl Object { pub fn with_capacity(capacity: usize) -> Self { Self { properties: VecMap::with_capacity(capacity), } } pub fn set(&mut self, property: Property, value: impl Into) -> bool { self.properties.set(property, value.into()) } pub fn append(&mut self, property: Property, value: impl Into) { self.properties.append(property, value.into()); } pub fn with_property(mut self, property: Property, value: impl Into) -> Self { self.properties.append(property, value.into()); self } pub fn remove(&mut self, property: &Property) -> Value { self.properties.remove(property).unwrap_or(Value::Null) } pub fn get(&self, property: &Property) -> &Value { self.properties.get(property).unwrap_or(&Value::Null) } } #[derive(Debug, Default, Clone, PartialEq, Eq)] pub enum Value { Text(String), UnsignedInt(u64), Bool(bool), Id(Id), Date(UTCDate), BlobId(BlobId), Keyword(Keyword), List(Vec), Object(Object), Acl(Vec), Blob(Vec), #[default] Null, } #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] pub struct UTCDate { pub year: u16, pub month: u8, pub day: u8, pub hour: u8, pub minute: u8, pub second: u8, pub tz_before_gmt: bool, pub tz_hour: u8, pub tz_minute: u8, } const TEXT: u8 = 0; const UNSIGNED_INT: u8 = 1; const BOOL_TRUE: u8 = 2; const BOOL_FALSE: u8 = 3; const ID: u8 = 4; const DATE: u8 = 5; const BLOB_ID: u8 = 6; const BLOB: u8 = 7; const KEYWORD: u8 = 8; const LIST: u8 = 9; const OBJECT: u8 = 10; const ACL: u8 = 11; const NULL: u8 = 12; pub trait DeserializeFrom: Sized { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option; } impl Deserialize for Object { fn deserialize(bytes: &[u8]) -> trc::Result { Object::deserialize_from(&mut bytes.iter()).ok_or_else(|| { trc::StoreEvent::DataCorruption .caused_by(trc::location!()) .ctx(trc::Key::Value, bytes) }) } } impl DeserializeFrom for AclGrant { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { let account_id = bytes.next_leb128()?; let mut grants = [0u8; U64_LEN]; for byte in grants.iter_mut() { *byte = *bytes.next()?; } Some(Self { account_id, grants: Bitmap::from(u64::from_be_bytes(grants)), }) } } impl DeserializeFrom for Object { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option> { let len = bytes.next_leb128()?; let mut properties = VecMap::with_capacity(len); for _ in 0..len { let key = Property::deserialize_from(bytes)?; let value = Value::deserialize_from(bytes)?; properties.append(key, value); } Some(Object { properties }) } } impl DeserializeFrom for Value { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { match *bytes.next()? { TEXT => Some(Value::Text(String::deserialize_from(bytes)?)), UNSIGNED_INT => Some(Value::UnsignedInt(bytes.next_leb128()?)), BOOL_TRUE => Some(Value::Bool(true)), BOOL_FALSE => Some(Value::Bool(false)), ID => Some(Value::Id(Id::new(bytes.next_leb128()?))), DATE => Some(Value::Date(UTCDate::from_timestamp( bytes.next_leb128::()? as i64, ))), BLOB_ID => Some(Value::BlobId(BlobId::deserialize_from(bytes)?)), KEYWORD => Some(Value::Keyword(Keyword::deserialize_from(bytes)?)), LIST => { let len = bytes.next_leb128()?; let mut items = Vec::with_capacity(len); for _ in 0..len { items.push(Value::deserialize_from(bytes)?); } Some(Value::List(items)) } OBJECT => Some(Value::Object(Object::deserialize_from(bytes)?)), BLOB => Some(Value::Blob(Vec::deserialize_from(bytes)?)), ACL => { let len = bytes.next_leb128()?; let mut items = Vec::with_capacity(len); for _ in 0..len { items.push(AclGrant::deserialize_from(bytes)?); } Some(Value::Acl(items)) } NULL => Some(Value::Null), _ => None, } } } impl DeserializeFrom for u32 { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { bytes.next_leb128() } } impl DeserializeFrom for u64 { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { bytes.next_leb128() } } impl DeserializeFrom for String { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { >::deserialize_from(bytes).and_then(|s| String::from_utf8(s).ok()) } } impl DeserializeFrom for Vec { fn deserialize_from(bytes: &mut Iter<'_, u8>) -> Option { let len: usize = bytes.next_leb128()?; let mut buf = Vec::with_capacity(len); for _ in 0..len { buf.push(*bytes.next()?); } buf.into() } } impl DeserializeFrom for BlobId { fn deserialize_from(bytes: &mut std::slice::Iter<'_, u8>) -> Option { BlobId::from_iter(bytes) } } impl DeserializeFrom for Keyword { fn deserialize_from(bytes: &mut std::slice::Iter<'_, u8>) -> Option { match bytes.next_leb128::()? { SEEN => Some(Keyword::Seen), DRAFT => Some(Keyword::Draft), FLAGGED => Some(Keyword::Flagged), ANSWERED => Some(Keyword::Answered), RECENT => Some(Keyword::Recent), IMPORTANT => Some(Keyword::Important), PHISHING => Some(Keyword::Phishing), JUNK => Some(Keyword::Junk), NOTJUNK => Some(Keyword::NotJunk), DELETED => Some(Keyword::Deleted), FORWARDED => Some(Keyword::Forwarded), MDN_SENT => Some(Keyword::MdnSent), other => { let len = other - 12; let mut keyword = Vec::with_capacity(len); for _ in 0..len { keyword.push(*bytes.next()?); } Some(Keyword::Other( String::from_utf8(keyword).ok()?.into_boxed_str(), )) } } } } impl DeserializeFrom for Property { fn deserialize_from(bytes: &mut std::slice::Iter<'_, u8>) -> Option { match *bytes.next()? { 0 => Some(Property::IsActive), 1 => Some(Property::IsEnabled), 2 => Some(Property::IsSubscribed), 3 => Some(Property::Keys), 4 => Some(Property::Keywords), 5 => Some(Property::Language), 6 => Some(Property::Location), 7 => Some(Property::MailboxIds), 8 => Some(Property::MayDelete), 9 => Some(Property::MdnBlobIds), 10 => Some(Property::Members), 11 => Some(Property::MessageId), 12 => Some(Property::MyRights), 13 => Some(Property::Name), 14 => Some(Property::ParentId), 15 => Some(Property::PartId), 16 => Some(Property::Picture), 17 => Some(Property::Preview), 18 => Some(Property::Quota), 19 => Some(Property::ReceivedAt), 20 => Some(Property::References), 21 => Some(Property::ReplyTo), 22 => Some(Property::Role), 23 => Some(Property::Secret), 24 => Some(Property::SendAt), 25 => Some(Property::Sender), 26 => Some(Property::SentAt), 27 => Some(Property::Size), 28 => Some(Property::SortOrder), 29 => Some(Property::Subject), 30 => Some(Property::SubParts), 31 => Some(Property::TextBody), 32 => Some(Property::TextSignature), 33 => Some(Property::ThreadId), 34 => Some(Property::Timezone), 35 => Some(Property::To), 36 => Some(Property::ToDate), 37 => Some(Property::TotalEmails), 38 => Some(Property::TotalThreads), 39 => Some(Property::Type), 40 => Some(Property::Types), 41 => Some(Property::UndoStatus), 42 => Some(Property::UnreadEmails), 43 => Some(Property::UnreadThreads), 44 => Some(Property::Url), 45 => Some(Property::VerificationCode), 46 => Some(Property::Parameters), 47 => Some(Property::Addresses), 48 => Some(Property::P256dh), 49 => Some(Property::Auth), 50 => Some(Property::Value), 51 => Some(Property::SmtpReply), 52 => Some(Property::Delivered), 53 => Some(Property::Displayed), 54 => Some(Property::MailFrom), 55 => Some(Property::RcptTo), 56 => Some(Property::IsEncodingProblem), 57 => Some(Property::IsTruncated), 58 => Some(Property::MayReadItems), 59 => Some(Property::MayAddItems), 60 => Some(Property::MayRemoveItems), 61 => Some(Property::MaySetSeen), 62 => Some(Property::MaySetKeywords), 63 => Some(Property::MayCreateChild), 64 => Some(Property::MayRename), 65 => Some(Property::MaySubmit), 66 => Some(Property::Acl), 67 => Some(Property::Aliases), 68 => Some(Property::Attachments), 69 => Some(Property::Bcc), 70 => Some(Property::BlobId), 71 => Some(Property::BodyStructure), 72 => Some(Property::BodyValues), 73 => Some(Property::Capabilities), 74 => Some(Property::Cc), 75 => Some(Property::Charset), 76 => Some(Property::Cid), 77 => Some(Property::DeliveryStatus), 78 => Some(Property::Description), 79 => Some(Property::DeviceClientId), 80 => Some(Property::Disposition), 81 => Some(Property::DsnBlobIds), 82 => Some(Property::Email), 83 => Some(Property::EmailId), 84 => Some(Property::EmailIds), 85 => Some(Property::Envelope), 86 => Some(Property::Expires), 87 => Some(Property::From), 88 => Some(Property::FromDate), 89 => Some(Property::HasAttachment), 91 => Some(Property::Headers), 92 => Some(Property::HtmlBody), 93 => Some(Property::HtmlSignature), 94 => Some(Property::Id), 95 => Some(Property::IdentityId), 96 => Some(Property::InReplyTo), 97 => String::deserialize_from(bytes).map(Property::_T), 98 => Some(Property::ResourceType), 99 => Some(Property::Used), 100 => Some(Property::HardLimit), 101 => Some(Property::WarnLimit), 102 => Some(Property::SoftLimit), 103 => Some(Property::Scope), _ => None, } } } pub trait FromLegacy { fn from_legacy(legacy: Object) -> Self; } pub trait TryFromLegacy: Sized { fn try_from_legacy(legacy: Object) -> Option; } impl Value { pub fn try_unwrap_id(self) -> Option { match self { Value::Id(id) => id.into(), _ => None, } } pub fn try_unwrap_bool(self) -> Option { match self { Value::Bool(b) => b.into(), _ => None, } } pub fn try_unwrap_keyword(self) -> Option { match self { Value::Keyword(k) => k.into(), _ => None, } } pub fn try_unwrap_string(self) -> Option { match self { Value::Text(s) => Some(s), _ => None, } } pub fn try_unwrap_object(self) -> Option> { match self { Value::Object(o) => Some(o), _ => None, } } pub fn try_unwrap_list(self) -> Option> { match self { Value::List(l) => Some(l), _ => None, } } pub fn try_unwrap_date(self) -> Option { match self { Value::Date(d) => Some(d), _ => None, } } pub fn try_unwrap_blob_id(self) -> Option { match self { Value::BlobId(b) => Some(b), _ => None, } } pub fn try_unwrap_uint(self) -> Option { match self { Value::UnsignedInt(u) => Some(u), _ => None, } } pub fn as_string(&self) -> Option<&str> { match self { Value::Text(s) => Some(s), _ => None, } } pub fn as_id(&self) -> Option<&Id> { match self { Value::Id(id) => Some(id), _ => None, } } pub fn as_blob_id(&self) -> Option<&BlobId> { match self { Value::BlobId(id) => Some(id), _ => None, } } pub fn as_list(&self) -> Option<&Vec> { match self { Value::List(l) => Some(l), _ => None, } } pub fn as_acl(&self) -> Option<&Vec> { match self { Value::Acl(l) => Some(l), _ => None, } } pub fn as_uint(&self) -> Option { match self { Value::UnsignedInt(u) => Some(*u), Value::Id(id) => Some(*id.as_ref()), _ => None, } } pub fn as_bool(&self) -> Option { match self { Value::Bool(b) => Some(*b), _ => None, } } pub fn as_date(&self) -> Option<&UTCDate> { match self { Value::Date(d) => Some(d), _ => None, } } pub fn as_obj(&self) -> Option<&Object> { match self { Value::Object(o) => Some(o), _ => None, } } pub fn as_obj_mut(&mut self) -> Option<&mut Object> { match self { Value::Object(o) => Some(o), _ => None, } } pub fn try_cast_uint(&self) -> Option { match self { Value::UnsignedInt(u) => Some(*u), Value::Id(id) => Some(id.id()), Value::Bool(b) => Some(*b as u64), _ => None, } } } impl UTCDate { pub fn from_timestamp(timestamp: i64) -> Self { // Ported from http://howardhinnant.github.io/date_algorithms.html#civil_from_days let (z, seconds) = ((timestamp / 86400) + 719468, timestamp % 86400); let era: i64 = (if z >= 0 { z } else { z - 146096 }) / 146097; let doe: u64 = (z - era * 146097) as u64; // [0, 146096] let yoe: u64 = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365; // [0, 399] let y: i64 = (yoe as i64) + era * 400; let doy: u64 = doe - (365 * yoe + yoe / 4 - yoe / 100); // [0, 365] let mp = (5 * doy + 2) / 153; // [0, 11] let d: u64 = doy - (153 * mp + 2) / 5 + 1; // [1, 31] let m: u64 = if mp < 10 { mp + 3 } else { mp - 9 }; // [1, 12] let (h, mn, s) = (seconds / 3600, (seconds / 60) % 60, seconds % 60); UTCDate { year: (y + i64::from(m <= 2)) as u16, month: m as u8, day: d as u8, hour: h as u8, minute: mn as u8, second: s as u8, tz_before_gmt: false, tz_hour: 0, tz_minute: 0, } } pub fn timestamp(&self) -> i64 { // Ported from https://github.com/protocolbuffers/upb/blob/22182e6e/upb/json_decode.c#L982-L992 let month = self.month as u32; let year_base = 4800; /* Before min year, multiple of 400. */ let m_adj = month.wrapping_sub(3); /* March-based month. */ let carry = i64::from(m_adj > month); let adjust = if carry > 0 { 12 } else { 0 }; let y_adj = self.year as i64 + year_base - carry; let month_days = ((m_adj.wrapping_add(adjust)) * 62719 + 769) / 2048; let leap_days = y_adj / 4 - y_adj / 100 + y_adj / 400; (y_adj * 365 + leap_days + month_days as i64 + (self.day as i64 - 1) - 2472632) * 86400 + self.hour as i64 * 3600 + self.minute as i64 * 60 + self.second as i64 + ((self.tz_hour as i64 * 3600 + self.tz_minute as i64 * 60) * if self.tz_before_gmt { 1 } else { -1 }) } }