Skip to content

Commit

Permalink
Bug fixes and enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Sep 12, 2023
1 parent eae56a6 commit 761eae3
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 105 deletions.
19 changes: 15 additions & 4 deletions src/compiler/grammar/expr/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ where
b']' if self.buf.contains(&b'[') => {
self.buf.push(b']');
}
b'*' if self.buf.last().map_or(false, |&c| c == b'[') => {
b'*' if self.buf.last().map_or(false, |&c| c == b'[' || c == b'.') => {
self.buf.push(ch);
}
_ => {
Expand Down Expand Up @@ -187,7 +187,7 @@ where
}
Token::Comma
}
b' ' => {
b' ' | b'\r' | b'\n' => {
if prev_token.is_some() {
return Ok(prev_token);
} else {
Expand Down Expand Up @@ -260,11 +260,22 @@ where
.map_err(|_| format!("Invalid integer value {}", buf,))
}
} else {
let result = (self.token_map)(&buf, self.has_dot);
let has_dot = self.has_dot;
let has_number = self.has_number;

self.has_alpha = false;
self.has_number = false;
self.has_dot = false;
result

if !has_number && !has_dot && [4, 5].contains(&buf.len()) {
if buf == "true" {
return Ok(Token::Number(Number::Integer(1)));
} else if buf == "false" {
return Ok(Token::Number(Number::Integer(0)));
}
}

(self.token_map)(&buf, has_dot)
}
}
}
152 changes: 95 additions & 57 deletions src/compiler/lexer/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -498,63 +498,8 @@ impl<'x> CompilerState<'x> {
if !name.is_empty() || has_wildcard {
Ok(VariableType::Header(HeaderVariable {
name,
part: match part.as_str() {
"" | "text" => HeaderPart::Text,
// Addresses
"name" | "addr.name" => HeaderPart::Address(AddressPart::Name),
"addr" | "addr.all" => HeaderPart::Address(AddressPart::All),
"addr.domain" => HeaderPart::Address(AddressPart::Domain),
"addr.local" => HeaderPart::Address(AddressPart::LocalPart),
"addr.user" => HeaderPart::Address(AddressPart::User),
"addr.detail" => HeaderPart::Address(AddressPart::Detail),

// Content-type
"type" => HeaderPart::ContentType(ContentTypePart::Type),
"subtype" => HeaderPart::ContentType(ContentTypePart::Subtype),

// Received
"rcvd" => HeaderPart::Text,
"rcvd.from" => HeaderPart::Received(ReceivedPart::From(ReceivedHostname::Any)),
"rcvd.from.name" => {
HeaderPart::Received(ReceivedPart::From(ReceivedHostname::Name))
}
"rcvd.from.ip" => {
HeaderPart::Received(ReceivedPart::From(ReceivedHostname::Ip))
}
"rcvd.ip" => HeaderPart::Received(ReceivedPart::FromIp),
"rcvd.iprev" => HeaderPart::Received(ReceivedPart::FromIpRev),
"rcvd.by" => HeaderPart::Received(ReceivedPart::By(ReceivedHostname::Any)),
"rcvd.by.name" => {
HeaderPart::Received(ReceivedPart::By(ReceivedHostname::Name))
}
"rcvd.by.ip" => HeaderPart::Received(ReceivedPart::By(ReceivedHostname::Ip)),
"rcvd.for" => HeaderPart::Received(ReceivedPart::For),
"rcvd.with" => HeaderPart::Received(ReceivedPart::With),
"rcvd.tls" => HeaderPart::Received(ReceivedPart::TlsVersion),
"rcvd.cipher" => HeaderPart::Received(ReceivedPart::TlsCipher),
"rcvd.id" => HeaderPart::Received(ReceivedPart::Id),
"rcvd.ident" => HeaderPart::Received(ReceivedPart::Ident),
"rcvd.date" => HeaderPart::Received(ReceivedPart::Date),
"rcvd.date.raw" => HeaderPart::Received(ReceivedPart::DateRaw),

// Id
"id" => HeaderPart::Id,

// Raw
"raw" => HeaderPart::Raw,

// Date
"date" => HeaderPart::Date,

// Content-type attributes
_ => {
if let Some(attr) = part.strip_prefix("attr.") {
HeaderPart::ContentType(ContentTypePart::Attribute(attr.to_string()))
} else {
return Err(ErrorType::InvalidExpression(var_name.to_string()));
}
}
},
part: HeaderPart::try_from(part.as_str())
.map_err(|_| ErrorType::InvalidExpression(var_name.to_string()))?,
index_hdr: match hdr_index.as_str() {
"" => {
if !has_wildcard {
Expand Down Expand Up @@ -652,6 +597,99 @@ impl<'x> CompilerState<'x> {
}
}

impl TryFrom<&str> for HeaderPart {
type Error = ();

fn try_from(value: &str) -> Result<Self, Self::Error> {
let (value, subvalue) = value.split_once('.').unwrap_or((value, ""));
Ok(match value {
"" | "text" => HeaderPart::Text,
// Addresses
"name" => HeaderPart::Address(AddressPart::Name),
"addr" => {
if !subvalue.is_empty() {
HeaderPart::Address(AddressPart::try_from(subvalue)?)
} else {
HeaderPart::Address(AddressPart::All)
}
}

// Content-type
"type" => HeaderPart::ContentType(ContentTypePart::Type),
"subtype" => HeaderPart::ContentType(ContentTypePart::Subtype),
"attr" if !subvalue.is_empty() => {
HeaderPart::ContentType(ContentTypePart::Attribute(subvalue.to_string()))
}

// Received
"rcvd" => {
if !subvalue.is_empty() {
HeaderPart::Received(ReceivedPart::try_from(subvalue)?)
} else {
HeaderPart::Text
}
}

// Id
"id" => HeaderPart::Id,

// Raw
"raw" => HeaderPart::Raw,

// Date
"date" => HeaderPart::Date,

// Content-type attributes
_ => {
return Err(());
}
})
}
}

impl TryFrom<&str> for ReceivedPart {
type Error = ();

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
// Received
"from" => ReceivedPart::From(ReceivedHostname::Any),
"from.name" => ReceivedPart::From(ReceivedHostname::Name),
"from.ip" => ReceivedPart::From(ReceivedHostname::Ip),
"ip" => ReceivedPart::FromIp,
"iprev" => ReceivedPart::FromIpRev,
"by" => ReceivedPart::By(ReceivedHostname::Any),
"by.name" => ReceivedPart::By(ReceivedHostname::Name),
"by.ip" => ReceivedPart::By(ReceivedHostname::Ip),
"for" => ReceivedPart::For,
"with" => ReceivedPart::With,
"tls" => ReceivedPart::TlsVersion,
"cipher" => ReceivedPart::TlsCipher,
"id" => ReceivedPart::Id,
"ident" => ReceivedPart::Ident,
"date" => ReceivedPart::Date,
"date.raw" => ReceivedPart::DateRaw,
_ => return Err(()),
})
}
}

impl TryFrom<&str> for AddressPart {
type Error = ();

fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(match value {
"name" => AddressPart::Name,
"addr" | "all" => AddressPart::All,
"addr.domain" => AddressPart::Domain,
"addr.local" => AddressPart::LocalPart,
"addr.user" => AddressPart::User,
"addr.detail" => AddressPart::Detail,
_ => return Err(()),
})
}
}

impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down
14 changes: 10 additions & 4 deletions src/runtime/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,15 +288,21 @@ impl<'x> Context<'x> {
.collect::<Vec<_>>()
.into_iter()
.enumerate(),
Variable::ArrayRef(arr) if !arr.is_empty() => arr
.iter()
.map(|v| v.to_owned())
.collect::<Vec<_>>()
.into_iter()
.enumerate(),
Variable::String(s) => s
.split('\n')
.map(|line| Variable::String(line.trim().to_string()))
.lines()
.map(|line| Variable::String(line.to_string()))
.collect::<Vec<_>>()
.into_iter()
.enumerate(),
Variable::StringRef(s) => s
.split('\n')
.map(|line| Variable::String(line.trim().to_string()))
.lines()
.map(|line| Variable::String(line.to_string()))
.collect::<Vec<_>>()
.into_iter()
.enumerate(),
Expand Down
58 changes: 30 additions & 28 deletions src/runtime/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use std::cmp::Ordering;
use mail_parser::{
decoders::html::{html_to_text, text_to_html},
parsers::MessageStream,
Addr, Header, HeaderName, HeaderValue, Host, PartType,
Addr, Header, HeaderName, HeaderValue, Host, PartType, Received,
};

use crate::{
Expand Down Expand Up @@ -196,10 +196,10 @@ impl<'x> Context<'x> {
}
}

if !result.is_empty() {
Some(Variable::Array(result))
} else {
None
match result.len() {
1 => result.pop(),
0 => None,
_ => Some(Variable::Array(result)),
}
}

Expand Down Expand Up @@ -394,33 +394,12 @@ impl HeaderVariable {
})
}),
},
(HeaderValue::Received(rcvd), HeaderPart::Received(part)) => match part {
ReceivedPart::From(from) => rcvd
.from()
.or_else(|| rcvd.helo())
.and_then(|v| from.to_variable(v)),
ReceivedPart::FromIp => rcvd.from_ip().map(|ip| Variable::from(ip.to_string())),
ReceivedPart::FromIpRev => rcvd.from_iprev().map(Variable::from),
ReceivedPart::By(by) => rcvd.by().and_then(|v: &Host<'_>| by.to_variable(v)),
ReceivedPart::For => rcvd.for_().map(Variable::from),
ReceivedPart::With => rcvd.with().map(|v| Variable::from(v.as_str())),
ReceivedPart::TlsVersion => {
rcvd.tls_version().map(|v| Variable::from(v.as_str()))
}
ReceivedPart::TlsCipher => rcvd.tls_cipher().map(Variable::from),
ReceivedPart::Id => rcvd.id().map(Variable::from),
ReceivedPart::Ident => rcvd.ident().map(Variable::from),
ReceivedPart::Via => rcvd.via().map(Variable::from),
ReceivedPart::Date => rcvd.date().map(|d| Variable::from(d.to_timestamp())),
ReceivedPart::DateRaw => rcvd.date().map(|d| Variable::from(d.to_rfc822())),
},
(HeaderValue::Received(rcvd), HeaderPart::Received(part)) => part.eval(rcvd),
_ => None,
},
};

