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,283 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use calcard::vcard::VCardVersion;
|
||||
use registry::schema::{
|
||||
enums::VCardVersion as RegistryVCardVersion,
|
||||
structs::{
|
||||
AddressBook, Calendar, CalendarAlarm, CalendarScheduling, DataRetention, FileStorage,
|
||||
Sharing, SystemSettings, WebDav,
|
||||
},
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use store::registry::bootstrap::Bootstrap;
|
||||
use utils::template::Template;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct GroupwareConfig {
|
||||
// DAV settings
|
||||
pub max_request_size: usize,
|
||||
pub dead_property_size: Option<usize>,
|
||||
pub live_property_size: usize,
|
||||
pub max_lock_timeout: u64,
|
||||
pub max_locks_per_user: usize,
|
||||
pub max_results: usize,
|
||||
pub assisted_discovery: bool,
|
||||
|
||||
// Calendar settings
|
||||
pub max_ical_size: usize,
|
||||
pub max_ical_instances: usize,
|
||||
pub max_ical_attendees_per_instance: usize,
|
||||
pub default_calendar_name: Option<String>,
|
||||
pub default_calendar_display_name: Option<String>,
|
||||
pub alarms_enabled: bool,
|
||||
pub alarms_minimum_interval: i64,
|
||||
pub alarms_allow_external_recipients: bool,
|
||||
pub alarms_from_name: String,
|
||||
pub alarms_from_email: Option<String>,
|
||||
pub alarms_template: Template<CalendarTemplateVariable>,
|
||||
pub itip_enabled: bool,
|
||||
pub itip_auto_add: bool,
|
||||
pub itip_inbound_max_ical_size: usize,
|
||||
pub itip_outbound_max_recipients: usize,
|
||||
pub itip_http_rsvp_url: Option<String>,
|
||||
pub itip_http_rsvp_expiration: u64,
|
||||
pub itip_inbox_auto_expunge: Option<u64>,
|
||||
pub itip_template: Template<CalendarTemplateVariable>,
|
||||
|
||||
// Addressbook settings
|
||||
pub max_vcard_size: usize,
|
||||
pub vcard_version: VCardVersion,
|
||||
pub default_addressbook_name: Option<String>,
|
||||
pub default_addressbook_display_name: Option<String>,
|
||||
|
||||
// File storage settings
|
||||
pub max_file_size: usize,
|
||||
|
||||
// Sharing settings
|
||||
pub max_shares_per_item: usize,
|
||||
pub allow_directory_query: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
|
||||
pub enum CalendarTemplateVariable {
|
||||
#[default]
|
||||
PageTitle,
|
||||
Lang,
|
||||
Dir,
|
||||
Header,
|
||||
Footer,
|
||||
EventTitle,
|
||||
EventDescription,
|
||||
EventDetails,
|
||||
Actions,
|
||||
ActionUrl,
|
||||
ActionName,
|
||||
AttendeesTitle,
|
||||
Attendees,
|
||||
Key,
|
||||
Color,
|
||||
Changed,
|
||||
Value,
|
||||
Link,
|
||||
LogoCid,
|
||||
OldValue,
|
||||
Rsvp,
|
||||
}
|
||||
|
||||
impl GroupwareConfig {
|
||||
pub async fn parse(bp: &mut Bootstrap) -> Self {
|
||||
let calendar = bp.setting_infallible::<Calendar>().await;
|
||||
let alarm = bp.setting_infallible::<CalendarAlarm>().await;
|
||||
let sched = bp.setting_infallible::<CalendarScheduling>().await;
|
||||
let book = bp.setting_infallible::<AddressBook>().await;
|
||||
let dav = bp.setting_infallible::<WebDav>().await;
|
||||
let file = bp.setting_infallible::<FileStorage>().await;
|
||||
let share = bp.setting_infallible::<Sharing>().await;
|
||||
let dr = bp.setting_infallible::<DataRetention>().await;
|
||||
let system = bp.setting_infallible::<SystemSettings>().await;
|
||||
|
||||
GroupwareConfig {
|
||||
max_request_size: dav.request_max_size as usize,
|
||||
dead_property_size: dav.dead_property_max_size.map(|v| v as usize),
|
||||
live_property_size: dav.live_property_max_size as usize,
|
||||
assisted_discovery: dav.enable_assisted_discovery,
|
||||
max_lock_timeout: dav.max_lock_timeout.into_inner().as_secs(),
|
||||
max_locks_per_user: dav.max_locks as usize,
|
||||
max_results: dav.max_results as usize,
|
||||
default_calendar_name: calendar.default_href_name,
|
||||
default_calendar_display_name: calendar.default_display_name,
|
||||
default_addressbook_name: book.default_href_name,
|
||||
default_addressbook_display_name: book.default_display_name,
|
||||
max_ical_size: calendar.max_i_calendar_size as usize,
|
||||
max_ical_instances: calendar.max_recurrence_expansions as usize,
|
||||
max_ical_attendees_per_instance: calendar.max_attendees as usize,
|
||||
max_vcard_size: book.max_v_card_size as usize,
|
||||
vcard_version: match book.v_card_version {
|
||||
RegistryVCardVersion::V3 => VCardVersion::V3_0,
|
||||
RegistryVCardVersion::V4 => VCardVersion::V4_0,
|
||||
},
|
||||
max_file_size: file.max_size as usize,
|
||||
alarms_enabled: alarm.enable,
|
||||
alarms_minimum_interval: alarm.min_trigger_interval.into_inner().as_secs() as i64,
|
||||
alarms_allow_external_recipients: alarm.allow_external_rcpts,
|
||||
alarms_from_name: alarm.from_name,
|
||||
alarms_from_email: alarm.from_email,
|
||||
alarms_template: Template::parse(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-alarm.html.min"
|
||||
)))
|
||||
.expect("Failed to parse calendar template"),
|
||||
itip_enabled: sched.enable,
|
||||
itip_auto_add: sched.auto_add_invitations,
|
||||
itip_inbound_max_ical_size: sched.itip_max_size as usize,
|
||||
itip_outbound_max_recipients: sched.max_recipients as usize,
|
||||
itip_inbox_auto_expunge: dr
|
||||
.expunge_scheduling_inbox_after
|
||||
.map(|d| d.into_inner().as_secs()),
|
||||
itip_http_rsvp_url: if sched.http_rsvp_enable {
|
||||
if let Some(url) = sched
|
||||
.http_rsvp_url
|
||||
.as_deref()
|
||||
.map(|v| v.trim().trim_end_matches('/'))
|
||||
.filter(|v| !v.is_empty())
|
||||
{
|
||||
Some(url.to_string())
|
||||
} else {
|
||||
Some(format!("https://{}/calendar/rsvp", system.default_hostname))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
},
|
||||
max_shares_per_item: share.max_shares as usize,
|
||||
allow_directory_query: share.allow_directory_queries,
|
||||
itip_http_rsvp_expiration: sched.http_rsvp_link_expiry.into_inner().as_secs(),
|
||||
itip_template: Template::parse(include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-invite.html.min"
|
||||
)))
|
||||
.expect("Failed to parse calendar template"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CalendarTemplateVariable {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"page_title" => Ok(CalendarTemplateVariable::PageTitle),
|
||||
"lang" => Ok(CalendarTemplateVariable::Lang),
|
||||
"dir" => Ok(CalendarTemplateVariable::Dir),
|
||||
"header" => Ok(CalendarTemplateVariable::Header),
|
||||
"footer" => Ok(CalendarTemplateVariable::Footer),
|
||||
"event_title" => Ok(CalendarTemplateVariable::EventTitle),
|
||||
"event_description" => Ok(CalendarTemplateVariable::EventDescription),
|
||||
"event_details" => Ok(CalendarTemplateVariable::EventDetails),
|
||||
"action_url" => Ok(CalendarTemplateVariable::ActionUrl),
|
||||
"action_name" => Ok(CalendarTemplateVariable::ActionName),
|
||||
"attendees" => Ok(CalendarTemplateVariable::Attendees),
|
||||
"attendees_title" => Ok(CalendarTemplateVariable::AttendeesTitle),
|
||||
"key" => Ok(CalendarTemplateVariable::Key),
|
||||
"value" => Ok(CalendarTemplateVariable::Value),
|
||||
"link" => Ok(CalendarTemplateVariable::Link),
|
||||
"logo_cid" => Ok(CalendarTemplateVariable::LogoCid),
|
||||
"actions" => Ok(CalendarTemplateVariable::Actions),
|
||||
"changed" => Ok(CalendarTemplateVariable::Changed),
|
||||
"old_value" => Ok(CalendarTemplateVariable::OldValue),
|
||||
"rsvp" => Ok(CalendarTemplateVariable::Rsvp),
|
||||
"color" => Ok(CalendarTemplateVariable::Color),
|
||||
_ => Err(format!("Unknown calendar template variable: {}", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::CalendarTemplateVariable;
|
||||
use utils::template::Template;
|
||||
|
||||
const TEMPLATES: [(&str, &str, &str); 2] = [
|
||||
(
|
||||
"calendar-invite.html",
|
||||
include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-invite.html"
|
||||
)),
|
||||
include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-invite.html.min"
|
||||
)),
|
||||
),
|
||||
(
|
||||
"calendar-alarm.html",
|
||||
include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-alarm.html"
|
||||
)),
|
||||
include_str!(concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../resources/html-templates/calendar-alarm.html.min"
|
||||
)),
|
||||
),
|
||||
];
|
||||
|
||||
// Every `{{...}}` token in a template, in order of appearance
|
||||
fn tokens(contents: &str) -> Vec<&str> {
|
||||
let mut tokens = Vec::new();
|
||||
let mut rest = contents;
|
||||
|
||||
while let Some((_, after)) = rest.split_once("{{") {
|
||||
match after.split_once("}}") {
|
||||
Some((token, tail)) => {
|
||||
tokens.push(token.trim());
|
||||
rest = tail;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
|
||||
tokens
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shipped_calendar_templates_parse() {
|
||||
for (name, source, minified) in TEMPLATES {
|
||||
Template::<CalendarTemplateVariable>::parse(source)
|
||||
.unwrap_or_else(|err| panic!("{name} failed to parse: {err}"));
|
||||
Template::<CalendarTemplateVariable>::parse(minified)
|
||||
.unwrap_or_else(|err| panic!("{name}.min failed to parse: {err}"));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minified_calendar_templates_are_in_sync() {
|
||||
for (name, source, minified) in TEMPLATES {
|
||||
let source = tokens(source);
|
||||
assert!(source.len() > 10, "{name} yielded no tokens to compare");
|
||||
assert_eq!(
|
||||
source,
|
||||
tokens(minified),
|
||||
"{name}.min is stale, re-run resources/scripts/minify_html.sh"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn calendar_template_tokens_are_single_line() {
|
||||
// A newline inside `{{...}}` makes the parser reject the block
|
||||
for (name, source, minified) in TEMPLATES {
|
||||
for (suffix, contents) in [("", source), (".min", minified)] {
|
||||
for token in tokens(contents) {
|
||||
assert!(
|
||||
!token.contains('\n') && !token.contains('\r'),
|
||||
"{name}{suffix} has a multi-line token: {token:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user