Clippy fixes

This commit is contained in:
mdecimus
2025-08-18 11:44:58 +01:00
parent d6531be012
commit e10aa551eb
102 changed files with 1336 additions and 1411 deletions
+11 -12
View File
@@ -473,20 +473,19 @@ async fn import_mailboxes(
for (path, mailbox) in build_mailbox_tree(&mailboxes) {
let id = mailbox.id().unwrap_result("obtain mailbox id");
// Find existing mailbox based on role
if !matches!(mailbox.role(), Role::None) {
if let Some(existing_mailbox) = existing_mailboxes
if !matches!(mailbox.role(), Role::None)
&& let Some(existing_mailbox) = existing_mailboxes
.iter()
.find(|m| m.role() == mailbox.role())
{
id_mappings.insert(
id.to_string(),
existing_mailbox
.id()
.unwrap_result("obtain mailbox id")
.to_string(),
);
continue;
}
{
id_mappings.insert(
id.to_string(),
existing_mailbox
.id()
.unwrap_result("obtain mailbox id")
.to_string(),
);
continue;
}
// Find existing mailbox by name
+5 -5
View File
@@ -168,7 +168,7 @@ impl AddressMapping {
struct Address<'x>(&'x str);
impl ResolveVariable for Address<'_> {
fn resolve_variable(&self, _: u32) -> crate::expr::Variable {
fn resolve_variable(&'_ self, _: u32) -> crate::expr::Variable<'_> {
Variable::from(self.0)
}
@@ -186,10 +186,10 @@ impl AddressMapping {
) -> Cow<'x, str> {
match self {
AddressMapping::Enable => {
if let Some((local_part, domain_part)) = address.rsplit_once('@') {
if let Some((local_part, _)) = local_part.split_once('+') {
return format!("{}@{}", local_part, domain_part).into();
}
if let Some((local_part, domain_part)) = address.rsplit_once('@')
&& let Some((local_part, _)) = local_part.split_once('+')
{
return format!("{}@{}", local_part, domain_part).into();
}
}
AddressMapping::Custom(if_block) => {
+26 -27
View File
@@ -69,32 +69,32 @@ impl Server {
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
if self.is_enterprise_edition() {
if let Some(tenant_id) = principal.tenant {
// Limit tenant permissions
if self.is_enterprise_edition()
&& let Some(tenant_id) = principal.tenant
{
// Limit tenant permissions
use directory::QueryParams;
permissions.intersection(&self.get_role_permissions(tenant_id).await?.enabled);
use directory::QueryParams;
permissions.intersection(&self.get_role_permissions(tenant_id).await?.enabled);
// Obtain tenant quota
tenant = Some(TenantInfo {
id: tenant_id,
quota: self
.store()
.query(QueryParams::id(tenant_id).with_return_member_of(false))
.await
.caused_by(trc::location!())?
.ok_or_else(|| {
trc::SecurityEvent::Unauthorized
.into_err()
.details("Tenant not found")
.id(tenant_id)
.caused_by(trc::location!())
})?
.quota
.unwrap_or_default(),
});
}
// Obtain tenant quota
tenant = Some(TenantInfo {
id: tenant_id,
quota: self
.store()
.query(QueryParams::id(tenant_id).with_return_member_of(false))
.await
.caused_by(trc::location!())?
.ok_or_else(|| {
trc::SecurityEvent::Unauthorized
.into_err()
.details("Tenant not found")
.id(tenant_id)
.caused_by(trc::location!())
})?
.quota
.unwrap_or_default(),
});
}
// SPDX-SnippetEnd
@@ -112,10 +112,9 @@ impl Server {
.query(QueryParams::id(group_id).with_return_member_of(false))
.await
.caused_by(trc::location!())?
&& group.typ == Type::Group
{
if group.typ == Type::Group {
emails.extend(group.emails);
}
emails.extend(group.emails);
}
}
+11 -13
View File
@@ -167,26 +167,24 @@ impl Server {
}
_ => {
// Validate API credentials
if req.allow_api_access {
if let Ok(Some(principal)) = self
if req.allow_api_access
&& let Ok(Some(principal)) = self
.store()
.query(
QueryParams::credentials(&req.credentials)
.with_return_member_of(req.return_member_of),
)
.await
{
if principal.typ == Type::ApiKey {
trc::event!(
Auth(trc::AuthEvent::Success),
AccountName = principal.name().to_string(),
AccountId = principal.id(),
SpanId = req.session_id,
);
&& principal.typ == Type::ApiKey
{
trc::event!(
Auth(trc::AuthEvent::Success),
AccountName = principal.name().to_string(),
AccountId = principal.id(),
SpanId = req.session_id,
);
return Ok(principal);
}
}
return Ok(principal);
}
}
}
+17 -18
View File
@@ -57,24 +57,23 @@ impl Server {
}
pub async fn is_http_anonymous_request_allowed(&self, addr: &IpAddr) -> trc::Result<()> {
if let Some(rate) = &self.core.jmap.rate_anonymous {
if !self.is_ip_allowed(addr)
&& self
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_HTTP_ANONYMOUS,
&ip_to_bytes(addr),
rate,
false,
)
.await
.caused_by(trc::location!())?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
if let Some(rate) = &self.core.jmap.rate_anonymous
&& !self.is_ip_allowed(addr)
&& self
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_HTTP_ANONYMOUS,
&ip_to_bytes(addr),
rate,
false,
)
.await
.caused_by(trc::location!())?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
Ok(())
}
+4 -5
View File
@@ -331,12 +331,11 @@ impl SpamFilterHeaderConfig {
if config
.property_or_default(("spam-filter.header", typ, "enable"), "true")
.unwrap_or(true)
&& let Some(value) = config.value(("spam-filter.header", typ, "name"))
{
if let Some(value) = config.value(("spam-filter.header", typ, "name")) {
let value = value.trim();
if !value.is_empty() {
*var = value.to_string().into();
}
let value = value.trim();
if !value.is_empty() {
*var = value.to_string().into();
}
}
}
+25 -30
View File
@@ -171,12 +171,10 @@ impl Tracers {
{
if let Some(event_type) =
config.try_parse_value::<EventType>(("tracing.level", &event_name), &event_name)
{
if let Some(level) =
&& let Some(level) =
config.property_require::<Level>(("tracing.level", &event_name))
{
custom_levels.insert(event_type, level);
}
{
custom_levels.insert(event_type, level);
}
}
@@ -531,28 +529,27 @@ impl Tracers {
if config
.property_or_default("tracing.history.enable", "false")
.unwrap_or(false)
&& let Some(store_id) = config.value_require("tracing.history.store")
{
if let Some(store_id) = config.value_require("tracing.history.store") {
if let Some(store) = stores.stores.get(store_id) {
let mut tracer = TelemetrySubscriber {
id: "history".to_string(),
interests: Default::default(),
lossy: false,
typ: TelemetrySubscriberType::StoreTracer(StoreTracer {
store: store.clone(),
}),
};
if let Some(store) = stores.stores.get(store_id) {
let mut tracer = TelemetrySubscriber {
id: "history".to_string(),
interests: Default::default(),
lossy: false,
typ: TelemetrySubscriberType::StoreTracer(StoreTracer {
store: store.clone(),
}),
};
for event_type in StoreTracer::default_events() {
tracer.interests.set(event_type);
global_interests.set(event_type);
}
tracers.push(tracer);
} else {
let err = format!("Store {store_id} not found");
config.new_build_error("tracing.history.store", err);
for event_type in StoreTracer::default_events() {
tracer.interests.set(event_type);
global_interests.set(event_type);
}
tracers.push(tracer);
} else {
let err = format!("Store {store_id} not found");
config.new_build_error("tracing.history.store", err);
}
}
}
@@ -617,14 +614,12 @@ impl Metrics {
.value(("tracer", tracer_id, "type"))
.unwrap_or_default()
== "log"
{
if let Some(path) = config
&& let Some(path) = config
.value(("tracer", tracer_id, "path"))
.map(|s| s.to_string())
{
metrics.log_path = Some(path);
break;
}
{
metrics.log_path = Some(path);
break;
}
}
+34 -34
View File
@@ -412,16 +412,16 @@ impl Server {
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant) = quotas.tenant.filter(|tenant| tenant.quota != 0) {
let used_quota = self.get_used_quota(tenant.id).await? as u64;
if self.core.is_enterprise_edition()
&& let Some(tenant) = quotas.tenant.filter(|tenant| tenant.quota != 0)
{
let used_quota = self.get_used_quota(tenant.id).await? as u64;
if used_quota + item_size > tenant.quota {
return Err(trc::LimitEvent::TenantQuota
.into_err()
.ctx(trc::Key::Limit, tenant.quota)
.ctx(trc::Key::Size, used_quota));
}
if used_quota + item_size > tenant.quota {
return Err(trc::LimitEvent::TenantQuota
.into_err()
.ctx(trc::Key::Limit, tenant.quota)
.ctx(trc::Key::Size, used_quota));
}
}
@@ -462,24 +462,24 @@ impl Server {
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant_id) = principal.tenant() {
quotas.tenant = TenantInfo {
id: tenant_id,
quota: self
.core
.storage
.directory
.query(QueryParams::id(tenant_id).with_return_member_of(false))
.await
.add_context(|err| {
err.caused_by(trc::location!()).account_id(tenant_id)
})?
.map(|tenant| tenant.quota())
.unwrap_or_default(),
}
.into();
if self.core.is_enterprise_edition()
&& let Some(tenant_id) = principal.tenant()
{
quotas.tenant = TenantInfo {
id: tenant_id,
quota: self
.core
.storage
.directory
.query(QueryParams::id(tenant_id).with_return_member_of(false))
.await
.add_context(|err| {
err.caused_by(trc::location!()).account_id(tenant_id)
})?
.map(|tenant| tenant.quota())
.unwrap_or_default(),
}
.into();
}
// SPDX-SnippetEnd
@@ -804,14 +804,14 @@ impl Server {
}
pub async fn cluster_broadcast(&self, event: BroadcastEvent) {
if let Some(broadcast_tx) = &self.inner.ipc.broadcast_tx.clone() {
if broadcast_tx.send(event).await.is_err() {
trc::event!(
Server(trc::ServerEvent::ThreadError),
Details = "Error sending broadcast event.",
CausedBy = trc::location!()
);
}
if let Some(broadcast_tx) = &self.inner.ipc.broadcast_tx.clone()
&& broadcast_tx.send(event).await.is_err()
{
trc::event!(
Server(trc::ServerEvent::ThreadError),
Details = "Error sending broadcast event.",
CausedBy = trc::location!()
);
}
}
+2 -2
View File
@@ -498,7 +498,7 @@ impl<'x> Variable<'x> {
}
}
pub fn to_string(&self) -> StringCow {
pub fn to_string(&'_ self) -> StringCow<'_> {
match self {
Variable::String(s) => StringCow::Borrowed(s.as_str()),
Variable::Integer(n) => StringCow::Owned(n.to_compact_string()),
@@ -577,7 +577,7 @@ impl<'x> Variable<'x> {
}
}
pub fn as_array(&self) -> Option<&[Variable]> {
pub fn as_array(&'_ self) -> Option<&'_ [Variable<'_>]> {
match self {
Variable::Array(l) => Some(l),
_ => None,
+16 -16
View File
@@ -279,23 +279,23 @@ impl SocketOpts {
Details = "Failed to set TCP_NODELAY",
);
}
if let Some(ttl) = self.ttl {
if let Err(err) = stream.set_ttl(ttl) {
trc::event!(
Network(trc::NetworkEvent::SetOptError),
Reason = err.to_string(),
Details = "Failed to set TTL",
);
}
if let Some(ttl) = self.ttl
&& let Err(err) = stream.set_ttl(ttl)
{
trc::event!(
Network(trc::NetworkEvent::SetOptError),
Reason = err.to_string(),
Details = "Failed to set TTL",
);
}
if self.linger.is_some() {
if let Err(err) = stream.set_linger(self.linger) {
trc::event!(
Network(trc::NetworkEvent::SetOptError),
Reason = err.to_string(),
Details = "Failed to set LINGER",
);
}
if self.linger.is_some()
&& let Err(err) = stream.set_linger(self.linger)
{
trc::event!(
Network(trc::NetworkEvent::SetOptError),
Reason = err.to_string(),
Details = "Failed to set LINGER",
);
}
}
}
+6 -7
View File
@@ -462,14 +462,13 @@ impl BootManager {
if config
.property_or_default::<bool>("spam-filter.auto-update", "false")
.unwrap_or_default()
&& let Err(err) = core.storage.config.update_spam_rules(false, false).await
{
if let Err(err) = core.storage.config.update_spam_rules(false, false).await {
trc::event!(
Resource(trc::ResourceEvent::Error),
Details = "Failed to update spam-filter",
CausedBy = err
);
}
trc::event!(
Resource(trc::ResourceEvent::Error),
Details = "Failed to update spam-filter",
CausedBy = err
);
}
// Build shared inner
+16 -16
View File
@@ -45,17 +45,17 @@ impl DavResources {
let check_acls = check_acls.into();
for resource in &self.resources {
if resource.document_id == document_id {
if let Some(acls) = resource.acls() {
for acl in acls {
if access_token.is_member(acl.account_id) {
let mut grants = acl.grants;
grants.intersection(&check_acls);
return !grants.is_empty();
}
if resource.document_id == document_id
&& let Some(acls) = resource.acls()
{
for acl in acls {
if access_token.is_member(acl.account_id) {
let mut grants = acl.grants;
grants.intersection(&check_acls);
return !grants.is_empty();
}
break;
}
break;
}
}
@@ -66,15 +66,15 @@ impl DavResources {
let mut account_acls = Bitmap::<Acl>::new();
for resource in &self.resources {
if resource.document_id == document_id {
if let Some(acls) = resource.acls() {
for acl in acls {
if access_token.is_member(acl.account_id) {
account_acls.union(&acl.grants);
}
if resource.document_id == document_id
&& let Some(acls) = resource.acls()
{
for acl in acls {
if access_token.is_member(acl.account_id) {
account_acls.union(&acl.grants);
}
break;
}
break;
}
}
+23 -25
View File
@@ -57,23 +57,22 @@ pub(crate) fn spawn_otel_tracer(builder: SubscriberBuilder, mut otel: OtelTracer
pending_logs.push(otel.build_log_record(&event));
}
if otel.span_exporter_enable {
if let Some(span) = event.inner.span.as_ref() {
let span_id = span.span_id().unwrap();
if !event.inner.typ.is_span_end() {
let events =
active_spans.entry(span_id).or_insert_with(Vec::new);
if events.len() < MAX_EVENTS {
events.push(event);
}
} else if let Some(events) = active_spans.remove(&span_id) {
pending_spans.push(build_span_data(
span,
&event,
events.iter().chain(std::iter::once(&event)),
&instrumentation,
));
if otel.span_exporter_enable
&& let Some(span) = event.inner.span.as_ref()
{
let span_id = span.span_id().unwrap();
if !event.inner.typ.is_span_end() {
let events = active_spans.entry(span_id).or_insert_with(Vec::new);
if events.len() < MAX_EVENTS {
events.push(event);
}
} else if let Some(events) = active_spans.remove(&span_id) {
pending_spans.push(build_span_data(
span,
&event,
events.iter().chain(std::iter::once(&event)),
&instrumentation,
));
}
}
}
@@ -91,18 +90,17 @@ pub(crate) fn spawn_otel_tracer(builder: SubscriberBuilder, mut otel: OtelTracer
if !pending_spans.is_empty() || !pending_logs.is_empty() {
next_delivery = now + otel.throttle;
if !pending_spans.is_empty() {
if let Err(err) = otel
if !pending_spans.is_empty()
&& let Err(err) = otel
.span_exporter
.export(std::mem::take(&mut pending_spans))
.await
{
trc::event!(
Telemetry(TelemetryEvent::OtelExporterError),
Details = "Failed to export spans",
Reason = err.to_string()
);
}
{
trc::event!(
Telemetry(TelemetryEvent::OtelExporterError),
Details = "Failed to export spans",
Reason = err.to_string()
);
}
if !pending_logs.is_empty() {
+1 -1
View File
@@ -29,7 +29,7 @@ impl<'x> Tokenizer<'x> {
}
}
pub fn token(&mut self) -> super::Result<Token> {
pub fn token(&'_ mut self) -> super::Result<Token<'_>> {
loop {
if self.last_is_end {
self.last_is_end = false;
+25 -27
View File
@@ -275,11 +275,11 @@ impl CalendarQueryHandler {
let mut matched_any = false;
for value in entry.values.iter() {
if let Some(text) = value.as_text() {
if text_match.matches(text) {
matched_any = true;
break;
}
if let Some(text) = value.as_text()
&& text_match.matches(text)
{
matched_any = true;
break;
}
}
@@ -443,31 +443,29 @@ impl CalendarQueryHandler {
.unwrap();
// Limit recurrence override
if let Some(limit_recurrence) = &data.limit_recurrence {
if component.is_recurrence_override()
&& !self.expanded_times.iter().any(|event| {
event.comp_id == component_id
&& limit_recurrence.is_in_range(
component.component_type == ICalendarComponentType::VTodo,
event.start,
event.end,
)
})
{
continue;
}
if let Some(limit_recurrence) = &data.limit_recurrence
&& component.is_recurrence_override()
&& !self.expanded_times.iter().any(|event| {
event.comp_id == component_id
&& limit_recurrence.is_in_range(
component.component_type == ICalendarComponentType::VTodo,
event.start,
event.end,
)
})
{
continue;
}
// Limit freebusy
if let Some(limit_recurrence) = &data.limit_freebusy {
if component.component_type == ICalendarComponentType::VFreebusy
&& !self.expanded_times.iter().any(|event| {
event.comp_id == component_id
&& limit_recurrence.is_in_range(false, event.start, event.end)
})
{
continue;
}
if let Some(limit_recurrence) = &data.limit_freebusy
&& component.component_type == ICalendarComponentType::VFreebusy
&& !self.expanded_times.iter().any(|event| {
event.comp_id == component_id
&& limit_recurrence.is_in_range(false, event.start, event.end)
})
{
continue;
}
// Filter entries
+5 -5
View File
@@ -124,11 +124,11 @@ pub(crate) fn vcard_query(card: &ArchivedVCard, filters: &AddressbookFilter) ->
let mut matched_any = false;
for value in entry.values.iter() {
if let Some(text) = value.as_text() {
if text_match.matches(text) {
matched_any = true;
break;
}
if let Some(text) = value.as_text()
&& text_match.matches(text)
{
matched_any = true;
break;
}
}
+13 -14
View File
@@ -526,8 +526,7 @@ impl LockRequestHandler for Server {
if let Some(document_id) =
resource_state.document_id.filter(|&id| id != u32::MAX)
{
if let Some(archive) = self
&& let Some(archive) = self
.get_archive(
resource_state.account_id,
resource_state.collection,
@@ -535,22 +534,22 @@ impl LockRequestHandler for Server {
)
.await
.caused_by(trc::location!())?
{
resource_state.etag = archive.etag().into();
}
{
resource_state.etag = archive.etag().into();
}
}
// Fetch lock token
if needs_lock_token && resource_state.lock_tokens.is_empty() {
if let Some(idx) = locks.find_cache_pos(self, resource_state).await? {
let found_locks = locks
.find_locks_by_pos(idx, resource_state, false)?
.iter()
.map(|(_, lock)| lock.urn().to_string())
.collect::<Vec<_>>();
resource_state.lock_tokens = found_locks;
}
if needs_lock_token
&& resource_state.lock_tokens.is_empty()
&& let Some(idx) = locks.find_cache_pos(self, resource_state).await?
{
let found_locks = locks
.find_locks_by_pos(idx, resource_state, false)?
.iter()
.map(|(_, lock)| lock.urn().to_string())
.collect::<Vec<_>>();
resource_state.lock_tokens = found_locks;
}
// Fetch sync token
+13 -14
View File
@@ -1090,12 +1090,11 @@ impl PropFindRequestHandler for Server {
}
// Add dead properties
if skip_not_found {
if let Some(dead_properties) =
if skip_not_found
&& let Some(dead_properties) =
dead_properties.filter(|dead_properties| !dead_properties.0.is_empty())
{
dead_properties.to_dav_values(&mut fields);
}
{
dead_properties.to_dav_values(&mut fields);
}
// Add response
@@ -1297,15 +1296,15 @@ async fn get(
}
}
if maybe_has_vanished {
if let Some(vanished_collection) = sync_collection.vanished_collection() {
vanished = server
.store()
.vanished(account_id, vanished_collection, Query::Since(id))
.await
.caused_by(trc::location!())?;
total_changes += vanished.len();
}
if maybe_has_vanished
&& let Some(vanished_collection) = sync_collection.vanished_collection()
{
vanished = server
.store()
.vanished(account_id, vanished_collection, Query::Since(id))
.await
.caused_by(trc::location!())?;
total_changes += vanished.len();
}
// Truncate changes
+9 -10
View File
@@ -146,16 +146,15 @@ impl FileCopyMoveRequestHandler for Server {
// Validate destination ACLs
if let Some(document_id) = destination.document_id {
if let Some(delete_destination) = &delete_destination {
if !access_token.is_member(to_account_id)
&& !from_resources.has_access_to_container(
access_token,
delete_destination.document_id.unwrap(),
Acl::Delete,
)
{
return Err(DavError::Code(StatusCode::FORBIDDEN));
}
if let Some(delete_destination) = &delete_destination
&& !access_token.is_member(to_account_id)
&& !from_resources.has_access_to_container(
access_token,
delete_destination.document_id.unwrap(),
Acl::Delete,
)
{
return Err(DavError::Code(StatusCode::FORBIDDEN));
}
if !access_token.is_member(to_account_id)
+1 -1
View File
@@ -235,7 +235,7 @@ impl PropStatBuilder {
}
// Workaround for Apple bug with missing percent encoding in paths
pub(crate) fn fix_percent_encoding(path: &str) -> Cow<str> {
pub(crate) fn fix_percent_encoding(path: &'_ str) -> Cow<'_, str> {
let (parent, name) = if let Some((parent, name)) = path.rsplit_once('/') {
(Some(parent), name)
} else {
+32 -32
View File
@@ -45,39 +45,39 @@ impl DirectoryStore for Store {
},
};
if let Some(account_id) = account_id {
if let Some(mut principal) = self.get_principal(account_id).await? {
if let Some(secret) = secret {
if !principal.verify_secret(secret, by.only_app_pass).await? {
return Ok(None);
}
}
if by.return_member_of {
let mut roles = vec![];
let mut lists = vec![];
let mut member_of = vec![];
for member in self.get_member_of(principal.id).await? {
match member.typ {
Type::List => lists.push(member.principal_id),
Type::Role => roles.push(member.principal_id),
_ => member_of.push(member.principal_id),
}
}
if !roles.is_empty() {
principal.data.push(PrincipalData::Roles(roles));
}
if !lists.is_empty() {
principal.data.push(PrincipalData::Lists(lists));
}
if !member_of.is_empty() {
principal.data.push(PrincipalData::MemberOf(member_of));
}
}
return Ok(Some(principal));
if let Some(account_id) = account_id
&& let Some(mut principal) = self.get_principal(account_id).await?
{
if let Some(secret) = secret
&& !principal.verify_secret(secret, by.only_app_pass).await?
{
return Ok(None);
}
if by.return_member_of {
let mut roles = vec![];
let mut lists = vec![];
let mut member_of = vec![];
for member in self.get_member_of(principal.id).await? {
match member.typ {
Type::List => lists.push(member.principal_id),
Type::Role => roles.push(member.principal_id),
_ => member_of.push(member.principal_id),
}
}
if !roles.is_empty() {
principal.data.push(PrincipalData::Roles(roles));
}
if !lists.is_empty() {
principal.data.push(PrincipalData::Lists(lists));
}
if !member_of.is_empty() {
principal.data.push(PrincipalData::MemberOf(member_of));
}
}
return Ok(Some(principal));
}
Ok(None)
}
+38 -43
View File
@@ -354,16 +354,15 @@ impl ManageDirectory for Store {
principal_create.tenant = tenant_id.into();
if !matches!(principal_create.typ, Type::Tenant | Type::Domain) {
if let Some(domain) = name.split('@').nth(1) {
if self
if let Some(domain) = name.split('@').nth(1)
&& self
.get_principal_info(domain)
.await
.caused_by(trc::location!())?
.filter(|v| v.typ == Type::Domain && v.has_tenant_access(tenant_id.into()))
.is_some()
{
valid_domains.insert(domain.into());
}
{
valid_domains.insert(domain.into());
}
if valid_domains.is_empty() {
@@ -524,14 +523,14 @@ impl ManageDirectory for Store {
if self.rcpt(&email).await.caused_by(trc::location!())? != RcptType::Invalid {
return Err(err_exists(PrincipalField::Emails, email.to_string()));
}
if let Some(domain) = email.split('@').nth(1) {
if valid_domains.insert(domain.into()) {
self.get_principal_info(domain)
.await
.caused_by(trc::location!())?
.filter(|v| v.typ == Type::Domain && v.has_tenant_access(tenant_id))
.ok_or_else(|| not_found(domain.to_string()))?;
}
if let Some(domain) = email.split('@').nth(1)
&& valid_domains.insert(domain.into())
{
self.get_principal_info(domain)
.await
.caused_by(trc::location!())?
.filter(|v| v.typ == Type::Domain && v.has_tenant_access(tenant_id))
.ok_or_else(|| not_found(domain.to_string()))?;
}
principal_create.emails.push(email);
}
@@ -1004,8 +1003,8 @@ impl ManageDirectory for Store {
if tenant_id.is_some()
&& !matches!(principal_type, Type::Tenant | Type::Domain)
{
if let Some(domain) = new_name.split('@').nth(1) {
if self
if let Some(domain) = new_name.split('@').nth(1)
&& self
.get_principal_info(domain)
.await
.caused_by(trc::location!())?
@@ -1013,9 +1012,8 @@ impl ManageDirectory for Store {
v.typ == Type::Domain && v.has_tenant_access(tenant_id)
})
.is_some()
{
valid_domains.insert(domain.to_string());
}
{
valid_domains.insert(domain.to_string());
}
if valid_domains.is_empty() {
@@ -1574,19 +1572,18 @@ impl ManageDirectory for Store {
for member_id in &members {
if !new_members.contains(member_id) {
// Update changed principal ids
if principal_type != Type::List {
if let Some(member_info) = self
if principal_type != Type::List
&& let Some(member_info) = self
.get_principal(*member_id)
.await
.caused_by(trc::location!())?
{
changed_principals.add_member_change(
*member_id,
member_info.typ,
principal_id,
principal_type,
);
}
{
changed_principals.add_member_change(
*member_id,
member_info.typ,
principal_id,
principal_type,
);
}
batch.clear(ValueClass::Directory(DirectoryClass::MemberOf {
@@ -2331,16 +2328,14 @@ impl ManageDirectory for Store {
// Map tenant name
#[cfg(feature = "enterprise")]
if let Some(tenant_id) = principal.tenant {
if fields.is_empty() || fields.contains(&PrincipalField::Tenant) {
if let Some(name) = self
.get_principal_name(tenant_id)
.await
.caused_by(trc::location!())?
{
result.set(PrincipalField::Tenant, name);
}
}
if let Some(tenant_id) = principal.tenant
&& (fields.is_empty() || fields.contains(&PrincipalField::Tenant))
&& let Some(name) = self
.get_principal_name(tenant_id)
.await
.caused_by(trc::location!())?
{
result.set(PrincipalField::Tenant, name);
}
// SPDX-SnippetEnd
@@ -2350,10 +2345,10 @@ impl ManageDirectory for Store {
(PrincipalField::Name, Some(principal.name)),
(PrincipalField::Description, principal.description),
] {
if let Some(value) = value {
if fields.is_empty() || fields.contains(&name) {
result.set(name, value);
}
if let Some(value) = value
&& (fields.is_empty() || fields.contains(&name))
{
result.set(name, value);
}
}
for (name, value) in [
@@ -2637,7 +2632,7 @@ impl ChangedPrincipals {
self.0.contains_key(&principal_id)
}
pub fn iter(&self) -> std::collections::hash_map::Iter<u32, ChangedPrincipal> {
pub fn iter(&'_ self) -> std::collections::hash_map::Iter<'_, u32, ChangedPrincipal> {
self.0.iter()
}
+14 -15
View File
@@ -230,13 +230,12 @@ impl LdapDirectory {
.map_err(|err| err.into_error().caused_by(trc::location!()))?;
for entry in rs {
'outer: for (attr, value) in SearchEntry::construct(entry).attrs {
if self.mappings.attr_name.contains(&attr) {
if let Some(group) = value.into_iter().next() {
if !group.is_empty() {
name = group;
break 'outer;
}
}
if self.mappings.attr_name.contains(&attr)
&& let Some(group) = value.into_iter().next()
&& !group.is_empty()
{
name = group;
break 'outer;
}
}
}
@@ -314,14 +313,14 @@ impl LdapDirectory {
for entry in rs {
let entry = SearchEntry::construct(entry);
for attr in &self.mappings.attr_name {
if let Some(name) = entry.attrs.get(attr).and_then(|v| v.first()) {
if !name.is_empty() {
return self
.data_store
.get_or_create_principal_id(name, Type::Individual)
.await
.map(Some);
}
if let Some(name) = entry.attrs.get(attr).and_then(|v| v.first())
&& !name.is_empty()
{
return self
.data_store
.get_or_create_principal_id(name, Type::Individual)
.await
.map(Some);
}
}
}
+4 -4
View File
@@ -294,10 +294,10 @@ impl SqlMappings {
if let Value::Text(text) = value {
principal.emails.push(text.to_lowercase());
}
} else if name.eq_ignore_ascii_case(&self.column_quota) {
if let Value::Integer(quota) = value {
principal.quota = (quota as u64).into();
}
} else if name.eq_ignore_ascii_case(&self.column_quota)
&& let Value::Integer(quota) = value
{
principal.quota = (quota as u64).into();
}
}
}
+8 -8
View File
@@ -40,10 +40,10 @@ impl Directory {
pub async fn is_local_domain(&self, domain: &str) -> trc::Result<bool> {
// Check cache
if let Some(cache) = &self.cache {
if let Some(result) = cache.get_domain(domain) {
return Ok(result);
}
if let Some(cache) = &self.cache
&& let Some(result) = cache.get_domain(domain)
{
return Ok(result);
}
let result = match &self.store {
@@ -67,10 +67,10 @@ impl Directory {
pub async fn rcpt(&self, email: &str) -> trc::Result<RcptType> {
// Check cache
if let Some(cache) = &self.cache {
if let Some(result) = cache.get_rcpt(email) {
return Ok(result);
}
if let Some(cache) = &self.cache
&& let Some(result) = cache.get_rcpt(email)
{
return Ok(result);
}
let result = match &self.store {
+1
View File
@@ -261,6 +261,7 @@ mod test {
#[test]
#[ignore]
#[allow(clippy::obfuscated_if_else)]
fn print_permissions() {
const CHECK: &str = ":white_check_mark:";
+9 -11
View File
@@ -238,13 +238,11 @@ impl Principal {
} else {
None
}
}) {
if let Some(idx) = permissions
.iter_mut()
.position(|p| p.permission == permission && p.grant == grant)
{
permissions.swap_remove(idx);
}
}) && let Some(idx) = permissions
.iter_mut()
.position(|p| p.permission == permission && p.grant == grant)
{
permissions.swap_remove(idx);
}
}
@@ -270,10 +268,10 @@ impl Principal {
}
// If the principal has no roles, take the ones from the external principal
if let Some(roles) = external.roles_mut().filter(|s| !s.is_empty()) {
if self.roles().is_empty() {
self.data.push(PrincipalData::Roles(std::mem::take(roles)));
}
if let Some(roles) = external.roles_mut().filter(|s| !s.is_empty())
&& self.roles().is_empty()
{
self.data.push(PrincipalData::Roles(std::mem::take(roles)));
}
if external.description.as_ref().is_some_and(|v| !v.is_empty())
+8 -9
View File
@@ -33,18 +33,17 @@ pub(crate) async fn update_email_cache(
};
for (document_id, is_update) in changed_ids {
if *is_update {
if let Some(archive) = server
if *is_update
&& let Some(archive) = server
.get_archive(account_id, Collection::Email, *document_id)
.await
.caused_by(trc::location!())?
{
insert_item(
&mut new_cache,
*document_id,
archive.to_unarchived::<MessageData>()?,
);
}
{
insert_item(
&mut new_cache,
*document_id,
archive.to_unarchived::<MessageData>()?,
);
}
}
+8 -9
View File
@@ -28,18 +28,17 @@ pub(crate) async fn update_mailbox_cache(
};
for (document_id, is_update) in changed_ids {
if *is_update {
if let Some(archive) = server
if *is_update
&& let Some(archive) = server
.get_archive(account_id, Collection::Mailbox, *document_id)
.await
.caused_by(trc::location!())?
{
insert_item(
&mut new_cache,
*document_id,
archive.unarchive::<Mailbox>()?,
);
}
{
insert_item(
&mut new_cache,
*document_id,
archive.unarchive::<Mailbox>()?,
);
}
}
+21 -21
View File
@@ -136,23 +136,23 @@ impl EmailDeletion for Server {
}
// Auto-expunge deleted and junk messages
if let Some(hold_period) = self.core.jmap.mail_autoexpunge_after {
if let Err(err) = self.emails_auto_expunge(account_id, hold_period).await {
trc::error!(
err.details("Failed to auto-expunge e-mail messages.")
.account_id(account_id)
);
}
if let Some(hold_period) = self.core.jmap.mail_autoexpunge_after
&& let Err(err) = self.emails_auto_expunge(account_id, hold_period).await
{
trc::error!(
err.details("Failed to auto-expunge e-mail messages.")
.account_id(account_id)
);
}
// Auto-expunge iMIP messages
if let Some(hold_period) = self.core.groupware.itip_inbox_auto_expunge {
if let Err(err) = self.itip_auto_expunge(account_id, hold_period).await {
trc::error!(
err.details("Failed to auto-expunge iTIP messages.")
.account_id(account_id)
);
}
if let Some(hold_period) = self.core.groupware.itip_inbox_auto_expunge
&& let Err(err) = self.itip_auto_expunge(account_id, hold_period).await
{
trc::error!(
err.details("Failed to auto-expunge iTIP messages.")
.account_id(account_id)
);
}
// Purge tombstoned messages
@@ -164,13 +164,13 @@ impl EmailDeletion for Server {
}
// Purge changelogs
if let Some(history) = self.core.jmap.changes_max_history {
if let Err(err) = self.delete_changes(account_id, history).await {
trc::error!(
err.details("Failed to purge changes.")
.account_id(account_id)
);
}
if let Some(history) = self.core.jmap.changes_max_history
&& let Err(err) = self.delete_changes(account_id, history).await
{
trc::error!(
err.details("Failed to purge changes.")
.account_id(account_id)
);
}
// Delete lock
+133 -138
View File
@@ -205,38 +205,36 @@ impl EmailIngest for Server {
}
// If the message is classified as spam, check whether the sender address is present in the user's address book
if is_spam && self.core.spam.card_is_ham {
if let Some(sender) = message
if is_spam
&& self.core.spam.card_is_ham
&& let Some(sender) = message
.from()
.and_then(|s| s.first())
.and_then(|s| s.address())
.and_then(sanitize_email)
&& sender != deliver_to
&& is_sender_authenticated
&& !self
.store()
.filter(
account_id,
Collection::ContactCard,
vec![Filter::eq(IDX_EMAIL, sender.into_bytes())],
)
.await
.caused_by(trc::location!())?
.results
.is_empty()
{
is_spam = false;
if self
.core
.spam
.bayes
.as_ref()
.is_some_and(|config| config.auto_learn_card_is_ham)
{
if sender != deliver_to
&& is_sender_authenticated
&& !self
.store()
.filter(
account_id,
Collection::ContactCard,
vec![Filter::eq(IDX_EMAIL, sender.into_bytes())],
)
.await
.caused_by(trc::location!())?
.results
.is_empty()
{
is_spam = false;
if self
.core
.spam
.bayes
.as_ref()
.is_some_and(|config| config.auto_learn_card_is_ham)
{
train_spam = Some(false);
}
}
train_spam = Some(false);
}
}
@@ -315,75 +313,72 @@ impl EmailIngest for Server {
.subtype()
.is_some_and(|st| st.eq_ignore_ascii_case("calendar"))
&& ct.has_attribute("method")
}) {
if let Some(itip_message) = part.text_contents() {
if itip_message.len()
< self.core.groupware.itip_inbound_max_ical_size
{
if let Some(sender) = sender.get_or_insert_with(|| {
message
.from()
.and_then(|s| s.first())
.and_then(|s| s.address())
.and_then(sanitize_email)
}) {
match self
.itip_ingest(
params.access_token,
&resource_token,
sender,
itip_message,
)
.await
{
Ok(message) => {
if let Some(message) = message {
itip_messages.push(message);
}
trc::event!(
Calendar(
trc::CalendarEvent::ItipMessageReceived
),
SpanId = params.session_id,
From = sender.to_string(),
AccountId = account_id,
);
}) && let Some(itip_message) = part.text_contents()
{
if itip_message.len() < self.core.groupware.itip_inbound_max_ical_size {
if let Some(sender) = sender.get_or_insert_with(|| {
message
.from()
.and_then(|s| s.first())
.and_then(|s| s.address())
.and_then(sanitize_email)
}) {
match self
.itip_ingest(
params.access_token,
&resource_token,
sender,
itip_message,
)
.await
{
Ok(message) => {
if let Some(message) = message {
itip_messages.push(message);
}
Err(ItipIngestError::Message(itip_error)) => {
match itip_error {
ItipError::NothingToSend
| ItipError::OtherSchedulingAgent => (),
err => {
trc::event!(
Calendar(trc::CalendarEvent::ItipMessageError),
SpanId = params.session_id,
From = sender.to_string(),
AccountId = account_id,
Details = err.to_string(),
)
}
trc::event!(
Calendar(trc::CalendarEvent::ItipMessageReceived),
SpanId = params.session_id,
From = sender.to_string(),
AccountId = account_id,
);
}
Err(ItipIngestError::Message(itip_error)) => {
match itip_error {
ItipError::NothingToSend
| ItipError::OtherSchedulingAgent => (),
err => {
trc::event!(
Calendar(
trc::CalendarEvent::ItipMessageError
),
SpanId = params.session_id,
From = sender.to_string(),
AccountId = account_id,
Details = err.to_string(),
)
}
}
Err(ItipIngestError::Internal(err)) => {
trc::error!(err.caused_by(trc::location!()));
}
}
Err(ItipIngestError::Internal(err)) => {
trc::error!(err.caused_by(trc::location!()));
}
}
} else {
trc::event!(
Calendar(trc::CalendarEvent::ItipMessageError),
SpanId = params.session_id,
From = message
.from()
.and_then(|a| a.first())
.and_then(|a| a.address())
.map(|a| a.to_string()),
AccountId = account_id,
Details = "iMIP message too large",
Limit = self.core.groupware.itip_inbound_max_ical_size,
Size = itip_message.len(),
)
}
} else {
trc::event!(
Calendar(trc::CalendarEvent::ItipMessageError),
SpanId = params.session_id,
From = message
.from()
.and_then(|a| a.first())
.and_then(|a| a.address())
.map(|a| a.to_string()),
AccountId = account_id,
Details = "iMIP message too large",
Limit = self.core.groupware.itip_inbound_max_ical_size,
Size = itip_message.len(),
)
}
}
}
@@ -515,12 +510,12 @@ impl EmailIngest for Server {
part.offset_end += offset_start as u32;
part.offset_header += offset_start as u32;
if let PartType::Message(sub_message) = &mut part.body {
if sub_message.root_part().offset_header != 0 {
sub_message.raw_message = raw_message.as_ref().into();
part_iter_stack.push(part_iter);
part_iter = sub_message.parts.iter_mut();
}
if let PartType::Message(sub_message) = &mut part.body
&& sub_message.root_part().offset_header != 0
{
sub_message.raw_message = raw_message.as_ref().into();
part_iter_stack.push(part_iter);
part_iter = sub_message.parts.iter_mut();
}
} else if let Some(iter) = part_iter_stack.pop() {
part_iter = iter;
@@ -544,56 +539,56 @@ impl EmailIngest for Server {
IngestSource::Smtp { .. } => self.core.jmap.encrypt,
IngestSource::Restore => false,
};
if do_encrypt && !message.is_encrypted() {
if let Some(encrypt_params_) = self
if do_encrypt
&& !message.is_encrypted()
&& let Some(encrypt_params_) = self
.get_archive_by_property(account_id, Collection::Principal, 0, Property::Parameters)
.await
.caused_by(trc::location!())?
{
let encrypt_params = encrypt_params_
.unarchive::<EncryptionParams>()
.caused_by(trc::location!())?;
match message.encrypt(encrypt_params).await {
Ok(new_raw_message) => {
raw_message = Cow::from(new_raw_message);
raw_message_len = raw_message.len() as u64;
message = MessageParser::default()
.parse(raw_message.as_ref())
.ok_or_else(|| {
trc::EventType::MessageIngest(trc::MessageIngestEvent::Error)
.ctx(trc::Key::Code, 550)
.ctx(
trc::Key::Reason,
"Failed to parse encrypted e-mail message.",
)
})?;
{
let encrypt_params = encrypt_params_
.unarchive::<EncryptionParams>()
.caused_by(trc::location!())?;
match message.encrypt(encrypt_params).await {
Ok(new_raw_message) => {
raw_message = Cow::from(new_raw_message);
raw_message_len = raw_message.len() as u64;
message = MessageParser::default()
.parse(raw_message.as_ref())
.ok_or_else(|| {
trc::EventType::MessageIngest(trc::MessageIngestEvent::Error)
.ctx(trc::Key::Code, 550)
.ctx(
trc::Key::Reason,
"Failed to parse encrypted e-mail message.",
)
})?;
// Remove contents from parsed message
for part in &mut message.parts {
match &mut part.body {
PartType::Text(txt) | PartType::Html(txt) => {
*txt = Cow::from("");
}
PartType::Binary(bin) | PartType::InlineBinary(bin) => {
*bin = Cow::from(&[][..]);
}
PartType::Message(_) => {
part.body = PartType::Binary(Cow::from(&[][..]));
}
PartType::Multipart(_) => (),
// Remove contents from parsed message
for part in &mut message.parts {
match &mut part.body {
PartType::Text(txt) | PartType::Html(txt) => {
*txt = Cow::from("");
}
PartType::Binary(bin) | PartType::InlineBinary(bin) => {
*bin = Cow::from(&[][..]);
}
PartType::Message(_) => {
part.body = PartType::Binary(Cow::from(&[][..]));
}
PartType::Multipart(_) => (),
}
}
Err(EncryptMessageError::Error(err)) => {
trc::bail!(
trc::StoreEvent::CryptoError
.into_err()
.caused_by(trc::location!())
.reason(err)
);
}
_ => unreachable!(),
}
Err(EncryptMessageError::Error(err)) => {
trc::bail!(
trc::StoreEvent::CryptoError
.into_err()
.caused_by(trc::location!())
.reason(err)
);
}
_ => unreachable!(),
}
}
+17 -18
View File
@@ -78,26 +78,25 @@ impl SieveScriptActivate for Server {
}
// Activate script
if let Some(document_id) = activate_id {
if let Some(sieve_) = self
if let Some(document_id) = activate_id
&& let Some(sieve_) = self
.get_archive(account_id, Collection::SieveScript, document_id)
.await?
{
let sieve = sieve_
.to_unarchived::<SieveScript>()
.caused_by(trc::location!())?;
let mut new_sieve = sieve.deserialize().caused_by(trc::location!())?;
new_sieve.is_active = true;
batch
.update_document(document_id)
.custom(
ObjectIndexBuilder::new()
.with_changes(new_sieve)
.with_current(sieve),
)
.caused_by(trc::location!())?;
changed_ids.push((document_id, true));
}
{
let sieve = sieve_
.to_unarchived::<SieveScript>()
.caused_by(trc::location!())?;
let mut new_sieve = sieve.deserialize().caused_by(trc::location!())?;
new_sieve.is_active = true;
batch
.update_document(document_id)
.custom(
ObjectIndexBuilder::new()
.with_changes(new_sieve)
.with_current(sieve),
)
.caused_by(trc::location!())?;
changed_ids.push((document_id, true));
}
// Write changes
+15 -15
View File
@@ -186,10 +186,10 @@ impl SieveScriptIngest for Server {
TRASH_ID
} else {
let mut mailbox_id = u32::MAX;
if let Ok(role) = SpecialUse::parse_value(&role) {
if let Some(m) = cache.mailbox_by_role(&role) {
mailbox_id = m.document_id;
}
if let Ok(role) = SpecialUse::parse_value(&role)
&& let Some(m) = cache.mailbox_by_role(&role)
{
mailbox_id = m.document_id;
}
mailbox_id
@@ -317,17 +317,17 @@ impl SieveScriptIngest for Server {
}
// Find mailbox by role
if let Some(special_use) = special_use {
if target_id == u32::MAX {
if special_use.eq_ignore_ascii_case("inbox") {
target_id = INBOX_ID;
} else if special_use.eq_ignore_ascii_case("trash") {
target_id = TRASH_ID;
} else if let Ok(role) = SpecialUse::parse_value(&special_use) {
if let Some(item) = cache.mailbox_by_role(&role) {
target_id = item.document_id;
}
}
if let Some(special_use) = special_use
&& target_id == u32::MAX
{
if special_use.eq_ignore_ascii_case("inbox") {
target_id = INBOX_ID;
} else if special_use.eq_ignore_ascii_case("trash") {
target_id = TRASH_ID;
} else if let Ok(role) = SpecialUse::parse_value(&special_use)
&& let Some(item) = cache.mailbox_by_role(&role)
{
target_id = item.document_id;
}
}
+48 -48
View File
@@ -80,54 +80,9 @@ impl ArchivedCalendarEventData {
.single()?
.timestamp();
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz) {
if alarm_time > start_time {
if let Some(next) = next_alarm {
if alarm_time < next.alarm_time {
next_alarm = Some(CalendarAlarm {
alarm_id: alarm.id.to_native(),
event_id: alarm.parent_id.to_native(),
alarm_time,
event_start: start_date_naive,
event_start_tz: start_tz.as_id(),
event_end: end_date_naive,
event_end_tz: end_tz.as_id(),
});
}
} else {
next_alarm = Some(CalendarAlarm {
alarm_id: alarm.id.to_native(),
event_id: alarm.parent_id.to_native(),
alarm_time,
event_start: start_date_naive,
event_start_tz: start_tz.as_id(),
event_end: end_date_naive,
event_end_tz: end_tz.as_id(),
});
}
continue 'outer;
}
}
}
} else {
// Single event
let start_date_naive = offset_or_count as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz) {
if alarm_time > start_time {
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz)
&& alarm_time > start_time
{
if let Some(next) = next_alarm {
if alarm_time < next.alarm_time {
next_alarm = Some(CalendarAlarm {
@@ -151,6 +106,51 @@ impl ArchivedCalendarEventData {
event_end_tz: end_tz.as_id(),
});
}
continue 'outer;
}
}
} else {
// Single event
let start_date_naive = offset_or_count as i64 + base_offset;
let end_date_naive = start_date_naive + duration;
let start = start_tz
.from_local_datetime(
&DateTime::from_timestamp(start_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
let end = end_tz
.from_local_datetime(
&DateTime::from_timestamp(end_date_naive, 0)?.naive_local(),
)
.single()?
.timestamp();
if let Some(alarm_time) = alarm.delta.to_timestamp(start, end, default_tz)
&& alarm_time > start_time
{
if let Some(next) = next_alarm {
if alarm_time < next.alarm_time {
next_alarm = Some(CalendarAlarm {
alarm_id: alarm.id.to_native(),
event_id: alarm.parent_id.to_native(),
alarm_time,
event_start: start_date_naive,
event_start_tz: start_tz.as_id(),
event_end: end_date_naive,
event_end_tz: end_tz.as_id(),
});
}
} else {
next_alarm = Some(CalendarAlarm {
alarm_id: alarm.id.to_native(),
event_id: alarm.parent_id.to_native(),
alarm_time,
event_start: start_date_naive,
event_start_tz: start_tz.as_id(),
event_end: end_date_naive,
event_end_tz: end_tz.as_id(),
});
}
}
}
+3 -4
View File
@@ -160,10 +160,9 @@ fn decode_plain_auth(token: &str) -> Option<Credentials<String>> {
}
fn decode_bearer_token(token: &str, allow_api_access: bool) -> Option<Credentials<String>> {
if allow_api_access {
if let Some(token) = token.strip_prefix("api_").and_then(decode_plain_auth) {
return Some(token);
}
if allow_api_access && let Some(token) = token.strip_prefix("api_").and_then(decode_plain_auth)
{
return Some(token);
}
Some(Credentials::OAuthBearer {
+12 -17
View File
@@ -203,22 +203,18 @@ impl Autoconfig for Server {
.email_to_id(emailaddress)
.await
.caused_by(trc::location!())?
{
if let Ok(Some(principal)) = self
&& let Ok(Some(principal)) = self
.core
.storage
.directory
.query(QueryParams::id(id).with_return_member_of(false))
.await
{
if principal
.emails
.first()
.is_some_and(|email| email.eq_ignore_ascii_case(emailaddress))
{
account_name = principal.name;
}
}
&& principal
.emails
.first()
.is_some_and(|email| email.eq_ignore_ascii_case(emailaddress))
{
account_name = principal.name;
}
Ok((account_name, self.core.network.server_name.clone(), domain))
@@ -296,12 +292,11 @@ fn parse_autodiscover_request(bytes: &[u8]) -> Result<String, String> {
}
}
if let Ok(Event::Text(text)) = reader.read_event_into(&mut buf) {
if let Ok(text) = text.unescape() {
if text.contains('@') {
return Ok(text.trim().to_lowercase());
}
}
if let Ok(Event::Text(text)) = reader.read_event_into(&mut buf)
&& let Ok(text) = text.unescape()
&& text.contains('@')
{
return Ok(text.trim().to_lowercase());
}
Err(format!(
+17 -18
View File
@@ -50,24 +50,23 @@ impl FormHandler for Server {
form_data: FormData,
) -> trc::Result<HttpResponse> {
// Validate rate
if let Some(rate) = &form.rate {
if !session.remote_ip.is_loopback()
&& self
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_CONTACT,
&ip_to_bytes(&session.remote_ip),
rate,
false,
)
.await
.caused_by(trc::location!())?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
if let Some(rate) = &form.rate
&& !session.remote_ip.is_loopback()
&& self
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_CONTACT,
&ip_to_bytes(&session.remote_ip),
rate,
false,
)
.await
.caused_by(trc::location!())?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
// Validate honeypot
+12 -13
View File
@@ -168,16 +168,15 @@ impl PrincipalManager for Server {
.await?;
// Set report domain
if let Some(report_domain) = report_domain {
if let Err(err) = self
if let Some(report_domain) = report_domain
&& let Err(err) = self
.core
.storage
.config
.set([("report.domain", report_domain)], true)
.await
{
trc::error!(err.details("Failed to set report domain"));
}
{
trc::error!(err.details("Failed to set report domain"));
}
// Increment revision
@@ -205,20 +204,20 @@ impl PrincipalManager for Server {
.unwrap_or_default()
.split(',')
{
if let Some(typ) = Type::parse(typ) {
if !types.contains(&typ) {
types.push(typ);
}
if let Some(typ) = Type::parse(typ)
&& !types.contains(&typ)
{
types.push(typ);
}
}
// Parse fields
let mut fields = Vec::new();
for field in params.get("fields").unwrap_or_default().split(',') {
if let Some(field) = PrincipalField::try_parse(field) {
if !fields.contains(&field) {
fields.push(field);
}
if let Some(field) = PrincipalField::try_parse(field)
&& !fields.contains(&field)
{
fields.push(field);
}
}
+24 -19
View File
@@ -145,24 +145,24 @@ impl QueueManagement for Server {
// Limit to tenant domains
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant) = access_token.tenant {
tenant_domains = self
.core
.storage
.data
.list_principals(None, tenant.id.into(), &[Type::Domain], false, 0, 0)
.await
.map(|principals| {
principals
.items
.into_iter()
.map(|p| p.name)
.collect::<Vec<_>>()
})
.caused_by(trc::location!())?
.into();
}
if self.core.is_enterprise_edition()
&& let Some(tenant) = access_token.tenant
{
tenant_domains = self
.core
.storage
.data
.list_principals(None, tenant.id.into(), &[Type::Domain], false, 0, 0)
.await
.map(|principals| {
principals
.items
.into_iter()
.map(|p| p.name)
.collect::<Vec<_>>()
})
.caused_by(trc::location!())?
.into();
}
// SPDX-SnippetEnd
@@ -458,7 +458,12 @@ impl QueueManagement for Server {
{
let mut rua = Vec::new();
if let Some(report) = self
.generate_tls_aggregate_report(&[event.clone()], &mut rua, None, 0)
.generate_tls_aggregate_report(
std::slice::from_ref(&event),
&mut rua,
None,
0,
)
.await?
{
result = Report::tls(event, report, rua).into();
+22 -23
View File
@@ -53,24 +53,24 @@ impl ManageReports for Server {
// Limit to tenant domains
#[cfg(feature = "enterprise")]
if self.core.is_enterprise_edition() {
if let Some(tenant) = access_token.tenant {
tenant_domains = self
.core
.storage
.data
.list_principals(None, tenant.id.into(), &[Type::Domain], false, 0, 0)
.await
.map(|principals| {
principals
.items
.into_iter()
.map(|p| p.name)
.collect::<Vec<_>>()
})
.caused_by(trc::location!())?
.into();
}
if self.core.is_enterprise_edition()
&& let Some(tenant) = access_token.tenant
{
tenant_domains = self
.core
.storage
.data
.list_principals(None, tenant.id.into(), &[Type::Domain], false, 0, 0)
.await
.map(|principals| {
principals
.items
.into_iter()
.map(|p| p.name)
.collect::<Vec<_>>()
})
.caused_by(trc::location!())?
.into();
}
// SPDX-SnippetEnd
@@ -206,12 +206,11 @@ impl ManageReports for Server {
}
}
if !batch.is_empty() {
if let Err(err) =
if !batch.is_empty()
&& let Err(err) =
server.core.storage.data.write(batch.build_all()).await
{
trc::error!(err.caused_by(trc::location!()));
}
{
trc::error!(err.caused_by(trc::location!()));
}
});
}
+15 -15
View File
@@ -91,20 +91,20 @@ impl ManageSettings for Server {
let mut total = 0;
let mut ids = Vec::new();
for key in settings.keys() {
if let Some(id) = key.strip_suffix(&suffix) {
if !id.is_empty() {
if !has_filter {
if offset == 0 {
if limit == 0 || ids.len() < limit {
ids.push(id);
}
} else {
offset -= 1;
if let Some(id) = key.strip_suffix(&suffix)
&& !id.is_empty()
{
if !has_filter {
if offset == 0 {
if limit == 0 || ids.len() < limit {
ids.push(id);
}
total += 1;
} else {
ids.push(id);
offset -= 1;
}
total += 1;
} else {
ids.push(id);
}
}
}
@@ -318,10 +318,10 @@ impl ManageSettings for Server {
{
return Err(trc::ManageEvent::AssertFailed.into_err());
}
} else if let Some((key, _)) = values.first() {
if self.core.storage.config.get(key).await?.is_some() {
return Err(trc::ManageEvent::AssertFailed.into_err());
}
} else if let Some((key, _)) = values.first()
&& self.core.storage.config.get(key).await?.is_some()
{
return Err(trc::ManageEvent::AssertFailed.into_err());
}
}
+7 -8
View File
@@ -557,16 +557,15 @@ impl ParseHttp for Server {
"metrics" => match path.next().unwrap_or_default() {
"prometheus" => {
if let Some(prometheus) = &self.core.metrics.prometheus {
if let Some(auth) = &prometheus.auth {
if req
if let Some(auth) = &prometheus.auth
&& req
.authorization_basic()
.is_none_or(|secret| secret != auth)
{
return Err(trc::AuthEvent::Failed
.into_err()
.details("Invalid or missing credentials.")
.caused_by(trc::location!()));
}
{
return Err(trc::AuthEvent::Failed
.into_err()
.details("Invalid or missing credentials.")
.caused_by(trc::location!()));
}
return Ok(Resource::new(
+1 -1
View File
@@ -220,7 +220,7 @@ mod tests {
],
}
.serialize(),
concat!("* CAPABILITY IMAP4rev2 STARTTLS LOGINDISABLED\r\n",).as_bytes()
"* CAPABILITY IMAP4rev2 STARTTLS LOGINDISABLED\r\n".as_bytes()
);
}
}
+1 -1
View File
@@ -105,7 +105,7 @@ mod tests {
.serialize()
)
.unwrap(),
concat!("* VANISHED 3:5\r\n")
"* VANISHED 3:5\r\n"
);
}
}
+1 -1
View File
@@ -105,7 +105,7 @@ mod tests {
}],
}],
},
concat!("* QUOTA \"INBOX\" (STORAGE 1024 1048576)\r\n"),
"* QUOTA \"INBOX\" (STORAGE 1024 1048576)\r\n",
),
(
super::Response {
+8 -8
View File
@@ -235,8 +235,8 @@ mod tests {
highest_modseq: None,
},
"A283",
concat!("* ESEARCH (TAG \"A283\") COUNT 3 MIN 2 MAX 11 ALL 2,10:11\r\n",),
concat!("* SEARCH 2 10 11\r\n"),
"* ESEARCH (TAG \"A283\") COUNT 3 MIN 2 MAX 11 ALL 2,10:11\r\n",
"* SEARCH 2 10 11\r\n",
),
(
super::Response {
@@ -252,8 +252,8 @@ mod tests {
highest_modseq: None,
},
"A283",
concat!("* ESEARCH (TAG \"A283\") ALL 1:3,5,10:13,90,92:99\r\n",),
concat!("* SEARCH 1 2 3 5 10 11 12 13 90 92 93 94 95 96 97 98 99\r\n",),
"* ESEARCH (TAG \"A283\") ALL 1:3,5,10:13,90,92:99\r\n",
"* SEARCH 1 2 3 5 10 11 12 13 90 92 93 94 95 96 97 98 99\r\n",
),
(
super::Response {
@@ -267,8 +267,8 @@ mod tests {
highest_modseq: None,
},
"A283",
concat!("* ESEARCH (TAG \"A283\")\r\n",),
concat!("* SEARCH\r\n"),
"* ESEARCH (TAG \"A283\")\r\n",
"* SEARCH\r\n",
),
(
super::Response {
@@ -282,8 +282,8 @@ mod tests {
highest_modseq: 12345.into(),
},
"A283",
concat!("* ESEARCH (TAG \"A283\") ALL 10:13,21 MODSEQ 12345\r\n",),
concat!("* SEARCH 10 11 12 13 21 (MODSEQ 12345)\r\n",),
"* ESEARCH (TAG \"A283\") ALL 10:13,21 MODSEQ 12345\r\n",
"* SEARCH 10 11 12 13 21 (MODSEQ 12345)\r\n",
),
] {
let response_v2 = String::from_utf8(response.clone().serialize(tag)).unwrap();
+1 -3
View File
@@ -102,9 +102,7 @@ mod tests {
assert_eq!(
String::from_utf8(buf).unwrap(),
concat!(
"* STATUS \"blurdybloop\" (MESSAGES 231 UIDNEXT 44292 MAILBOXID (abc-123))\r\n",
)
"* STATUS \"blurdybloop\" (MESSAGES 231 UIDNEXT 44292 MAILBOXID (abc-123))\r\n"
);
}
}
+1 -1
View File
@@ -59,7 +59,7 @@ mod tests {
.serialize()
)
.unwrap(),
concat!("* THREAD (2 10 11)(49)(1 3)\r\n",)
"* THREAD (2 10 11)(49)(1 3)\r\n"
);
}
}
+22 -25
View File
@@ -260,14 +260,13 @@ impl<T: SessionStream> Session<T> {
}
}
if let Some(needs_literal) = needs_literal {
if let Err(err) = self
if let Some(needs_literal) = needs_literal
&& let Err(err) = self
.write_bytes(format!("+ Ready for {} bytes.\r\n", needs_literal).into_bytes())
.await
{
self.write_error(err).await;
return SessionResult::Close;
}
{
self.write_error(err).await;
return SessionResult::Close;
}
SessionResult::Continue
@@ -294,25 +293,23 @@ impl<T: SessionStream> Session<T> {
async fn is_allowed(&self, request: Request<Command>) -> trc::Result<Request<Command>> {
let state = &self.state;
// Rate limit request
if let State::Authenticated { data } | State::Selected { data, .. } = state {
if let Some(rate) = &self.server.core.imap.rate_requests {
if data
.server
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_IMAP,
&data.account_id.to_be_bytes(),
rate,
true,
)
.await?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
}
if let State::Authenticated { data } | State::Selected { data, .. } = state
&& let Some(rate) = &self.server.core.imap.rate_requests
&& data
.server
.core
.storage
.lookup
.is_rate_allowed(
KV_RATE_LIMIT_IMAP,
&data.account_id.to_be_bytes(),
rate,
true,
)
.await?
.is_some()
{
return Err(trc::LimitEvent::TooManyRequests.into_err());
}
match &request.command {
+5 -6
View File
@@ -316,12 +316,11 @@ impl<T: SessionStream> SessionData<T> {
// Add new mailboxes
for (mailbox_name, mailbox_id) in new_account.mailbox_names.iter() {
if let Some(old_mailbox) = old_account.mailbox_state.get(mailbox_id) {
if let Some(mailbox) = new_account.mailbox_state.get(mailbox_id) {
if mailbox.total_messages != old_mailbox.total_messages
|| mailbox.total_unseen != old_mailbox.total_unseen
{
changes.changed.push(mailbox_name.clone());
}
if let Some(mailbox) = new_account.mailbox_state.get(mailbox_id)
&& (mailbox.total_messages != old_mailbox.total_messages
|| mailbox.total_unseen != old_mailbox.total_unseen)
{
changes.changed.push(mailbox_name.clone());
}
} else {
changes.added.push(mailbox_name.clone());
+6 -6
View File
@@ -28,12 +28,12 @@ impl SessionManager for ImapSessionManager {
session: SessionData<T>,
) -> impl std::future::Future<Output = ()> + Send {
async move {
if let Ok(mut session) = Session::new(session, self).await {
if session.handle_conn().await && session.instance.acceptor.is_tls() {
if let Ok(mut session) = session.into_tls().await {
session.handle_conn().await;
}
}
if let Ok(mut session) = Session::new(session, self).await
&& session.handle_conn().await
&& session.instance.acceptor.is_tls()
&& let Ok(mut session) = session.into_tls().await
{
session.handle_conn().await;
}
}
}
+4 -4
View File
@@ -85,10 +85,10 @@ impl<T: SessionStream> SessionData<T> {
for (pos, &path_item) in params.path.iter().enumerate() {
let mut mailbox = email::mailbox::Mailbox::new(path_item).with_parent_id(parent_id);
if pos == params.path.len() - 1 {
if let Some(mailbox_role) = arguments.mailbox_role.map(attr_to_role) {
mailbox.role = mailbox_role;
}
if pos == params.path.len() - 1
&& let Some(mailbox_role) = arguments.mailbox_role.map(attr_to_role)
{
mailbox.role = mailbox_role;
}
let mailbox_id = next_document_id;
next_document_id -= 1;
+37 -39
View File
@@ -540,33 +540,32 @@ impl<T: SessionStream> SessionData<T> {
self.write_bytes(buf).await?;
// Add to set flags
if set_seen_flag {
if let Some(data_) = self
if set_seen_flag
&& let Some(data_) = self
.server
.get_archive(account_id, Collection::Email, id)
.await
.imap_ctx(&arguments.tag, trc::location!())?
{
let data = data_
.to_unarchived::<MessageData>()
.imap_ctx(&arguments.tag, trc::location!())?;
let mut new_data = data
.deserialize()
.imap_ctx(&arguments.tag, trc::location!())?;
new_data.keywords.push(Keyword::Seen);
{
let data = data_
.to_unarchived::<MessageData>()
.imap_ctx(&arguments.tag, trc::location!())?;
let mut new_data = data
.deserialize()
.imap_ctx(&arguments.tag, trc::location!())?;
new_data.keywords.push(Keyword::Seen);
batch
.with_account_id(account_id)
.with_collection(Collection::Email)
.update_document(id)
.custom(
ObjectIndexBuilder::new()
.with_current(data)
.with_changes(new_data),
)
.imap_ctx(&arguments.tag, trc::location!())?
.commit_point();
}
batch
.with_account_id(account_id)
.with_collection(Collection::Email)
.update_document(id)
.custom(
ObjectIndexBuilder::new()
.with_current(data)
.with_changes(new_data),
)
.imap_ctx(&arguments.tag, trc::location!())?
.commit_point();
}
}
@@ -620,7 +619,7 @@ impl<T: SessionStream> SessionData<T> {
#[allow(clippy::result_unit_err)]
pub trait AsImapDataItem {
fn body_structure(&self, decoded: &DecodedParts<'_>, is_extended: bool) -> BodyPart;
fn body_structure(&'_ self, decoded: &DecodedParts<'_>, is_extended: bool) -> BodyPart<'_>;
fn body_section<'x>(
&self,
decoded: &'x DecodedParts<'x>,
@@ -639,24 +638,24 @@ pub trait AsImapDataItem {
#[allow(clippy::result_unit_err)]
pub trait AsImapDataItemPart {
fn as_body_part(
&self,
&'_ self,
decoded: &DecodedParts<'_>,
message_id: usize,
part_id: usize,
is_extended: bool,
) -> BodyPart;
) -> BodyPart<'_>;
fn envelope(&self) -> Envelope;
fn envelope(&'_ self) -> Envelope<'_>;
}
impl AsImapDataItemPart for ArchivedMessageMetadataContents {
fn as_body_part(
&self,
&'_ self,
decoded: &DecodedParts<'_>,
message_id: usize,
part_id: usize,
is_extended: bool,
) -> BodyPart {
) -> BodyPart<'_> {
let part = &self.parts[part_id];
let body = decoded.raw_message_section_arch(message_id, part.offset_body, part.offset_end);
let (is_multipart, is_text) = match &part.body {
@@ -800,7 +799,7 @@ impl AsImapDataItemPart for ArchivedMessageMetadataContents {
}
}
fn envelope(&self) -> Envelope {
fn envelope(&'_ self) -> Envelope<'_> {
let headers = self.root_part();
Envelope {
date: headers.date(),
@@ -847,7 +846,7 @@ impl AsImapDataItemPart for ArchivedMessageMetadataContents {
}
impl AsImapDataItem for ArchivedMessageMetadata {
fn body_structure(&self, decoded: &DecodedParts<'_>, is_extended: bool) -> BodyPart {
fn body_structure(&'_ self, decoded: &DecodedParts<'_>, is_extended: bool) -> BodyPart<'_> {
let mut stack = Vec::new();
let base_part = [u16_le::from_native(0)];
let mut parts = base_part.as_slice().iter();
@@ -941,19 +940,18 @@ impl AsImapDataItem for ArchivedMessageMetadata {
None
}?;
if let ArchivedMetadataPartType::Message(nested_message_id) = &part.body {
if let Some((
if let ArchivedMetadataPartType::Message(nested_message_id) = &part.body
&& let Some((
_,
Section::Part { .. }
| Section::Header
| Section::HeaderFields { .. }
| Section::Text,
)) = sections_iter.peek()
{
message = self.message_id(*nested_message_id);
part = message.root_part();
message_id = u16::from(nested_message_id) as usize;
}
{
message = self.message_id(*nested_message_id);
part = message.root_part();
message_id = u16::from(nested_message_id) as usize;
}
}
Section::Header => {
@@ -1208,11 +1206,11 @@ fn get_partial_bytes(bytes: &[u8], partial: Option<(u32, u32)>) -> &[u8] {
}
trait AsImapAddress {
fn as_imap_address(&self) -> Vec<fetch::Address>;
fn as_imap_address(&'_ self) -> Vec<fetch::Address<'_>>;
}
impl AsImapAddress for ArchivedHeaderValue<'_> {
fn as_imap_address(&self) -> Vec<fetch::Address> {
fn as_imap_address(&'_ self) -> Vec<fetch::Address<'_>> {
let mut addresses = Vec::new();
match self {
+11 -11
View File
@@ -76,17 +76,17 @@ impl<T: SessionStream> SessionData<T> {
// Verify if mailbox is already subscribed/unsubscribed
for account in self.mailboxes.lock().iter_mut() {
if account.account_id == account_id {
if let Some(mailbox) = account.mailbox_state.get(&mailbox_id) {
if mailbox.is_subscribed == subscribe {
return Err(trc::ImapEvent::Error
.into_err()
.details(if subscribe {
"Mailbox is already subscribed."
} else {
"Mailbox is already unsubscribed."
})
.id(tag));
}
if let Some(mailbox) = account.mailbox_state.get(&mailbox_id)
&& mailbox.is_subscribed == subscribe
{
return Err(trc::ImapEvent::Error
.into_err()
.details(if subscribe {
"Mailbox is already subscribed."
} else {
"Mailbox is already unsubscribed."
})
.id(tag));
}
break;
}
+4 -4
View File
@@ -89,10 +89,10 @@ impl<T: SessionStream> SessionData<T> {
let mut threads: AHashMap<u32, Vec<u32>> = AHashMap::new();
let state = mailbox.state.lock();
for item in &cache.emails.items {
if result_set.results.contains(item.document_id) {
if let Some((imap_id, _)) = state.map_result_id(item.document_id, is_uid) {
threads.entry(item.thread_id).or_default().push(imap_id);
}
if result_set.results.contains(item.document_id)
&& let Some((imap_id, _)) = state.map_result_id(item.document_id, is_uid)
{
threads.entry(item.thread_id).or_default().push(imap_id);
}
}
+2 -2
View File
@@ -186,7 +186,7 @@ impl<'x> RequestError<'x> {
RequestError::blank(401, "Unauthorized", "You have to authenticate first.")
}
pub fn unknown_capability(capability: &str) -> RequestError {
pub fn unknown_capability(capability: &'_ str) -> RequestError<'_> {
RequestError {
p_type: RequestErrorType::UnknownCapability,
limit: None,
@@ -204,7 +204,7 @@ impl<'x> RequestError<'x> {
}
}
pub fn not_json(detail: &str) -> RequestError {
pub fn not_json(detail: &'_ str) -> RequestError<'_> {
RequestError {
p_type: RequestErrorType::NotJSON,
limit: None,
+19 -19
View File
@@ -144,25 +144,25 @@ impl Response {
let mut graph = HashMap::with_capacity(request.create.len());
for (create_id, object) in request.create.iter_mut() {
for data in &mut object.data {
if let DataSourceObject::Id { id, .. } = data {
if let MaybeReference::Reference(parent_id) = id {
match self.created_ids.get(parent_id) {
Some(AnyId::Blob(blob_id)) => {
*id = MaybeReference::Value(blob_id.clone());
}
Some(_) => {
return Err(trc::JmapEvent::InvalidResultReference
.into_err()
.details(format_compact!(
"Id reference {parent_id:?} points to invalid type."
)));
}
None => {
graph
.entry(create_id.to_string())
.or_insert_with(Vec::new)
.push(parent_id.to_string());
}
if let DataSourceObject::Id { id, .. } = data
&& let MaybeReference::Reference(parent_id) = id
{
match self.created_ids.get(parent_id) {
Some(AnyId::Blob(blob_id)) => {
*id = MaybeReference::Value(blob_id.clone());
}
Some(_) => {
return Err(trc::JmapEvent::InvalidResultReference
.into_err()
.details(format_compact!(
"Id reference {parent_id:?} points to invalid type."
)));
}
None => {
graph
.entry(create_id.to_string())
.or_insert_with(Vec::new)
.push(parent_id.to_string());
}
}
}
+8 -9
View File
@@ -580,21 +580,20 @@ impl JsonQueryable for Value {
) {
match pointer.next() {
Some(JsonPointerItem::String(n)) => {
if let Value::Object(map) = self {
if let Some(v) = map
if let Value::Object(map) = self
&& let Some(v) = map
.0
.iter()
.find_map(|(k, v)| if k.as_str() == n { Some(v) } else { None })
{
v.eval_pointer(pointer, results);
}
{
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Number(n)) => {
if let Value::List(values) = self {
if let Some(v) = values.get(*n as usize) {
v.eval_pointer(pointer, results);
}
if let Value::List(values) = self
&& let Some(v) = values.get(*n as usize)
{
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Wildcard) => match self {
+8 -8
View File
@@ -48,10 +48,10 @@ impl StateManager for Server {
if_in_state: &Option<State>,
) -> trc::Result<State> {
let old_state: State = self.get_state(account_id, collection).await?;
if let Some(if_in_state) = if_in_state {
if &old_state != if_in_state {
return Err(trc::JmapEvent::StateMismatch.into_err());
}
if let Some(if_in_state) = if_in_state
&& &old_state != if_in_state
{
return Err(trc::JmapEvent::StateMismatch.into_err());
}
Ok(old_state)
@@ -69,10 +69,10 @@ impl MessageCacheState for MessageStoreCache {
fn assert_state(&self, is_mailbox: bool, if_in_state: &Option<State>) -> trc::Result<State> {
let old_state: State = self.get_state(is_mailbox);
if let Some(if_in_state) = if_in_state {
if &old_state != if_in_state {
return Err(trc::JmapEvent::StateMismatch.into_err());
}
if let Some(if_in_state) = if_in_state
&& &old_state != if_in_state
{
return Err(trc::JmapEvent::StateMismatch.into_err());
}
Ok(old_state)
}
+4 -4
View File
@@ -86,11 +86,11 @@ impl JmapMethods for Server {
}
async fn build_query_response<T: Sync + Send>(
&self,
&'_ self,
result_set: &ResultSet,
query_state: State,
request: &QueryRequest<T>,
) -> trc::Result<(QueryResponse, Option<Pagination>)> {
) -> trc::Result<(QueryResponse, Option<Pagination<'_>>)> {
let total = result_set.results.len() as usize;
let (limit_total, limit) = if let Some(limit) = request.limit {
if limit > 0 {
@@ -182,11 +182,11 @@ pub trait JmapMethods: Sync + Send {
) -> impl Future<Output = trc::Result<RoaringBitmap>> + Send;
fn build_query_response<T: Sync + Send>(
&self,
&'_ self,
result_set: &ResultSet,
query_state: State,
request: &QueryRequest<T>,
) -> impl Future<Output = trc::Result<(QueryResponse, Option<Pagination>)>> + Send;
) -> impl Future<Output = trc::Result<(QueryResponse, Option<Pagination<'_>>)>> + Send;
fn sort(
&self,
+4 -4
View File
@@ -182,10 +182,10 @@ impl MailboxQuery for Server {
if response.total.is_some() {
response.total = Some(total);
}
if let Some(paginate) = &mut paginate {
if paginate.limit > total {
paginate.limit = total;
}
if let Some(paginate) = &mut paginate
&& paginate.limit > total
{
paginate.limit = total;
}
result_set.results = filtered_ids;
}
+8 -10
View File
@@ -391,8 +391,7 @@ impl SieveScriptSet for Server {
} else if update
.as_ref()
.is_none_or(|(_, obj)| obj.inner.name != value)
{
if let Some(id) = self
&& let Some(id) = self
.filter(
ctx.resource_token.account_id,
Collection::SieveScript,
@@ -401,14 +400,13 @@ impl SieveScriptSet for Server {
.await?
.results
.min()
{
return Ok(Err(SetError::already_exists()
.with_existing_id(id.into())
.with_description(format!(
"A sieve script with name '{}' already exists.",
value
))));
}
{
return Ok(Err(SetError::already_exists()
.with_existing_id(id.into())
.with_description(format!(
"A sieve script with name '{}' already exists.",
value
))));
}
changes.name = value;
+29 -29
View File
@@ -103,39 +103,39 @@ impl EmailSubmissionGet for Server {
.map(|(k, v)| (k.to_string(), DeliveryStatus::from(v)))
.collect::<VecMap<_, _>>();
let mut is_pending = false;
if let Some(queue_id) = submission.queue_id.as_ref().map(u64::from) {
if let Some(queued_message_) = self
if let Some(queue_id) = submission.queue_id.as_ref().map(u64::from)
&& let Some(queued_message_) = self
.read_message_archive(queue_id)
.await
.caused_by(trc::location!())?
{
let queued_message = queued_message_
.unarchive::<Message>()
.caused_by(trc::location!())?;
for rcpt in queued_message.recipients.iter() {
*delivery_status.get_mut_or_insert(rcpt.address().to_string()) =
DeliveryStatus {
smtp_reply: match &rcpt.status {
ArchivedStatus::Completed(reply) => {
format_archived_response(&reply.response)
}
ArchivedStatus::TemporaryFailure(reply)
| ArchivedStatus::PermanentFailure(reply) => {
format_archived_error_details(reply)
}
ArchivedStatus::Scheduled => "250 2.1.5 Queued".to_string(),
},
delivered: match &rcpt.status {
ArchivedStatus::Scheduled
| ArchivedStatus::TemporaryFailure(_) => Delivered::Queued,
ArchivedStatus::Completed(_) => Delivered::Yes,
ArchivedStatus::PermanentFailure(_) => Delivered::No,
},
displayed: false,
};
}
is_pending = true;
{
let queued_message = queued_message_
.unarchive::<Message>()
.caused_by(trc::location!())?;
for rcpt in queued_message.recipients.iter() {
*delivery_status.get_mut_or_insert(rcpt.address().to_string()) =
DeliveryStatus {
smtp_reply: match &rcpt.status {
ArchivedStatus::Completed(reply) => {
format_archived_response(&reply.response)
}
ArchivedStatus::TemporaryFailure(reply)
| ArchivedStatus::PermanentFailure(reply) => {
format_archived_error_details(reply)
}
ArchivedStatus::Scheduled => "250 2.1.5 Queued".to_string(),
},
delivered: match &rcpt.status {
ArchivedStatus::Scheduled | ArchivedStatus::TemporaryFailure(_) => {
Delivered::Queued
}
ArchivedStatus::Completed(_) => Delivered::Yes,
ArchivedStatus::PermanentFailure(_) => Delivered::No,
},
displayed: false,
};
}
is_pending = true;
}
let mut result = Object::with_capacity(properties.len());
+24 -24
View File
@@ -511,17 +511,17 @@ impl EmailSubmissionSet for Server {
}
if let ArchivedHeaderValue::Address(addr) = &header.value {
for address in addr.iter() {
if let Some(address) = address.address().and_then(sanitize_email) {
if !rcpt_to.iter().any(|rcpt| rcpt.address == address) {
submission.envelope.rcpt_to.push(Address {
email: address.to_string(),
parameters: None,
});
rcpt_to.push(RcptTo {
address,
..Default::default()
});
}
if let Some(address) = address.address().and_then(sanitize_email)
&& !rcpt_to.iter().any(|rcpt| rcpt.address == address)
{
submission.envelope.rcpt_to.push(Address {
email: address.to_string(),
parameters: None,
});
rcpt_to.push(RcptTo {
address,
..Default::default()
});
}
}
}
@@ -696,19 +696,19 @@ fn parse_envelope_address(
let mut params_list = VecMap::with_capacity(params.0.len());
for (k, v) in params.0 {
if let Property::_T(k) = k {
if !k.is_empty() {
if !params_text.is_empty() {
params_text.push(' ');
}
params_text.push_str(&k);
if let Value::Text(v) = v {
params_text.push('=');
params_text.push_str(&v);
params_list.append(k, Some(v));
} else {
params_list.append(k, None);
}
if let Property::_T(k) = k
&& !k.is_empty()
{
if !params_text.is_empty() {
params_text.push(' ');
}
params_text.push_str(&k);
if let Value::Text(v) = v {
params_text.push('=');
params_text.push_str(&v);
params_list.append(k, Some(v));
} else {
params_list.append(k, None);
}
}
}
+7 -8
View File
@@ -315,14 +315,13 @@ impl VacationResponseSet for Server {
}
} else if !will_destroy.is_empty() {
for id in will_destroy {
if id.is_singleton() {
if let Some(document_id) = self.get_vacation_sieve_script_id(account_id).await?
{
self.sieve_script_delete(&resource_token, document_id, false, &mut batch)
.await?;
response.destroyed.push(id);
continue;
}
if id.is_singleton()
&& let Some(document_id) = self.get_vacation_sieve_script_id(account_id).await?
{
self.sieve_script_delete(&resource_token, document_id, false, &mut batch)
.await?;
response.destroyed.push(id);
continue;
}
response.not_destroyed.append(id, SetError::not_found());
+5 -6
View File
@@ -132,14 +132,13 @@ impl<T: SessionStream> Session<T> {
}
}
if let Some(needs_literal) = needs_literal {
if let Err(err) = self
if let Some(needs_literal) = needs_literal
&& let Err(err) = self
.write(format!("OK Ready for {} bytes.\r\n", needs_literal).as_bytes())
.await
{
trc::error!(err.span_id(self.session_id));
return SessionResult::Close;
}
{
trc::error!(err.span_id(self.session_id));
return SessionResult::Close;
}
SessionResult::Continue
+5 -6
View File
@@ -42,13 +42,12 @@ impl SessionManager for ManageSieveSessionManager {
.is_ok()
&& session.handle_conn().await
&& session.instance.acceptor.is_tls()
&& let Ok(mut session) = session.into_tls().await
{
if let Ok(mut session) = session.into_tls().await {
let _ = session
.write(&session.handle_capability(SERVER_GREETING).await.unwrap())
.await;
session.handle_conn().await;
}
let _ = session
.write(&session.handle_capability(SERVER_GREETING).await.unwrap())
.await;
session.handle_conn().await;
}
}
}
+16 -16
View File
@@ -236,27 +236,27 @@ fn convert_envelope(value: &Value) -> Envelope {
}
fn convert_envelope_address(envelope: &Value) -> Option<Address> {
if let Value::Object(envelope) = envelope {
if let (Value::Text(email), Value::Object(params)) = (
if let Value::Object(envelope) = envelope
&& let (Value::Text(email), Value::Object(params)) = (
envelope.get(&Property::Email),
envelope.get(&Property::Parameters),
) {
let mut addr = Address {
email: email.to_string(),
parameters: None,
};
for (k, v) in params.0.iter() {
if let Property::_T(k) = &k {
if !k.is_empty() {
let k = k.to_string();
let v = v.as_string().map(|s| s.to_string());
)
{
let mut addr = Address {
email: email.to_string(),
parameters: None,
};
for (k, v) in params.0.iter() {
if let Property::_T(k) = &k
&& !k.is_empty()
{
let k = k.to_string();
let v = v.as_string().map(|s| s.to_string());
addr.parameters.get_or_insert_default().append(k, v);
}
}
addr.parameters.get_or_insert_default().append(k, v);
}
return Some(addr);
}
return Some(addr);
}
None
+1 -1
View File
@@ -13,7 +13,7 @@ pub struct SpaceTokenizer<'x> {
}
impl SpaceTokenizer<'_> {
pub fn new(text: &str, max_token_length: usize) -> SpaceTokenizer {
pub fn new(text: &'_ str, max_token_length: usize) -> SpaceTokenizer<'_> {
SpaceTokenizer {
iterator: text.chars(),
token: String::new(),
+6 -5
View File
@@ -88,11 +88,12 @@ impl<'x> Iterator for TypesTokenizer<'x> {
}
// Try parsing currencies and floating point numbers
if self.tokenize_numbers && !last_is_dot {
if let Some(num) = self.try_parse_number() {
self.peek_advance();
return Some(num);
}
if self.tokenize_numbers
&& !last_is_dot
&& let Some(num) = self.try_parse_number()
{
self.peek_advance();
return Some(num);
}
self.peek_rewind();
+1 -1
View File
@@ -15,7 +15,7 @@ pub struct WordTokenizer<'x> {
}
impl WordTokenizer<'_> {
pub fn new(text: &str, max_token_length: usize) -> WordTokenizer {
pub fn new(text: &'_ str, max_token_length: usize) -> WordTokenizer<'_> {
WordTokenizer {
max_token_length,
text,
+5 -8
View File
@@ -49,10 +49,9 @@ impl SessionManager for Pop3SessionManager {
.is_ok()
&& session.handle_conn().await
&& session.instance.acceptor.is_tls()
&& let Ok(mut session) = session.into_tls().await
{
if let Ok(mut session) = session.into_tls().await {
session.handle_conn().await;
}
session.handle_conn().await;
}
}
}
@@ -190,11 +189,9 @@ impl<T: SessionStream> Session<T> {
trc::error!(err.span_id(self.session_id));
if write_err {
if let Err(err) = self.write_bytes(response).await {
trc::error!(err.span_id(self.session_id));
return false;
}
if write_err && let Err(err) = self.write_bytes(response).await {
trc::error!(err.span_id(self.session_id));
return false;
}
!disconnect
+27 -28
View File
@@ -98,11 +98,11 @@ pub fn spawn_housekeeper(inner: Arc<Inner>, mut rx: mpsc::Receiver<HousekeeperEv
}
// OTEL Push Metrics
if server.core.network.roles.push_metrics {
if let Some(otel) = &server.core.metrics.otel {
OtelMetrics::enable_errors();
queue.schedule(Instant::now() + otel.interval, ActionClass::OtelMetrics);
}
if server.core.network.roles.push_metrics
&& let Some(otel) = &server.core.metrics.otel
{
OtelMetrics::enable_errors();
queue.schedule(Instant::now() + otel.interval, ActionClass::OtelMetrics);
}
// Calculate expensive metrics
@@ -199,13 +199,13 @@ pub fn spawn_housekeeper(inner: Arc<Inner>, mut rx: mpsc::Receiver<HousekeeperEv
);
}
if let Some(metrics_store) = enterprise.metrics_store.as_ref() {
if !queue.has_action(&ActionClass::InternalMetrics) {
queue.schedule(
Instant::now() + metrics_store.interval.time_to_next(),
ActionClass::InternalMetrics,
);
}
if let Some(metrics_store) = enterprise.metrics_store.as_ref()
&& !queue.has_action(&ActionClass::InternalMetrics)
{
queue.schedule(
Instant::now() + metrics_store.interval.time_to_next(),
ActionClass::InternalMetrics,
);
}
if !enterprise.metrics_alerts.is_empty()
@@ -719,17 +719,17 @@ impl Purge for Server {
// SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
// SPDX-License-Identifier: LicenseRef-SEL
#[cfg(feature = "enterprise")]
if let Some(trace_retention) = trace_retention {
if let Err(err) = store.purge_spans(trace_retention).await {
trc::error!(err.details("Failed to purge tracing spans"));
}
if let Some(trace_retention) = trace_retention
&& let Err(err) = store.purge_spans(trace_retention).await
{
trc::error!(err.details("Failed to purge tracing spans"));
}
#[cfg(feature = "enterprise")]
if let Some(metrics_retention) = metrics_retention {
if let Err(err) = store.purge_metrics(metrics_retention).await {
trc::error!(err.details("Failed to purge metrics"));
}
if let Some(metrics_retention) = metrics_retention
&& let Err(err) = store.purge_metrics(metrics_retention).await
{
trc::error!(err.details("Failed to purge metrics"));
}
// SPDX-SnippetEnd
}
@@ -767,17 +767,16 @@ impl Purge for Server {
);
// Remove lock
if let Some(lock_name) = &lock_name {
if let Err(err) = self
if let Some(lock_name) = &lock_name
&& let Err(err) = self
.in_memory_store()
.remove_lock(KV_LOCK_HOUSEKEEPER, lock_name)
.await
{
trc::error!(
err.details("Failed to delete task lock.")
.details(lock_type)
);
}
{
trc::error!(
err.details("Failed to delete task lock.")
.details(lock_type)
);
}
}
}
+22 -26
View File
@@ -75,14 +75,12 @@ pub fn spawn_state_manager(inner: Arc<Inner>, mut change_rx: mpsc::Receiver<Stat
.access_to
.iter()
.any(|(id, _)| *id == *shared_account_id)
{
if let Some(shared_list) =
&& let Some(shared_list) =
shared_accounts_map.get_mut(shared_account_id)
{
shared_list.remove(&account_id);
if shared_list.is_empty() {
shared_accounts_map.remove(shared_account_id);
}
{
shared_list.remove(&account_id);
if shared_list.is_empty() {
shared_accounts_map.remove(shared_account_id);
}
}
}
@@ -140,20 +138,18 @@ pub fn spawn_state_manager(inner: Arc<Inner>, mut change_rx: mpsc::Receiver<Stat
broadcast,
} => {
// Publish event to cluster
if broadcast {
if let Some(broadcast_tx) = &inner.ipc.broadcast_tx.clone() {
if broadcast_tx
.send(BroadcastEvent::StateChange(state_change))
.await
.is_err()
{
trc::event!(
Server(trc::ServerEvent::ThreadError),
Details = "Error sending broadcast event.",
CausedBy = trc::location!()
);
}
}
if broadcast
&& let Some(broadcast_tx) = &inner.ipc.broadcast_tx.clone()
&& broadcast_tx
.send(BroadcastEvent::StateChange(state_change))
.await
.is_err()
{
trc::event!(
Server(trc::ServerEvent::ThreadError),
Details = "Error sending broadcast event.",
CausedBy = trc::location!()
);
}
if let Some(shared_accounts) = shared_accounts_map.get(&state_change.account_id)
@@ -247,14 +243,14 @@ pub fn spawn_state_manager(inner: Arc<Inner>, mut change_rx: mpsc::Receiver<Stat
let mut remove_ids = Vec::new();
for subscriber_id in subscribers.keys() {
if let SubscriberId::Push(push_id) = subscriber_id {
if !subscriptions.iter().any(|s| {
if let SubscriberId::Push(push_id) = subscriber_id
&& !subscriptions.iter().any(|s| {
matches!(s, UpdateSubscription::Verified(
PushSubscription { id, .. }
) if id == push_id)
}) {
remove_ids.push(*subscriber_id);
}
})
{
remove_ids.push(*subscriber_id);
}
}
+63 -66
View File
@@ -476,82 +476,79 @@ pub async fn build_itip_template(
if let Some(guests) = fields
.iter()
.find(|e| e.name == ICalendarProperty::Attendee)
&& let ArchivedItipValue::Participants(guests) = &guests.value
{
if let ArchivedItipValue::Participants(guests) = &guests.value {
variables.insert_single(
CalendarTemplateVariable::AttendeesTitle,
locale.calendar_attendees.to_string(),
);
variables.insert_block(
CalendarTemplateVariable::Attendees,
guests.iter().map(|guest| {
[
(
CalendarTemplateVariable::Key,
if guest.is_organizer {
if let Some(name) = guest.name.as_ref() {
format!("{name} - {}", locale.calendar_organizer)
} else {
locale.calendar_organizer.to_string()
}
variables.insert_single(
CalendarTemplateVariable::AttendeesTitle,
locale.calendar_attendees.to_string(),
);
variables.insert_block(
CalendarTemplateVariable::Attendees,
guests.iter().map(|guest| {
[
(
CalendarTemplateVariable::Key,
if guest.is_organizer {
if let Some(name) = guest.name.as_ref() {
format!("{name} - {}", locale.calendar_organizer)
} else {
guest
.name
.as_ref()
.map(|n| n.as_str())
.unwrap_or_default()
.to_string()
},
),
(CalendarTemplateVariable::Value, guest.email.to_string()),
]
}),
);
}
locale.calendar_organizer.to_string()
}
} else {
guest
.name
.as_ref()
.map(|n| n.as_str())
.unwrap_or_default()
.to_string()
},
),
(CalendarTemplateVariable::Value, guest.email.to_string()),
]
}),
);
}
// Add RSVP buttons
if matches!(
summary,
ArchivedItipSummary::Invite(_) | ArchivedItipSummary::Update { .. }
) {
if let Some(rsvp_url) = server
.http_rsvp_url(task.account_id, task.document_id, to)
.await
{
variables.insert_single(
CalendarTemplateVariable::Rsvp,
locale.calendar_reply_as.replace("$name", to),
);
variables.insert_block(
CalendarTemplateVariable::Actions,
) && let Some(rsvp_url) = server
.http_rsvp_url(task.account_id, task.document_id, to)
.await
{
variables.insert_single(
CalendarTemplateVariable::Rsvp,
locale.calendar_reply_as.replace("$name", to),
);
variables.insert_block(
CalendarTemplateVariable::Actions,
[
(
ICalendarParticipationStatus::Accepted,
locale.calendar_yes.to_string(),
"info",
),
(
ICalendarParticipationStatus::Declined,
locale.calendar_no.to_string(),
"danger",
),
(
ICalendarParticipationStatus::Tentative,
locale.calendar_maybe.to_string(),
"warning",
),
]
.into_iter()
.map(|(status, title, color)| {
[
(
ICalendarParticipationStatus::Accepted,
locale.calendar_yes.to_string(),
"info",
),
(
ICalendarParticipationStatus::Declined,
locale.calendar_no.to_string(),
"danger",
),
(
ICalendarParticipationStatus::Tentative,
locale.calendar_maybe.to_string(),
"warning",
),
(CalendarTemplateVariable::ActionName, title.to_string()),
(CalendarTemplateVariable::ActionUrl, rsvp_url.url(&status)),
(CalendarTemplateVariable::Color, color.to_string()),
]
.into_iter()
.map(|(status, title, color)| {
[
(CalendarTemplateVariable::ActionName, title.to_string()),
(CalendarTemplateVariable::ActionUrl, rsvp_url.url(&status)),
(CalendarTemplateVariable::Color, color.to_string()),
]
}),
);
}
}),
);
}
// Add footer
+25 -25
View File
@@ -388,38 +388,38 @@ impl<T: SessionStream> Session<T> {
}
// Add Received-SPF header
if let Some(spf_output) = &self.data.spf_mail_from {
if self
if let Some(spf_output) = &self.data.spf_mail_from
&& self
.server
.eval_if(&dc.add_received_spf, self, self.data.session_id)
.await
.unwrap_or(true)
{
ReceivedSpf::new(
spf_output,
self.data.remote_ip,
&self.data.helo_domain,
&mail_from.address_lcase,
&self.hostname,
)
.write_header(&mut headers);
}
{
ReceivedSpf::new(
spf_output,
self.data.remote_ip,
&self.data.helo_domain,
&mail_from.address_lcase,
&self.hostname,
)
.write_header(&mut headers);
}
// ARC Seal
if let (Some(arc_sealer), Some(arc_output)) = (arc_sealer, &arc_output) {
if !dkim_output.is_empty() && arc_output.can_be_sealed() {
match arc_sealer.seal(&auth_message, &auth_results, arc_output) {
Ok(set) => {
set.write_header(&mut headers);
}
Err(err) => {
trc::error!(
trc::Error::from(err)
.span_id(self.data.session_id)
.details("Failed to ARC seal message")
);
}
if let (Some(arc_sealer), Some(arc_output)) = (arc_sealer, &arc_output)
&& !dkim_output.is_empty()
&& arc_output.can_be_sealed()
{
match arc_sealer.seal(&auth_message, &auth_results, arc_output) {
Ok(set) => {
set.write_header(&mut headers);
}
Err(err) => {
trc::error!(
trc::Error::from(err)
.span_id(self.data.session_id)
.details("Failed to ARC seal message")
);
}
}
}
+6 -8
View File
@@ -98,20 +98,18 @@ impl<T: SessionStream> Session<T> {
.get_trusted_sieve_script(&name, self.data.session_id)
.map(|s| (s, name))
})
{
if let ScriptResult::Reject(message) = self
&& let ScriptResult::Reject(message) = self
.run_script(
script_id,
script.clone(),
self.build_script_parameters("ehlo"),
)
.await
{
self.data.mail_from = None;
self.data.helo_domain = prev_helo_domain;
self.data.spf_ehlo = None;
return self.write(message.as_bytes()).await;
}
{
self.data.mail_from = None;
self.data.helo_domain = prev_helo_domain;
self.data.spf_ehlo = None;
return self.write(message.as_bytes()).await;
}
// Milter filtering
+6 -9
View File
@@ -47,10 +47,9 @@ impl SessionManager for SmtpSessionManager {
&& session.init_conn().await
&& session.handle_conn().await
&& session.instance.acceptor.is_tls()
&& let Ok(mut session) = session.into_tls().await
{
if let Ok(mut session) = session.into_tls().await {
session.handle_conn().await;
}
session.handle_conn().await;
}
}
@@ -89,18 +88,16 @@ impl<T: SessionStream> Session<T> {
.get_trusted_sieve_script(&name, self.data.session_id)
.map(|s| (s, name))
})
{
if let ScriptResult::Reject(message) = self
&& let ScriptResult::Reject(message) = self
.run_script(
script_id,
script.clone(),
self.build_script_parameters("connect"),
)
.await
{
let _ = self.write(message.as_bytes()).await;
return false;
}
{
let _ = self.write(message.as_bytes()).await;
return false;
}
// Milter filtering
+26 -26
View File
@@ -1029,36 +1029,36 @@ impl QueuedMessage {
);
// Verify DANE
if let Some(dane_policy) = &dane_policy {
if let Err(status) = dane_policy.verify(
if let Some(dane_policy) = &dane_policy
&& let Err(status) = dane_policy.verify(
message.span_id,
envelope.mx,
smtp_client.tls_connection().peer_certificates(),
) {
// Report DANE verification failure
if let Some(tls_report) = &tls_report {
server
.schedule_report(TlsEvent {
policy: dane_policy.into(),
domain: domain.to_string(),
failure: FailureDetails::new(
ResultType::ValidationFailure,
)
.with_receiving_mx_hostname(envelope.mx)
.with_receiving_ip(remote_ip)
.with_failure_reason_code(
"No matching certificates found.",
)
.into(),
tls_record: tls_report.record.clone(),
interval: tls_report.interval,
})
.await;
}
last_status = status;
continue 'next_host;
)
{
// Report DANE verification failure
if let Some(tls_report) = &tls_report {
server
.schedule_report(TlsEvent {
policy: dane_policy.into(),
domain: domain.to_string(),
failure: FailureDetails::new(
ResultType::ValidationFailure,
)
.with_receiving_mx_hostname(envelope.mx)
.with_receiving_ip(remote_ip)
.with_failure_reason_code(
"No matching certificates found.",
)
.into(),
tls_record: tls_report.record.clone(),
interval: tls_report.interval,
})
.await;
}
last_status = status;
continue 'next_host;
}
// Report TLS success
+4 -4
View File
@@ -59,10 +59,10 @@ impl MtaStsLookup for Server {
};
// Check if the policy has been cached
if let Some(value) = self.inner.cache.dbs_mta_sts.get(domain) {
if value.id == record.id {
return Ok(value);
}
if let Some(value) = self.inner.cache.dbs_mta_sts.get(domain)
&& value.id == record.id
{
return Ok(value);
}
// Fetch policy
+4 -4
View File
@@ -22,10 +22,10 @@ impl VerifyPolicy for Policy {
}
}
MxPattern::StartsWith(domain) => {
if let Some((_, suffix)) = mx_host.split_once('.') {
if suffix == domain {
return true;
}
if let Some((_, suffix)) = mx_host.split_once('.')
&& suffix == domain
{
return true;
}
}
}
+17 -17
View File
@@ -396,13 +396,13 @@ impl MessageWrapper {
let now = now();
for rcpt in &mut self.message.recipients {
if !rcpt.has_flag(RCPT_DSN_SENT | RCPT_NOTIFY_NEVER) {
if let Status::PermanentFailure(err) = &rcpt.status {
rcpt.flags |= RCPT_DSN_SENT;
let mut dsn = String::new();
err.write_dsn_text(&rcpt.address, &mut dsn);
is_double_bounce.push(dsn);
}
if !rcpt.has_flag(RCPT_DSN_SENT | RCPT_NOTIFY_NEVER)
&& let Status::PermanentFailure(err) = &rcpt.status
{
rcpt.flags |= RCPT_DSN_SENT;
let mut dsn = String::new();
err.write_dsn_text(&rcpt.address, &mut dsn);
is_double_bounce.push(dsn);
}
if rcpt.notify.due <= now {
@@ -529,12 +529,12 @@ impl Recipient {
}
fn write_dsn_will_retry_until(&self, created: u64, dsn: &mut String) {
if let Some(expires) = self.expiration_time(created) {
if expires > now() {
dsn.push_str("Will-Retry-Until: ");
dsn.push_str(&DateTime::from_timestamp(expires as i64).to_rfc822());
dsn.push_str("\r\n");
}
if let Some(expires) = self.expiration_time(created)
&& expires > now()
{
dsn.push_str("Will-Retry-Until: ");
dsn.push_str(&DateTime::from_timestamp(expires as i64).to_rfc822());
dsn.push_str("\r\n");
}
}
}
@@ -624,10 +624,10 @@ impl Status<HostResponse<String>, ErrorDetails> {
}
fn write_dsn_diagnostic(&self, dsn: &mut String) {
if let Status::PermanentFailure(err) | Status::TemporaryFailure(err) = self {
if let Error::UnexpectedResponse(response) = &err.details {
response.response.write_dsn_diagnostic(dsn);
}
if let Status::PermanentFailure(err) | Status::TemporaryFailure(err) = self
&& let Error::UnexpectedResponse(response) = &err.details
{
response.response.write_dsn_diagnostic(dsn);
}
}
}
+4 -4
View File
@@ -303,10 +303,10 @@ impl TlsReporting for Server {
continue;
};
if let Some(serialized_size) = serialized_size.as_deref_mut() {
if serde::Serialize::serialize(&tls, serialized_size).is_err() {
continue;
}
if let Some(serialized_size) = serialized_size.as_deref_mut()
&& serde::Serialize::serialize(&tls, serialized_size).is_err()
{
continue;
}
// Group duplicates
+14 -13
View File
@@ -24,21 +24,22 @@ pub trait SpamFilterAnalyzeBayes: Sync + Send {
impl SpamFilterAnalyzeBayes for Server {
async fn spam_filter_analyze_bayes_classify(&self, ctx: &mut SpamFilterContext<'_>) {
if let Some(config) = &self.core.spam.bayes {
if !ctx.result.has_tag("SPAM_TRAP") && !ctx.result.has_tag("TRUSTED_REPLY") {
match self.bayes_classify(ctx).await {
Ok(Some(score)) => {
if score > config.score_spam {
ctx.result.add_tag("BAYES_SPAM");
} else if score < config.score_ham {
ctx.result.add_tag("BAYES_HAM");
}
}
Ok(None) => (),
Err(err) => {
trc::error!(err.span_id(ctx.input.span_id).caused_by(trc::location!()));
if let Some(config) = &self.core.spam.bayes
&& !ctx.result.has_tag("SPAM_TRAP")
&& !ctx.result.has_tag("TRUSTED_REPLY")
{
match self.bayes_classify(ctx).await {
Ok(Some(score)) => {
if score > config.score_spam {
ctx.result.add_tag("BAYES_SPAM");
} else if score < config.score_ham {
ctx.result.add_tag("BAYES_HAM");
}
}
Ok(None) => (),
Err(err) => {
trc::error!(err.span_id(ctx.input.span_id).caused_by(trc::location!()));
}
}
}
}
+11 -12
View File
@@ -41,13 +41,13 @@ impl SpamFilterAnalyzeDomain for Server {
// Add DKIM domains
for dkim in ctx.input.dkim_result {
if dkim.result() == &DkimResult::Pass {
if let Some(domain) = dkim.signature().map(|s| &s.d) {
domains.insert(ElementLocation::new(
CompactString::from_str_to_lowercase(domain),
Location::HeaderDkimPass,
));
}
if dkim.result() == &DkimResult::Pass
&& let Some(domain) = dkim.signature().map(|s| &s.d)
{
domains.insert(ElementLocation::new(
CompactString::from_str_to_lowercase(domain),
Location::HeaderDkimPass,
));
}
}
@@ -59,11 +59,10 @@ impl SpamFilterAnalyzeDomain for Server {
.into_iter()
.flatten()
{
if let Host::Name(name) = host {
if let Some(name) = Hostname::new(name.as_ref()).sld {
domains
.insert(ElementLocation::new(name, Location::HeaderReceived));
}
if let Host::Name(name) = host
&& let Some(name) = Hostname::new(name.as_ref()).sld
{
domains.insert(ElementLocation::new(name, Location::HeaderReceived));
}
}
}
+21 -22
View File
@@ -86,8 +86,8 @@ impl SpamFilterAnalyzeFrom for Server {
ctx.result.add_tag("FROM_HAS_DN");
}
if from_name_trimmed.contains('@') {
if let Some(from_name_addr) = TypesTokenizer::new(from_name_trimmed)
if from_name_trimmed.contains('@')
&& let Some(from_name_addr) = TypesTokenizer::new(from_name_trimmed)
.tokenize_numbers(false)
.tokenize_urls(false)
.tokenize_urls_without_scheme(false)
@@ -100,19 +100,18 @@ impl SpamFilterAnalyzeFrom for Server {
_ => None,
})
.next()
{
if (from_addr_is_valid
&& from_name_addr.domain_part.sld != from_addr.domain_part.sld)
|| (!env_from_empty
&& ctx.output.env_from_addr.domain_part.sld
!= from_name_addr.domain_part.sld)
|| (env_from_empty
&& ctx.output.ehlo_host.sld != from_name_addr.domain_part.sld)
{
if (from_addr_is_valid
&& from_name_addr.domain_part.sld != from_addr.domain_part.sld)
|| (!env_from_empty
&& ctx.output.env_from_addr.domain_part.sld
!= from_name_addr.domain_part.sld)
|| (env_from_empty
&& ctx.output.ehlo_host.sld != from_name_addr.domain_part.sld)
{
ctx.result.add_tag("SPOOF_DISPLAY_NAME");
} else {
ctx.result.add_tag("FROM_NEQ_DISPLAY_NAME");
}
ctx.result.add_tag("SPOOF_DISPLAY_NAME");
} else {
ctx.result.add_tag("FROM_NEQ_DISPLAY_NAME");
}
}
}
@@ -188,10 +187,10 @@ impl SpamFilterAnalyzeFrom for Server {
}
// Check whether read confirmation address is different to from address
if let Some(crt) = crt {
if crt != from_addr.address {
ctx.result.add_tag("HEADER_RCONFIRM_MISMATCH");
}
if let Some(crt) = crt
&& crt != from_addr.address
{
ctx.result.add_tag("HEADER_RCONFIRM_MISMATCH");
}
}
@@ -216,10 +215,10 @@ impl SpamFilterAnalyzeFrom for Server {
}
// Check whether disposition notification address is different to return path
if let Some(dnt) = dnt {
if dnt != ctx.output.env_from_addr.address {
ctx.result.add_tag("HEADER_FORGED_MDN");
}
if let Some(dnt) = dnt
&& dnt != ctx.output.env_from_addr.address
{
ctx.result.add_tag("HEADER_FORGED_MDN");
}
}
}
+10 -8
View File
@@ -38,19 +38,21 @@ impl SpamFilterAnalyzeIp for Server {
if let (HeaderName::Received, HeaderValue::Received(received)) =
(&header.name, &header.value)
{
if let Some(ip) = received.from_ip() {
if !ip.is_loopback() && !self.is_ip_allowed(&ip) {
ips.insert(ElementLocation::new(ip, Location::HeaderReceived));
}
if let Some(ip) = received.from_ip()
&& !ip.is_loopback()
&& !self.is_ip_allowed(&ip)
{
ips.insert(ElementLocation::new(ip, Location::HeaderReceived));
}
for host in [&received.from, &received.helo, &received.by]
.into_iter()
.flatten()
{
if let Host::IpAddr(ip) = host {
if !ip.is_loopback() && !self.is_ip_allowed(ip) {
ips.insert(ElementLocation::new(*ip, Location::HeaderReceived));
}
if let Host::IpAddr(ip) = host
&& !ip.is_loopback()
&& !self.is_ip_allowed(ip)
{
ips.insert(ElementLocation::new(*ip, Location::HeaderReceived));
}
}
}
+9 -9
View File
@@ -366,15 +366,15 @@ impl SpamFilterAnalyzeMime for Server {
if is_attachment {
// Has a MIME attachment
ctx.result.add_tag("HAS_ATTACHMENT");
if ct_full != "application/octet-stream" {
if let Some(t) = infer::get(part.contents()) {
if t.mime_type() == ct_full {
// Known content-type
ctx.result.add_tag("MIME_GOOD");
} else {
// Known bad content-type
ctx.result.add_tag("MIME_BAD");
}
if ct_full != "application/octet-stream"
&& let Some(t) = infer::get(part.contents())
{
if t.mime_type() == ct_full {
// Known content-type
ctx.result.add_tag("MIME_GOOD");
} else {
// Known bad content-type
ctx.result.add_tag("MIME_BAD");
}
}
}
+8 -10
View File
@@ -48,22 +48,20 @@ impl SpamFilterAnalyzeReceived for Server {
ctx.result.add_tag("RCVD_HELO_USER");
} else if let (Some(Host::Name(helo_domain)), Some(ip_rev)) =
(helo_domain, ip_rev)
&& helo_domain.to_lowercase() != ip_rev.to_lowercase()
{
if helo_domain.to_lowercase() != ip_rev.to_lowercase() {
// HELO domain does not match PTR record
ctx.result.add_tag("FORGED_RCVD_TRAIL");
}
// HELO domain does not match PTR record
ctx.result.add_tag("FORGED_RCVD_TRAIL");
}
if let Some(delivered_for) = received.for_().map(|s| s.to_lowercase()) {
if ctx
if let Some(delivered_for) = received.for_().map(|s| s.to_lowercase())
&& ctx
.output
.all_recipients()
.any(|r| r.email.address == delivered_for)
{
// Recipient appears on Received trail
ctx.result.add_tag("PREVIOUSLY_DELIVERED");
}
{
// Recipient appears on Received trail
ctx.result.add_tag("PREVIOUSLY_DELIVERED");
}
if matches!(received.from, Some(Host::IpAddr(_))) {
+5 -5
View File
@@ -101,11 +101,11 @@ impl SpamFilterAnalyzeRecipient for Server {
// Check for spaces in recipient addresses
for token in raw_utf8.split('<') {
if let Some((addr, _)) = token.split_once('>') {
if addr.starts_with(' ') || addr.ends_with(' ') {
ctx.result.add_tag("TO_WRAPPED_IN_SPACES");
break;
}
if let Some((addr, _)) = token.split_once('>')
&& (addr.starts_with(' ') || addr.ends_with(' '))
{
ctx.result.add_tag("TO_WRAPPED_IN_SPACES");
break;
}
}
}
@@ -64,17 +64,15 @@ impl SpamFilterAnalyzeTrustedReply for Server {
if let (Some(hold_time), Some(message_id)) = (
self.core.spam.expiry.trusted_reply,
ctx.input.message.message_id(),
) {
if let Err(err) = self
.in_memory_store()
.key_set(
KeyValue::with_prefix(KV_TRUSTED_REPLY, message_id.as_bytes(), vec![])
.expires(hold_time),
)
.await
{
trc::error!(err.span_id(ctx.input.span_id).caused_by(trc::location!()));
}
) && let Err(err) = self
.in_memory_store()
.key_set(
KeyValue::with_prefix(KV_TRUSTED_REPLY, message_id.as_bytes(), vec![])
.expires(hold_time),
)
.await
{
trc::error!(err.span_id(ctx.input.span_id).caused_by(trc::location!()));
}
if self
+9 -8
View File
@@ -96,14 +96,15 @@ impl SpamFilterAnalyzeUrl for Server {
for token in tokens {
match token {
TokenType::Url(url) | TokenType::UrlNoScheme(url) => {
if is_body && !ctx.result.has_tag("RCPT_DOMAIN_IN_BODY") {
if let Some(url_parsed) = &url.url_parsed {
let host = url_parsed.host.sld_or_default();
for rcpt in ctx.output.all_recipients() {
if rcpt.email.domain_part.sld_or_default() == host {
ctx.result.add_tag("RCPT_DOMAIN_IN_BODY");
break;
}
if is_body
&& !ctx.result.has_tag("RCPT_DOMAIN_IN_BODY")
&& let Some(url_parsed) = &url.url_parsed
{
let host = url_parsed.host.sld_or_default();
for rcpt in ctx.output.all_recipients() {
if rcpt.email.domain_part.sld_or_default() == host {
ctx.result.add_tag("RCPT_DOMAIN_IN_BODY");
break;
}
}
}
+5 -5
View File
@@ -51,8 +51,9 @@ pub(crate) async fn check_dnsbl(
};
for dnsbl in &server.core.spam.dnsbl.servers {
if dnsbl.scope == scope && checks < max_checks {
if let Some(tag) = is_dnsbl(
if dnsbl.scope == scope
&& checks < max_checks
&& let Some(tag) = is_dnsbl(
server,
dnsbl,
SpamFilterResolver::new(ctx, resolver, location),
@@ -60,9 +61,8 @@ pub(crate) async fn check_dnsbl(
&mut checks,
)
.await
{
ctx.result.add_tag(tag);
}
{
ctx.result.add_tag(tag);
}
}
+7 -7
View File
@@ -320,13 +320,13 @@ fn html_to_text(input: &str) -> String {
continue;
}
b'>' if in_tag => {
if tag_token_pos == 1 {
if let Some(tag) = input.get(token_start..token_end + 1) {
if tag.eq_ignore_ascii_case(b"style") {
in_style = !is_tag_close;
} else if tag.eq_ignore_ascii_case(b"script") {
in_script = !is_tag_close;
}
if tag_token_pos == 1
&& let Some(tag) = input.get(token_start..token_end + 1)
{
if tag.eq_ignore_ascii_case(b"style") {
in_style = !is_tag_close;
} else if tag.eq_ignore_ascii_case(b"script") {
in_script = !is_tag_close;
}
}
+2 -4
View File
@@ -78,10 +78,8 @@ impl MysqlStore {
conn_pool: Pool::new(opts),
};
if create_tables {
if let Err(err) = db.create_tables().await {
config.new_build_error(prefix.as_str(), format!("Failed to create tables: {err}"));
}
if create_tables && let Err(err) = db.create_tables().await {
config.new_build_error(prefix.as_str(), format!("Failed to create tables: {err}"));
}
Some(db)
+2 -4
View File
@@ -65,10 +65,8 @@ impl PostgresStore {
.ok()?,
};
if create_tables {
if let Err(err) = db.create_tables().await {
config.new_build_error(prefix.as_str(), format!("Failed to create tables: {err}"));
}
if create_tables && let Err(err) = db.create_tables().await {
config.new_build_error(prefix.as_str(), format!("Failed to create tables: {err}"));
}
Some(db)
+4 -4
View File
@@ -431,10 +431,10 @@ impl IsActiveStore for Config {
"tracing.history.store",
"metrics.history.store",
] {
if let Some(store_id) = self.value(key) {
if store_id == id {
return true;
}
if let Some(store_id) = self.value(key)
&& store_id == id
{
return true;
}
}
+4 -4
View File
@@ -71,10 +71,10 @@ impl JsonQueryable for serde_json::Value {
) {
match pointer.next() {
Some(JsonPointerItem::String(n)) => {
if let serde_json::Value::Object(map) = self {
if let Some(v) = map.get(n) {
v.eval_pointer(pointer, results);
}
if let serde_json::Value::Object(map) = self
&& let Some(v) = map.get(n)
{
v.eval_pointer(pointer, results);
}
}
Some(JsonPointerItem::Number(n)) => match self {
+14 -16
View File
@@ -504,33 +504,31 @@ impl AssertResult for Vec<String> {
}
fn into_response_code(self) -> String {
if let Some((_, code)) = self.last().unwrap().split_once('[') {
if let Some((code, _)) = code.split_once(']') {
return code.to_string();
}
if let Some((_, code)) = self.last().unwrap().split_once('[')
&& let Some((code, _)) = code.split_once(']')
{
return code.to_string();
}
panic!("No response code found in {:?}", self.last().unwrap());
}
fn into_append_uid(self) -> String {
if let Some((_, code)) = self.last().unwrap().split_once("[APPENDUID ") {
if let Some((code, _)) = code.split_once(']') {
if let Some((_, uid)) = code.split_once(' ') {
return uid.to_string();
}
}
if let Some((_, code)) = self.last().unwrap().split_once("[APPENDUID ")
&& let Some((code, _)) = code.split_once(']')
&& let Some((_, uid)) = code.split_once(' ')
{
return uid.to_string();
}
panic!("No APPENDUID found in {:?}", self.last().unwrap());
}
fn into_copy_uid(self) -> String {
for line in &self {
if let Some((_, code)) = line.split_once("[COPYUID ") {
if let Some((code, _)) = code.split_once(']') {
if let Some((_, uid)) = code.rsplit_once(' ') {
return uid.to_string();
}
}
if let Some((_, code)) = line.split_once("[COPYUID ")
&& let Some((code, _)) = code.split_once(']')
&& let Some((_, uid)) = code.rsplit_once(' ')
{
return uid.to_string();
}
}
panic!("No COPYUID found in {:?}", self);
+4 -4
View File
@@ -72,10 +72,10 @@ impl AssertConfig for utils::config::Config {
pub fn enable_logging() {
use common::config::telemetry::Telemetry;
if let Ok(level) = std::env::var("LOG") {
if !Collector::is_enabled() {
Telemetry::test_tracer(level.parse().expect("Invalid log level"));
}
if let Ok(level) = std::env::var("LOG")
&& !Collector::is_enabled()
{
Telemetry::test_tracer(level.parse().expect("Invalid log level"));
}
}

Some files were not shown because too many files have changed in this diff Show More