Import upstream v0.16.22, stripped
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.
This commit is contained in:
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::{decode_mailto_address, extract_addr_spec};
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use calcard::{
|
||||
common::{IanaString, PartialDateTime},
|
||||
icalendar::{
|
||||
ICalendar, ICalendarComponent, ICalendarDuration, ICalendarEntry, ICalendarMethod,
|
||||
ICalendarParameter, ICalendarParticipationRole, ICalendarParticipationStatus,
|
||||
ICalendarPeriod, ICalendarProperty, ICalendarRecurrenceRule,
|
||||
ICalendarScheduleForceSendValue, ICalendarStatus, ICalendarUserTypes, ICalendarValue, Uri,
|
||||
},
|
||||
};
|
||||
use indexmap::IndexSet;
|
||||
use registry::schema::structs::TaskCalendarItipContents;
|
||||
use std::{fmt::Display, hash::Hash};
|
||||
use utils::sanitize_email;
|
||||
|
||||
pub mod attendee;
|
||||
pub mod event_cancel;
|
||||
pub mod event_create;
|
||||
pub mod event_update;
|
||||
pub mod format;
|
||||
pub mod inbound;
|
||||
pub mod itip;
|
||||
pub mod organizer;
|
||||
pub mod snapshot;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItipSnapshots<'x> {
|
||||
pub organizer: Organizer<'x>,
|
||||
pub uid: &'x str,
|
||||
pub components: AHashMap<InstanceId, ItipSnapshot<'x>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItipSnapshot<'x> {
|
||||
pub comp_id: u16,
|
||||
pub comp: &'x ICalendarComponent,
|
||||
pub attendees: AHashSet<Attendee<'x>>,
|
||||
pub dtstamp: Option<&'x PartialDateTime>,
|
||||
pub entries: ItipEntries<'x>,
|
||||
pub sequence: Option<i64>,
|
||||
pub request_status: Vec<&'x str>,
|
||||
}
|
||||
|
||||
pub type ItipEntries<'x> = IndexSet<ItipEntry<'x>, ahash::RandomState>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ItipEntry<'x> {
|
||||
pub name: &'x ICalendarProperty,
|
||||
pub value: ItipEntryValue<'x>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ItipEntryValue<'x> {
|
||||
DateTime(ItipDateTime<'x>),
|
||||
Period(&'x ICalendarPeriod),
|
||||
Duration(&'x ICalendarDuration),
|
||||
Status(&'x ICalendarStatus),
|
||||
RRule(&'x ICalendarRecurrenceRule),
|
||||
Text(&'x str),
|
||||
Integer(i64),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItipDateTime<'x> {
|
||||
pub date: &'x PartialDateTime,
|
||||
pub tz_id: Option<&'x str>,
|
||||
pub tz_code: u16,
|
||||
pub timestamp: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum InstanceId {
|
||||
Main,
|
||||
Recurrence(RecurrenceId),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord)]
|
||||
pub struct RecurrenceId {
|
||||
pub entry_id: u16,
|
||||
pub date: i64,
|
||||
pub this_and_future: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Attendee<'x> {
|
||||
pub entry_id: u16,
|
||||
pub email: Email,
|
||||
pub name: Option<&'x str>,
|
||||
pub part_stat: Option<&'x ICalendarParticipationStatus>,
|
||||
pub delegated_from: Vec<Email>,
|
||||
pub delegated_to: Vec<Email>,
|
||||
pub role: Option<&'x ICalendarParticipationRole>,
|
||||
pub cu_type: Option<&'x ICalendarUserTypes>,
|
||||
pub sent_by: Option<Email>,
|
||||
pub rsvp: Option<bool>,
|
||||
pub is_server_scheduling: bool,
|
||||
pub force_send: Option<&'x ICalendarScheduleForceSendValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Organizer<'x> {
|
||||
pub entry_id: u16,
|
||||
pub email: Email,
|
||||
pub name: Option<&'x str>,
|
||||
pub is_server_scheduling: bool,
|
||||
pub force_send: Option<&'x ICalendarScheduleForceSendValue>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Email {
|
||||
pub email: String,
|
||||
pub is_local: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ItipError {
|
||||
NoSchedulingInfo,
|
||||
OtherSchedulingAgent,
|
||||
NotOrganizer,
|
||||
NotOrganizerNorAttendee,
|
||||
NothingToSend,
|
||||
MissingUid,
|
||||
MultipleUid,
|
||||
MultipleOrganizer,
|
||||
MultipleObjectTypes,
|
||||
MultipleObjectInstances,
|
||||
CannotModifyProperty(ICalendarProperty),
|
||||
CannotModifyInstance,
|
||||
CannotModifyAddress,
|
||||
OrganizerMismatch,
|
||||
MissingMethod,
|
||||
InvalidComponentType,
|
||||
OutOfSequence,
|
||||
OrganizerIsLocalAddress,
|
||||
SenderIsNotOrganizerNorAttendee,
|
||||
SenderIsNotParticipant(String),
|
||||
UnknownParticipant(String),
|
||||
UnsupportedMethod(ICalendarMethod),
|
||||
ICalendarParseError,
|
||||
EventNotFound,
|
||||
EventTooLarge,
|
||||
QuotaExceeded,
|
||||
NoDefaultCalendar,
|
||||
AutoAddDisabled,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ItipMessage<T> {
|
||||
pub from: String,
|
||||
pub from_organizer: bool,
|
||||
pub to: Vec<String>,
|
||||
pub summary: ItipSummary,
|
||||
pub message: T,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub enum ItipSummary {
|
||||
Invite(Vec<ItipField>),
|
||||
Update {
|
||||
method: ICalendarMethod,
|
||||
current: Vec<ItipField>,
|
||||
previous: Vec<ItipField>,
|
||||
},
|
||||
Cancel(Vec<ItipField>),
|
||||
Rsvp {
|
||||
part_stat: ICalendarParticipationStatus,
|
||||
current: Vec<ItipField>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ItipField {
|
||||
pub name: ICalendarProperty,
|
||||
pub value: ItipValue,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(tag = "type", content = "value")]
|
||||
pub enum ItipValue {
|
||||
Text(String),
|
||||
Time(ItipTime),
|
||||
Rrule(Box<ICalendarRecurrenceRule>),
|
||||
Participants(Vec<ItipParticipant>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ItipTime {
|
||||
pub start: i64,
|
||||
pub tz_id: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ItipParticipant {
|
||||
pub email: String,
|
||||
pub name: Option<String>,
|
||||
pub is_organizer: bool,
|
||||
}
|
||||
|
||||
pub struct ItipMessages {
|
||||
pub messages: Vec<TaskCalendarItipContents>,
|
||||
}
|
||||
|
||||
impl Attendee<'_> {
|
||||
pub fn send_invite_messages(&self) -> bool {
|
||||
!self.email.is_local
|
||||
&& self.is_server_scheduling
|
||||
&& self.rsvp.is_none_or(|rsvp| rsvp)
|
||||
&& (self.force_send.is_some()
|
||||
|| self.part_stat.is_none_or(|part_stat| {
|
||||
part_stat == &ICalendarParticipationStatus::NeedsAction
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn send_update_messages(&self) -> bool {
|
||||
!self.email.is_local
|
||||
&& self.is_server_scheduling
|
||||
&& self.rsvp.is_none_or(|rsvp| rsvp)
|
||||
&& (self.force_send.is_some()
|
||||
|| self
|
||||
.part_stat
|
||||
.is_none_or(|part_stat| part_stat != &ICalendarParticipationStatus::Declined))
|
||||
}
|
||||
|
||||
pub fn is_delegated_from(&self, attendee: &Attendee<'_>) -> bool {
|
||||
self.delegated_from
|
||||
.iter()
|
||||
.any(|d| d.email == attendee.email.email)
|
||||
}
|
||||
|
||||
pub fn is_delegated_to(&self, attendee: &Attendee<'_>) -> bool {
|
||||
self.delegated_to
|
||||
.iter()
|
||||
.any(|d| d.email == attendee.email.email)
|
||||
}
|
||||
}
|
||||
|
||||
impl Email {
|
||||
pub fn new(email: &str, local_addresses: &[String]) -> Option<Self> {
|
||||
let decoded = decode_mailto_address(email.trim());
|
||||
let email = sanitize_email(decoded.as_ref())
|
||||
.or_else(|| extract_addr_spec(decoded.as_ref()).and_then(sanitize_email))?;
|
||||
let is_local = local_addresses.contains(&email);
|
||||
Some(Email { email, is_local })
|
||||
}
|
||||
|
||||
pub fn from_uri(uri: &Uri, local_addresses: &[String]) -> Option<Self> {
|
||||
if let Uri::Location(uri) = uri {
|
||||
Email::new(uri.as_str(), local_addresses)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ical_size(ical: &ICalendar) -> usize {
|
||||
struct SizeWriter(usize);
|
||||
|
||||
impl std::fmt::Write for SizeWriter {
|
||||
fn write_str(&mut self, text: &str) -> std::fmt::Result {
|
||||
self.0 += text.len();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
let mut writer = SizeWriter(0);
|
||||
let _ = ical.write_to(&mut writer);
|
||||
writer.0
|
||||
}
|
||||
|
||||
impl PartialEq for Attendee<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.email == other.email
|
||||
&& self.part_stat == other.part_stat
|
||||
&& self.delegated_from == other.delegated_from
|
||||
&& self.delegated_to == other.delegated_to
|
||||
&& self.role == other.role
|
||||
&& self.cu_type == other.cu_type
|
||||
&& self.sent_by == other.sent_by
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Attendee<'_> {}
|
||||
|
||||
impl Hash for Attendee<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.email.hash(state);
|
||||
self.part_stat.hash(state);
|
||||
self.delegated_from.hash(state);
|
||||
self.delegated_to.hash(state);
|
||||
self.role.hash(state);
|
||||
self.cu_type.hash(state);
|
||||
self.sent_by.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Email {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "mailto:{}", self.email)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Email {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.email.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Email {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.email == other.email
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Email {}
|
||||
|
||||
impl PartialEq for RecurrenceId {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.date == other.date && self.this_and_future == other.this_and_future
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for RecurrenceId {}
|
||||
|
||||
impl Hash for RecurrenceId {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.date.hash(state);
|
||||
self.this_and_future.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for ItipDateTime<'_> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.timestamp == other.timestamp
|
||||
}
|
||||
}
|
||||
impl Eq for ItipDateTime<'_> {}
|
||||
|
||||
impl Hash for ItipDateTime<'_> {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.timestamp.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl ItipDateTime<'_> {
|
||||
pub fn to_entry(&self, name: ICalendarProperty) -> ICalendarEntry {
|
||||
ICalendarEntry {
|
||||
name,
|
||||
params: self
|
||||
.tz_id
|
||||
.map(|tz_id| vec![ICalendarParameter::tzid(tz_id.to_string())])
|
||||
.unwrap_or_default(),
|
||||
values: vec![ICalendarValue::PartialDateTime(Box::new(self.date.clone()))],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ItipError {
|
||||
pub fn is_jmap_error(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
ItipError::MultipleOrganizer
|
||||
| ItipError::OrganizerIsLocalAddress
|
||||
| ItipError::SenderIsNotParticipant(_)
|
||||
| ItipError::OrganizerMismatch
|
||||
| ItipError::CannotModifyProperty(_)
|
||||
| ItipError::CannotModifyInstance
|
||||
| ItipError::CannotModifyAddress
|
||||
//| ItipError::MissingUid
|
||||
| ItipError::MultipleUid
|
||||
| ItipError::MultipleObjectTypes
|
||||
| ItipError::MultipleObjectInstances
|
||||
| ItipError::MissingMethod
|
||||
| ItipError::InvalidComponentType
|
||||
| ItipError::OutOfSequence
|
||||
| ItipError::UnknownParticipant(_)
|
||||
| ItipError::UnsupportedMethod(_)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ItipError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
ItipError::NoSchedulingInfo => write!(f, "No scheduling information found"),
|
||||
ItipError::OtherSchedulingAgent => write!(f, "Other scheduling agent"),
|
||||
ItipError::NotOrganizer => write!(f, "Not the organizer of the event"),
|
||||
ItipError::NotOrganizerNorAttendee => write!(f, "Not an organizer or attendee"),
|
||||
ItipError::NothingToSend => write!(f, "No iTIP messages to send"),
|
||||
ItipError::MissingUid => write!(f, "Missing UID in iCalendar object"),
|
||||
ItipError::MultipleUid => write!(f, "Multiple UIDs found in iCalendar object"),
|
||||
ItipError::MultipleOrganizer => {
|
||||
write!(f, "Multiple organizers found in iCalendar object")
|
||||
}
|
||||
ItipError::MultipleObjectTypes => {
|
||||
write!(f, "Multiple object types found in iCalendar object")
|
||||
}
|
||||
ItipError::MultipleObjectInstances => {
|
||||
write!(f, "Multiple object instances found in iCalendar object")
|
||||
}
|
||||
ItipError::CannotModifyProperty(prop) => {
|
||||
write!(f, "Cannot modify property {}", prop.as_str())
|
||||
}
|
||||
ItipError::CannotModifyInstance => write!(f, "Cannot modify instance of the event"),
|
||||
ItipError::CannotModifyAddress => write!(f, "Cannot modify address of the event"),
|
||||
ItipError::OrganizerMismatch => write!(f, "Organizer mismatch in iCalendar object"),
|
||||
ItipError::MissingMethod => write!(f, "Missing method in the iTIP message"),
|
||||
ItipError::InvalidComponentType => {
|
||||
write!(f, "Invalid component type in iCalendar object")
|
||||
}
|
||||
ItipError::OutOfSequence => write!(f, "Old sequence number found"),
|
||||
ItipError::OrganizerIsLocalAddress => {
|
||||
write!(
|
||||
f,
|
||||
"Organizer matches one of the recipient's account addresses"
|
||||
)
|
||||
}
|
||||
ItipError::SenderIsNotParticipant(participant) => {
|
||||
write!(f, "Sender {participant:?} is not a participant")
|
||||
}
|
||||
ItipError::SenderIsNotOrganizerNorAttendee => {
|
||||
write!(f, "Sender is neither organizer nor attendee")
|
||||
}
|
||||
ItipError::UnknownParticipant(participant) => {
|
||||
write!(f, "Unknown participant: {}", participant)
|
||||
}
|
||||
ItipError::UnsupportedMethod(method) => {
|
||||
write!(f, "Unsupported method: {}", method.as_str())
|
||||
}
|
||||
ItipError::ICalendarParseError => write!(f, "Failed to parse iCalendar object"),
|
||||
ItipError::EventNotFound => write!(f, "Event found in index but not in database"),
|
||||
ItipError::EventTooLarge => write!(
|
||||
f,
|
||||
"Applying the iTIP message would exceed the maximum event size"
|
||||
),
|
||||
ItipError::QuotaExceeded => write!(f, "Quota exceeded"),
|
||||
ItipError::NoDefaultCalendar => write!(f, "No default calendar found for the account"),
|
||||
ItipError::AutoAddDisabled => {
|
||||
write!(f, "Auto-adding events is disabled for this account")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user