Fix Calendar: Uppercase MAILTO calendar addresses become invalid SMTP recipients
This commit is contained in:
@@ -13,6 +13,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If
|
||||
## Fixed
|
||||
- IMAP: Mailbox object-quota only enforced in JMAP.
|
||||
- JMAP: Read-only sharee cannot set `isSubscribed` on a shared mailbox.
|
||||
- Calendar: Uppercase `MAILTO` calendar addresses become invalid SMTP recipients.
|
||||
|
||||
## [0.16.13] - 2026-07-12
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ use dav_proto::{
|
||||
response::{CalCondition, Href, ScheduleResponse, ScheduleResponseItem},
|
||||
},
|
||||
};
|
||||
use groupware::{DestroyArchive, cache::GroupwareCache, calendar::CalendarEventNotification};
|
||||
use groupware::{
|
||||
DestroyArchive, cache::GroupwareCache, calendar::CalendarEventNotification, strip_mailto_scheme,
|
||||
};
|
||||
use http_proto::HttpResponse;
|
||||
use hyper::StatusCode;
|
||||
use store::{
|
||||
@@ -325,9 +327,7 @@ impl CalendarEventNotificationHandler for Server {
|
||||
ICalendarValue::Text(value) | ICalendarValue::Uri(Uri::Location(value)),
|
||||
),
|
||||
) => {
|
||||
if let Some(email) =
|
||||
sanitize_email(value.strip_prefix("mailto:").unwrap_or(value.as_str()))
|
||||
{
|
||||
if let Some(email) = sanitize_email(strip_mailto_scheme(value.as_str())) {
|
||||
attendees.insert(email, entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ use super::{
|
||||
ArchivedCalendar, ArchivedCalendarEvent, ArchivedCalendarPreferences, ArchivedDefaultAlert,
|
||||
ArchivedTimezone, Calendar, CalendarEvent, CalendarPreferences, DefaultAlert, Timezone,
|
||||
};
|
||||
use crate::calendar::{
|
||||
ArchivedCalendarEventNotification, ArchivedChangedBy, ArchivedEventPreferences,
|
||||
CalendarEventNotification, ChangedBy, EventPreferences,
|
||||
use crate::{
|
||||
calendar::{
|
||||
ArchivedCalendarEventNotification, ArchivedChangedBy, ArchivedEventPreferences,
|
||||
CalendarEventNotification, ChangedBy, EventPreferences,
|
||||
},
|
||||
strip_mailto_scheme,
|
||||
};
|
||||
use ahash::AHashSet;
|
||||
use calcard::icalendar::{
|
||||
@@ -463,7 +466,7 @@ impl ArchivedCalendarEvent {
|
||||
_ => None,
|
||||
}))
|
||||
{
|
||||
let value = value.strip_prefix("mailto:").unwrap_or(value).trim();
|
||||
let value = strip_mailto_scheme(value);
|
||||
let lang = if is_lang {
|
||||
detector.detect(value, MIN_LANGUAGE_SCORE);
|
||||
Language::Unknown
|
||||
|
||||
@@ -149,3 +149,10 @@ impl DavCalendarResource for DavResources {
|
||||
.map(|p| p.tz)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn strip_mailto_scheme(value: &str) -> &str {
|
||||
value
|
||||
.split_once(':')
|
||||
.filter(|(scheme, _)| scheme.eq_ignore_ascii_case("mailto"))
|
||||
.map_or(value, |(_, address)| address.trim())
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use crate::strip_mailto_scheme;
|
||||
use ahash::{AHashMap, AHashSet};
|
||||
use calcard::{
|
||||
common::{IanaString, PartialDateTime},
|
||||
@@ -238,7 +239,7 @@ impl Attendee<'_> {
|
||||
impl Email {
|
||||
pub fn new(email: &str, local_addresses: &[String]) -> Option<Self> {
|
||||
email.contains('@').then(|| {
|
||||
let email = email.trim().trim_start_matches("mailto:").to_lowercase();
|
||||
let email = strip_mailto_scheme(email.trim()).to_lowercase();
|
||||
let is_local = local_addresses.contains(&email);
|
||||
Email { email, is_local }
|
||||
})
|
||||
|
||||
@@ -6,7 +6,10 @@
|
||||
|
||||
use crate::participant_identity::get::ParticipantIdentityGet;
|
||||
use common::Server;
|
||||
use groupware::calendar::{ParticipantIdentities, ParticipantIdentity};
|
||||
use groupware::{
|
||||
calendar::{ParticipantIdentities, ParticipantIdentity},
|
||||
strip_mailto_scheme,
|
||||
};
|
||||
use jmap_proto::{
|
||||
error::set::{SetError, SetErrorType},
|
||||
method::set::{SetRequest, SetResponse},
|
||||
@@ -226,11 +229,7 @@ fn validate_identity_value(
|
||||
}
|
||||
(ParticipantIdentityProperty::CalendarAddress, Value::Str(value)) => {
|
||||
if identity.calendar_address != value {
|
||||
let email = if let Some(email) = value.strip_prefix("mailto:") {
|
||||
sanitize_email(email)
|
||||
} else {
|
||||
sanitize_email(&value)
|
||||
};
|
||||
let email = sanitize_email(strip_mailto_scheme(&value));
|
||||
|
||||
if let Some(email) = email {
|
||||
if allowed_emails.iter().any(|e| e == &email) {
|
||||
|
||||
@@ -21,6 +21,7 @@ use common::{
|
||||
use groupware::{
|
||||
cache::GroupwareCache,
|
||||
calendar::{CALENDAR_SUBSCRIBED, CalendarEvent},
|
||||
strip_mailto_scheme,
|
||||
};
|
||||
use jmap_proto::{
|
||||
method::availability::{
|
||||
@@ -258,9 +259,7 @@ impl PrincipalGetAvailability for Server {
|
||||
if include_in_availability == IncludeInAvailability::Attending =>
|
||||
{
|
||||
if let Some(attendee) = value.as_text().and_then(|attendee| {
|
||||
sanitize_email(
|
||||
attendee.strip_prefix("mailto:").unwrap_or(attendee),
|
||||
)
|
||||
sanitize_email(strip_mailto_scheme(attendee))
|
||||
}) {
|
||||
// Condition: the Principal is a participant of the event, and has a "participationStatus" of "accepted" or "tentative".
|
||||
if principal_account.addresses().contains(&attendee) {
|
||||
|
||||
@@ -17,7 +17,10 @@ use common::{
|
||||
ipc::{CalendarAlert, PushNotification},
|
||||
network::{ServerInstance, stream::NullIo},
|
||||
};
|
||||
use groupware::calendar::{ArchivedCalendarEvent, CalendarEvent};
|
||||
use groupware::{
|
||||
calendar::{ArchivedCalendarEvent, CalendarEvent},
|
||||
strip_mailto_scheme,
|
||||
};
|
||||
use mail_builder::{
|
||||
MessageBuilder,
|
||||
headers::{HeaderType, content_type::ContentType},
|
||||
@@ -478,7 +481,7 @@ async fn build_template(
|
||||
.values
|
||||
.first()
|
||||
.and_then(|v| v.as_text())
|
||||
.map(|v| v.strip_prefix("mailto:").unwrap_or(v))
|
||||
.map(strip_mailto_scheme)
|
||||
.and_then(sanitize_email);
|
||||
}
|
||||
_ => {}
|
||||
@@ -501,7 +504,7 @@ async fn build_template(
|
||||
.values
|
||||
.first()
|
||||
.and_then(|v| v.as_text())
|
||||
.map(|v| v.strip_prefix("mailto:").unwrap_or(v));
|
||||
.map(strip_mailto_scheme);
|
||||
let name = entry.params.iter().find_map(|param| {
|
||||
if let ArchivedICalendarParameterName::Cn = param.name {
|
||||
param.value.as_text()
|
||||
|
||||
Reference in New Issue
Block a user