mail-server/crates/antispam/src/import/mod.rs

301 lines
6 KiB
Rust

use std::collections::HashMap;
pub mod ast;
pub mod meta;
pub mod spamassassin;
pub mod tokenizer;
pub mod utils;
#[derive(Debug, Default, Clone)]
struct Rule {
name: String,
t: RuleType,
scores: Vec<f64>,
description: HashMap<String, String>,
priority: i32,
flags: Vec<TestFlag>,
forward_score_pos: f64,
forward_score_neg: f64,
}
#[derive(Debug, Default, Clone)]
enum RuleType {
Header {
matches: HeaderMatches,
header: Header,
part: Vec<HeaderPart>,
if_unset: Option<String>,
pattern: String,
},
Body {
pattern: String,
raw: bool,
},
Full {
pattern: String,
},
Uri {
pattern: String,
},
Eval {
function: String,
params: Vec<String>,
},
Meta {
expr: MetaExpression,
},
#[default]
None,
}
#[derive(Debug, Clone, Default)]
pub struct MetaExpression {
pub tokens: Vec<Token>,
pub expr: Expr,
}
impl RuleType {
pub fn pattern(&mut self) -> Option<&mut String> {
match self {
RuleType::Header { pattern, .. } => Some(pattern),
RuleType::Body { pattern, .. } => Some(pattern),
RuleType::Full { pattern, .. } => Some(pattern),
RuleType::Uri { pattern, .. } => Some(pattern),
_ => None,
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
enum TestFlag {
Net,
Nice,
UserConf,
Learn,
NoAutoLearn,
Publish,
Multiple,
NoTrim,
DomainsOnly,
NoSubject,
AutoLearnBody,
A,
MaxHits(u32),
DnsBlockRule(String),
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
enum Header {
#[default]
All,
MessageId,
AllExternal,
EnvelopeFrom,
ToCc,
Name(String),
}
#[derive(Debug, Default, Clone)]
enum HeaderMatches {
#[default]
Matches,
NotMatches,
Exists,
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
enum HeaderPart {
Name,
Addr,
#[default]
Raw,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Token {
Tag(String),
Number(u32),
Logical(Logical),
Comparator(Comparator),
Operation(Operation),
OpenParen,
CloseParen,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Logical {
And,
Or,
Not,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Comparator {
Gt,
Lt,
Eq,
Ge,
Le,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum Operation {
Add,
Multiply,
Divide,
Subtract,
And,
Or,
Not,
}
#[derive(Debug, PartialEq, Clone)]
pub enum Expr {
UnaryOp(UnaryOperator, Box<Expr>),
BinaryOp(Box<Expr>, BinaryOperator, Box<Expr>),
Literal(u32),
Identifier(String),
}
impl Default for Expr {
fn default() -> Self {
Self::Literal(0)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum UnaryOperator {
Not,
Minus,
}
#[derive(Debug, PartialEq, Clone)]
pub enum BinaryOperator {
Or,
And,
Greater,
Lesser,
GreaterOrEqual,
LesserOrEqual,
Equal,
Add,
Subtract,
Multiply,
Divide,
BitwiseAnd,
BitwiseOr,
}
impl BinaryOperator {
pub fn precedence(&self) -> u32 {
match self {
Self::Or => 1,
Self::And => 2,
Self::Greater
| Self::Lesser
| Self::GreaterOrEqual
| Self::LesserOrEqual
| Self::Equal => 3,
Self::Add | Self::Subtract => 4,
Self::Multiply | Self::Divide => 5,
Self::BitwiseAnd | Self::BitwiseOr => 6,
}
}
}
impl Rule {
fn score(&self) -> f64 {
self.scores.last().copied().unwrap_or_else(|| {
if self.name.starts_with("__") {
0.0
} else if self.name.starts_with("T_") {
0.01
} else {
1.0
}
})
}
}
impl Ord for Rule {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
let this_score = self.score();
let other_score = other.score();
let this_is_negative = this_score < 0.0;
let other_is_negative = other_score < 0.0;
if this_is_negative != other_is_negative {
if this_is_negative {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
} else {
let this_priority = if this_score != 0.0 {
self.priority
} else {
9000
};
let other_priority = if other_score != 0.0 {
other.priority
} else {
9000
};
match this_priority.cmp(&other_priority) {
std::cmp::Ordering::Equal => {
match other_score.abs().partial_cmp(&this_score.abs()).unwrap() {
std::cmp::Ordering::Equal => other.name.cmp(&self.name),
x => x,
}
}
x => x,
}
}
}
}
impl PartialOrd for Rule {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Rule {
fn eq(&self, other: &Self) -> bool {
self.name == other.name && self.priority == other.priority && self.scores == other.scores
}
}
impl Eq for Rule {}
pub trait UnwrapResult<T> {
fn unwrap_result(self, action: &str) -> T;
}
impl<T> UnwrapResult<T> for Option<T> {
fn unwrap_result(self, message: &str) -> T {
match self {
Some(result) => result,
None => {
eprintln!("Failed to {}", message);
std::process::exit(1);
}
}
}
}
impl<T, E: std::fmt::Display> UnwrapResult<T> for Result<T, E> {
fn unwrap_result(self, message: &str) -> T {
match self {
Ok(result) => result,
Err(err) => {
eprintln!("Failed to {}: {}", message, err);
std::process::exit(1);
}
}
}
}