JMAP Registry API implementation - part 7

This commit is contained in:
mdecimus
2026-03-02 19:26:59 +01:00
parent 9ccc2ed6f0
commit 08f1a8013a
33 changed files with 1843 additions and 927 deletions

View File

@@ -0,0 +1,120 @@
/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::schema::prelude::{ArchivedItem, UTCDateTime};
use types::{blob::BlobId, id::Id};
impl ArchivedItem {
pub fn account_id(&self) -> Id {
match self {
ArchivedItem::Email(i) => i.account_id,
ArchivedItem::FileNode(i) => i.account_id,
ArchivedItem::CalendarEvent(i) => i.account_id,
ArchivedItem::ContactCard(i) => i.account_id,
ArchivedItem::SieveScript(i) => i.account_id,
}
}
pub fn blob_id(&self) -> &BlobId {
match self {
ArchivedItem::Email(i) => &i.blob_id,
ArchivedItem::FileNode(i) => &i.blob_id,
ArchivedItem::CalendarEvent(i) => &i.blob_id,
ArchivedItem::ContactCard(i) => &i.blob_id,
ArchivedItem::SieveScript(i) => &i.blob_id,
}
}
pub fn archived_until(&self) -> UTCDateTime {
match self {
ArchivedItem::Email(i) => i.archived_until,
ArchivedItem::FileNode(i) => i.archived_until,
ArchivedItem::CalendarEvent(i) => i.archived_until,
ArchivedItem::ContactCard(i) => i.archived_until,
ArchivedItem::SieveScript(i) => i.archived_until,
}
}
pub fn created_at(&self) -> UTCDateTime {
match self {
ArchivedItem::Email(i) => i.received_at,
ArchivedItem::FileNode(i) => i.created_at,
ArchivedItem::CalendarEvent(i) => i.created_at,
ArchivedItem::ContactCard(i) => i.created_at,
ArchivedItem::SieveScript(i) => i.created_at,
}
}
pub fn set_account_id(&mut self, value: Id) {
match self {
ArchivedItem::Email(i) => i.account_id = value,
ArchivedItem::FileNode(i) => i.account_id = value,
ArchivedItem::CalendarEvent(i) => i.account_id = value,
ArchivedItem::ContactCard(i) => i.account_id = value,
ArchivedItem::SieveScript(i) => i.account_id = value,
}
}
pub fn set_blob_id(&mut self, value: BlobId) {
match self {
ArchivedItem::Email(i) => i.blob_id = value,
ArchivedItem::FileNode(i) => i.blob_id = value,
ArchivedItem::CalendarEvent(i) => i.blob_id = value,
ArchivedItem::ContactCard(i) => i.blob_id = value,
ArchivedItem::SieveScript(i) => i.blob_id = value,
}
}
pub fn set_archived_until(&mut self, value: UTCDateTime) {
match self {
ArchivedItem::Email(i) => i.archived_until = value,
ArchivedItem::FileNode(i) => i.archived_until = value,
ArchivedItem::CalendarEvent(i) => i.archived_until = value,
ArchivedItem::ContactCard(i) => i.archived_until = value,
ArchivedItem::SieveScript(i) => i.archived_until = value,
}
}
pub fn account_id_mut(&mut self) -> &mut Id {
match self {
ArchivedItem::Email(i) => &mut i.account_id,
ArchivedItem::FileNode(i) => &mut i.account_id,
ArchivedItem::CalendarEvent(i) => &mut i.account_id,
ArchivedItem::ContactCard(i) => &mut i.account_id,
ArchivedItem::SieveScript(i) => &mut i.account_id,
}
}
pub fn blob_id_mut(&mut self) -> &mut BlobId {
match self {
ArchivedItem::Email(i) => &mut i.blob_id,
ArchivedItem::FileNode(i) => &mut i.blob_id,
ArchivedItem::CalendarEvent(i) => &mut i.blob_id,
ArchivedItem::ContactCard(i) => &mut i.blob_id,
ArchivedItem::SieveScript(i) => &mut i.blob_id,
}
}
pub fn archived_until_mut(&mut self) -> &mut UTCDateTime {
match self {
ArchivedItem::Email(i) => &mut i.archived_until,
ArchivedItem::FileNode(i) => &mut i.archived_until,
ArchivedItem::CalendarEvent(i) => &mut i.archived_until,
ArchivedItem::ContactCard(i) => &mut i.archived_until,
ArchivedItem::SieveScript(i) => &mut i.archived_until,
}
}
pub fn into_blob_id(self) -> BlobId {
match self {
ArchivedItem::Email(i) => i.blob_id,
ArchivedItem::FileNode(i) => i.blob_id,
ArchivedItem::CalendarEvent(i) => i.blob_id,
ArchivedItem::ContactCard(i) => i.blob_id,
ArchivedItem::SieveScript(i) => i.blob_id,
}
}
}

View File