if let Some(var) = var {
result.push(var);
}
result.push(var.unwrap_or_default());
}

#[inline(always)]
Expand All @@ -429,6 +408,29 @@ impl HeaderVariable {
}
}

impl ReceivedPart {
pub fn eval<'x>(&self, rcvd: &'x Received<'x>) -> Option<Variable<'x>> {
match self {
ReceivedPart::From(from) => rcvd
.from()
.or_else(|| rcvd.helo())
.and_then(|v| from.to_variable(v)),
ReceivedPart::FromIp => rcvd.from_ip().map(|ip| Variable::from(ip.to_string())),
ReceivedPart::FromIpRev => rcvd.from_iprev().map(Variable::from),
ReceivedPart::By(by) => rcvd.by().and_then(|v: &Host<'_>| by.to_variable(v)),
ReceivedPart::For => rcvd.for_().map(Variable::from),
ReceivedPart::With => rcvd.with().map(|v| Variable::from(v.as_str())),
ReceivedPart::TlsVersion => rcvd.tls_version().map(|v| Variable::from(v.as_str())),
ReceivedPart::TlsCipher => rcvd.tls_cipher().map(Variable::from),
ReceivedPart::Id => rcvd.id().map(Variable::from),
ReceivedPart::Ident => rcvd.ident().map(Variable::from),
ReceivedPart::Via => rcvd.via().map(Variable::from),
ReceivedPart::Date => rcvd.date().map(|d| Variable::from(d.to_timestamp())),
ReceivedPart::DateRaw => rcvd.date().map(|d| Variable::from(d.to_rfc822())),
}
}
}

trait AddrToText<'x> {
fn to_text<'z: 'x>(&'z self) -> Variable<'x>;
}
Expand Down
36 changes: 30 additions & 6 deletions src/runtime/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<'x> Context<'x> {
for expr in expr {
match expr {
Expression::Variable(v) => {
stack.push(self.variable(v)?);
stack.push(self.variable(v).unwrap_or_default());
}
Expression::Number(val) => {
stack.push(Variable::from(*val));
Expand Down Expand Up @@ -121,10 +121,34 @@ impl<'x> Variable<'x> {
.chain(b.iter().map(|v| v.as_ref()))
.collect(),
),
(Variable::String(a), b) => Variable::String(format!("{}{}", a, b)),
(a, Variable::String(b)) => Variable::String(format!("{}{}", a, b)),
(Variable::StringRef(a), b) => Variable::String(format!("{}{}", a, b)),
(a, Variable::StringRef(b)) => Variable::String(format!("{}{}", a, b)),
(Variable::String(a), b) => {
if !a.is_empty() {
Variable::String(format!("{}{}", a, b))
} else {
b
}
}
(a, Variable::String(b)) => {
if !b.is_empty() {
Variable::String(format!("{}{}", a, b))
} else {
a
}
}
(Variable::StringRef(a), b) => {
if !a.is_empty() {
Variable::String(format!("{}{}", a, b))
} else {
b
}
}
(a, Variable::StringRef(b)) => {
if !b.is_empty() {
Variable::String(format!("{}{}", a, b))
} else {
a
}
}
}
}

Expand Down Expand Up @@ -245,7 +269,7 @@ impl<'x> Variable<'x> {
}
}

fn to_bool(&self) -> bool {
pub fn to_bool(&self) -> bool {
match self {
Variable::Float(f) => *f != 0.0,
Variable::Integer(n) => *n != 0,
Expand Down
10 changes: 10 additions & 0 deletions src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,16 @@ impl<'x> Variable<'x> {
}
}

pub fn to_integer(&self) -> i64 {
match self {
Variable::Integer(n) => *n,
Variable::Float(n) => *n as i64,
Variable::String(s) if !s.is_empty() => s.parse::<i64>().unwrap_or(0),
Variable::StringRef(s) if !s.is_empty() => s.parse::<i64>().unwrap_or(0),
_ => 0,
}
}

pub fn len(&self) -> usize {
match self {
Variable::String(s) => s.len(),
Expand Down
Loading

0 comments on commit 761eae3

Please sign in to comment.