325 lines
7.5 KiB
Rust
325 lines
7.5 KiB
Rust
/*
|
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
|
*
|
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
*/
|
|
|
|
use crate::config::smtp::{
|
|
queue::QueueName,
|
|
report::AggregateFrequency,
|
|
resolver::{Policy, Tlsa},
|
|
};
|
|
use ahash::RandomState;
|
|
use mail_auth::{
|
|
dmarc::Dmarc,
|
|
mta_sts::TlsRpt,
|
|
report::{Record, tlsrpt::FailureDetails},
|
|
};
|
|
use registry::{schema::prelude::ObjectType, types::id::ObjectId};
|
|
use std::sync::{
|
|
Arc,
|
|
atomic::{AtomicBool, Ordering},
|
|
};
|
|
use tokio::sync::{Semaphore, SemaphorePermit, mpsc};
|
|
use types::type_state::{DataType, StateChange};
|
|
use utils::map::bitmap::Bitmap;
|
|
|
|
#[derive(Debug)]
|
|
pub enum PushEvent {
|
|
Subscribe {
|
|
account_ids: Vec<u32>,
|
|
types: Bitmap<DataType>,
|
|
tx: mpsc::Sender<PushNotification>,
|
|
},
|
|
Publish {
|
|
notification: PushNotification,
|
|
broadcast: bool,
|
|
},
|
|
PushServerRegister {
|
|
activate: Vec<u32>,
|
|
expired: Vec<u32>,
|
|
},
|
|
PushServerUpdate {
|
|
account_id: u32,
|
|
broadcast: bool,
|
|
},
|
|
Stop,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum PushNotification {
|
|
StateChange(StateChange),
|
|
CalendarAlert(CalendarAlert),
|
|
EmailPush(EmailPush),
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct EmailPush {
|
|
pub account_id: u32,
|
|
pub email_id: u32,
|
|
pub change_id: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct CalendarAlert {
|
|
pub account_id: u32,
|
|
pub event_id: u32,
|
|
pub recurrence_id: Option<i64>,
|
|
pub uid: String,
|
|
pub alert_id: String,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum BroadcastEvent {
|
|
PushNotification(PushNotification),
|
|
PushServerUpdate(u32),
|
|
RegistryChange(RegistryChange),
|
|
CacheInvalidate(Vec<CacheInvalidation>),
|
|
CacheInvalidateAll,
|
|
CacheInvalidateNegative,
|
|
MtaQueueStatus { is_running: bool },
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub enum RegistryChange {
|
|
Insert(ObjectId),
|
|
Delete(ObjectId),
|
|
Reload(ObjectType),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
|
pub enum CacheInvalidation {
|
|
AccessToken(u32),
|
|
DavResources(u32),
|
|
Domain(u32),
|
|
Account(u32),
|
|
DkimSignature(u32),
|
|
Tenant(u32),
|
|
Role(u32),
|
|
List(u32),
|
|
DomainLogo(u32),
|
|
TenantLogo(u32),
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum QueueEvent {
|
|
Refresh,
|
|
WorkerDone {
|
|
queue_id: u64,
|
|
queue_name: QueueName,
|
|
status: QueueEventStatus,
|
|
},
|
|
Paused(bool),
|
|
ReloadSettings,
|
|
Stop,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum QueueEventStatus {
|
|
Completed,
|
|
Locked,
|
|
Deferred,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum ReportingEvent {
|
|
Dmarc(Box<DmarcEvent>),
|
|
Tls(Box<TlsEvent>),
|
|
Stop,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct DmarcEvent {
|
|
pub domain: String,
|
|
pub report_record: Record,
|
|
pub dmarc_record: Arc<Dmarc>,
|
|
pub interval: AggregateFrequency,
|
|
pub span_id: u64,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TlsEvent {
|
|
pub domain: String,
|
|
pub policy: PolicyType,
|
|
pub failure: Option<FailureDetails>,
|
|
pub tls_record: Arc<TlsRpt>,
|
|
pub interval: AggregateFrequency,
|
|
pub span_id: u64,
|
|
}
|
|
|
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
pub enum PolicyType {
|
|
Tlsa(Option<Arc<Tlsa>>),
|
|
Sts(Option<Arc<Policy>>),
|
|
None,
|
|
}
|
|
|
|
pub struct TrainTaskController {
|
|
semaphore: Semaphore,
|
|
stop_flag: AtomicBool,
|
|
}
|
|
|
|
impl Default for TrainTaskController {
|
|
fn default() -> Self {
|
|
Self {
|
|
semaphore: Semaphore::new(1),
|
|
stop_flag: AtomicBool::new(false),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TrainTaskController {
|
|
pub fn try_run(&self) -> Option<SemaphorePermit<'_>> {
|
|
let permit = self.semaphore.try_acquire().ok()?;
|
|
|
|
self.stop_flag.store(false, Ordering::SeqCst);
|
|
|
|
Some(permit)
|
|
}
|
|
|
|
pub fn is_running(&self) -> bool {
|
|
self.semaphore.available_permits() == 0
|
|
}
|
|
|
|
pub fn stop(&self) {
|
|
self.stop_flag.store(true, Ordering::SeqCst);
|
|
}
|
|
|
|
pub fn should_stop(&self) -> bool {
|
|
self.stop_flag.load(Ordering::SeqCst)
|
|
}
|
|
}
|
|
|
|
impl BroadcastEvent {
|
|
pub fn reload(object: ObjectType) -> Self {
|
|
BroadcastEvent::RegistryChange(RegistryChange::Reload(object))
|
|
}
|
|
}
|
|
|
|
pub trait ToHash {
|
|
fn to_hash(&self) -> u64;
|
|
}
|
|
|
|
impl ToHash for Dmarc {
|
|
fn to_hash(&self) -> u64 {
|
|
RandomState::with_seeds(1, 9, 7, 9).hash_one(self)
|
|
}
|
|
}
|
|
|
|
impl ToHash for PolicyType {
|
|
fn to_hash(&self) -> u64 {
|
|
RandomState::with_seeds(1, 9, 7, 9).hash_one(self)
|
|
}
|
|
}
|
|
|
|
impl From<DmarcEvent> for ReportingEvent {
|
|
fn from(value: DmarcEvent) -> Self {
|
|
ReportingEvent::Dmarc(Box::new(value))
|
|
}
|
|
}
|
|
|
|
impl From<TlsEvent> for ReportingEvent {
|
|
fn from(value: TlsEvent) -> Self {
|
|
ReportingEvent::Tls(Box::new(value))
|
|
}
|
|
}
|
|
|
|
impl From<Arc<Tlsa>> for PolicyType {
|
|
fn from(value: Arc<Tlsa>) -> Self {
|
|
PolicyType::Tlsa(Some(value))
|
|
}
|
|
}
|
|
|
|
impl From<Arc<Policy>> for PolicyType {
|
|
fn from(value: Arc<Policy>) -> Self {
|
|
PolicyType::Sts(Some(value))
|
|
}
|
|
}
|
|
|
|
impl From<&Arc<Tlsa>> for PolicyType {
|
|
fn from(value: &Arc<Tlsa>) -> Self {
|
|
PolicyType::Tlsa(Some(value.clone()))
|
|
}
|
|
}
|
|
|
|
impl From<&Arc<Policy>> for PolicyType {
|
|
fn from(value: &Arc<Policy>) -> Self {
|
|
PolicyType::Sts(Some(value.clone()))
|
|
}
|
|
}
|
|
|
|
impl From<(&Option<Arc<Policy>>, &Option<Arc<Tlsa>>)> for PolicyType {
|
|
fn from(value: (&Option<Arc<Policy>>, &Option<Arc<Tlsa>>)) -> Self {
|
|
match value {
|
|
(Some(value), _) => PolicyType::Sts(Some(value.clone())),
|
|
(_, Some(value)) => PolicyType::Tlsa(Some(value.clone())),
|
|
_ => PolicyType::None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PushNotification {
|
|
pub fn account_id(&self) -> u32 {
|
|
match self {
|
|
PushNotification::StateChange(state_change) => state_change.account_id,
|
|
PushNotification::CalendarAlert(calendar_alert) => calendar_alert.account_id,
|
|
PushNotification::EmailPush(email_push) => email_push.account_id,
|
|
}
|
|
}
|
|
|
|
pub fn filter_types(&self, types: &Bitmap<DataType>) -> Option<PushNotification> {
|
|
match self {
|
|
PushNotification::StateChange(state_change) => {
|
|
let mut filtered_types = state_change.types;
|
|
filtered_types.intersection(types);
|
|
if !filtered_types.is_empty() {
|
|
Some(PushNotification::StateChange(StateChange {
|
|
account_id: state_change.account_id,
|
|
change_id: state_change.change_id,
|
|
types: filtered_types,
|
|
}))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
PushNotification::CalendarAlert(_) => {
|
|
if types.contains(DataType::CalendarAlert) {
|
|
Some(self.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
PushNotification::EmailPush(_) => {
|
|
if types.contains_any(
|
|
[
|
|
DataType::EmailDelivery,
|
|
DataType::Email,
|
|
DataType::Mailbox,
|
|
DataType::Thread,
|
|
]
|
|
.into_iter(),
|
|
) {
|
|
Some(self.clone())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl EmailPush {
|
|
pub fn to_state_change(&self) -> StateChange {
|
|
StateChange {
|
|
account_id: self.account_id,
|
|
change_id: self.change_id,
|
|
types: Bitmap::from_iter([
|
|
DataType::EmailDelivery,
|
|
DataType::Email,
|
|
DataType::Mailbox,
|
|
DataType::Thread,
|
|
]),
|
|
}
|
|
}
|
|
}
|