@@ -8,6 +8,7 @@ use crate::schema::prelude::Roles;
use types::id::Id;
pub mod account;
pub mod archived_item;
pub mod cron;
pub mod http;
pub mod report;

View File

@@ -5,160 +5,11 @@
*/
use crate::{
schema::{
enums,
prelude::{Property, UTCDateTime},
structs,
},
types::{index::IndexBuilder, ipaddr::IpAddr},
schema::{enums, prelude::UTCDateTime, structs},
types::ipaddr::IpAddr,
};
use mail_auth::report::{tlsrpt::*, *};
use std::borrow::Cow;
use types::id::Id;
pub trait ReportIndex {
fn text(&self) -> impl Iterator<Item = &str>;
fn tenant_id(&self) -> Option<Id>;
fn expires_at(&self) -> u64;
fn domains(&self) -> impl Iterator<Item = &str>;
fn build_search_index<'x>(&'x self, index: &mut IndexBuilder<'x>) {
for text in self.text() {
index.text(Property::Domain, text);
}
if let Some(tenant_id) = self.tenant_id() {
index.search(Property::MemberTenantId, tenant_id.id());
}
index.search(Property::ExpiresAt, self.expires_at());
}
}
impl ReportIndex for structs::ArfExternalReport {
fn domains(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
report
.reported_domains
.iter()
.filter_map(|s| non_empty(s))
.chain(
[report.dkim_domain.as_deref()]
.into_iter()
.flatten()
.filter_map(non_empty),
)
}
fn text(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
report
.reported_domains
.iter()
.filter_map(|s| non_empty(s))
.chain(
[
report.dkim_domain.as_deref(),
report.reporting_mta.as_deref(),
report.original_mail_from.as_deref(),
report.original_rcpt_to.as_deref(),
]
.into_iter()
.flatten()
.filter_map(non_empty),
)
.chain(non_empty(&self.from))
}
fn tenant_id(&self) -> Option<Id> {
self.member_tenant_id
}
fn expires_at(&self) -> u64 {
self.expires_at.timestamp() as u64
}
}
impl ReportIndex for structs::DmarcExternalReport {
fn domains(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
non_empty(&report.policy_domain)
.into_iter()
.filter_map(non_empty)
}
fn text(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
non_empty(&report.email)
.into_iter()
.filter_map(non_empty)
.chain(non_empty(&report.policy_domain))
.chain(report.records.iter().flat_map(|r| {
r.envelope_to
.as_deref()
.into_iter()
.filter_map(non_empty)
.chain(non_empty(&r.envelope_from))
.chain(non_empty(&r.header_from))
.chain(r.dkim_results.iter().filter_map(|d| non_empty(&d.domain)))
.chain(r.spf_results.iter().filter_map(|s| non_empty(&s.domain)))
}))
.chain(non_empty(&self.from))
}
fn tenant_id(&self) -> Option<Id> {
self.member_tenant_id
}
fn expires_at(&self) -> u64 {
self.expires_at.timestamp() as u64
}
}
impl ReportIndex for structs::TlsExternalReport {
fn domains(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
report
.policies
.iter()
.flat_map(|p| non_empty(&p.policy_domain).into_iter())
}
fn text(&self) -> impl Iterator<Item = &str> {
let report = &self.report;
report
.policies
.iter()
.flat_map(|p| {
non_empty(&p.policy_domain)
.into_iter()
.chain(p.mx_hosts.iter().filter_map(|s| non_empty(s)))
.chain(p.failure_details.iter().flat_map(|fd| {
non_empty_opt(&fd.receiving_mx_hostname)
.into_iter()
.chain(non_empty_opt(&fd.receiving_mx_helo))
}))
})
.chain(non_empty(&self.from))
}
fn tenant_id(&self) -> Option<Id> {
self.member_tenant_id
}
fn expires_at(&self) -> u64 {
self.expires_at.timestamp() as u64
}
}
impl From<enums::DmarcAlignment> for Alignment {
fn from(value: enums::DmarcAlignment) -> Self {
@@ -938,13 +789,3 @@ fn fo_to_failure_reporting_options(fo: &Option<String>) -> Vec<enums::FailureRep
.collect(),
}
}
#[inline(always)]
fn non_empty(s: &str) -> Option<&str> {
if s.is_empty() { None } else { Some(s) }
}
#[inline(always)]
fn non_empty_opt(s: &Option<String>) -> Option<&str> {
s.as_deref().filter(|s| !s.is_empty())
}

View File

@@ -18,6 +18,7 @@ impl Task {
Task::MergeThreads(task) => task.status = status,
Task::DmarcReport(task) => task.status = status,
Task::TlsReport(task) => task.status = status,
Task::RestoreArchivedItem(task) => task.status = status,
}
}
@@ -32,6 +33,7 @@ impl Task {
Task::MergeThreads(task) => &task.status,
Task::DmarcReport(task) => &task.status,
Task::TlsReport(task) => &task.status,
Task::RestoreArchivedItem(task) => &task.status,
}
}