Lookup store refactoring
This commit is contained in:
@@ -217,7 +217,7 @@ impl ConfigCondition for Config {
|
||||
MatchType::Lookup => {
|
||||
if let Some(lookup) = ctx.directory.lookups.get(value_str) {
|
||||
ConditionMatch::Lookup(lookup.clone().into())
|
||||
} else if let Some(lookup) = ctx.stores.lookups.get(value_str) {
|
||||
} else if let Some(lookup) = ctx.stores.lookup_stores.get(value_str) {
|
||||
ConditionMatch::Lookup(lookup.clone().into())
|
||||
} else {
|
||||
return Err(format!(
|
||||
|
||||
@@ -121,7 +121,7 @@ impl ConfigSieve for Config {
|
||||
.with_valid_notification_uri("mailto")
|
||||
.with_valid_ext_lists(
|
||||
ctx.stores
|
||||
.lookups
|
||||
.lookup_stores
|
||||
.keys()
|
||||
.chain(ctx.directory.lookups.keys())
|
||||
.map(|k| k.to_string()),
|
||||
@@ -193,18 +193,6 @@ impl ConfigSieve for Config {
|
||||
Ok(SieveCore {
|
||||
runtime,
|
||||
scripts: ctx.scripts.clone(),
|
||||
lookup: ctx
|
||||
.stores
|
||||
.lookups
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.clone().into()))
|
||||
.chain(
|
||||
ctx.directory
|
||||
.lookups
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.clone().into())),
|
||||
)
|
||||
.collect(),
|
||||
lookup_stores: ctx.stores.lookup_stores.clone(),
|
||||
directories: ctx.directory.directories.clone(),
|
||||
default_directory: self
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
hash::Hash,
|
||||
net::IpAddr,
|
||||
sync::{atomic::AtomicU32, Arc},
|
||||
@@ -41,7 +40,7 @@ use smtp_proto::{
|
||||
},
|
||||
IntoString,
|
||||
};
|
||||
use store::{LookupStore, Row, Value};
|
||||
use store::{LookupKey, LookupStore, LookupValue, Value};
|
||||
use tokio::{
|
||||
io::{AsyncRead, AsyncWrite},
|
||||
sync::mpsc,
|
||||
@@ -65,6 +64,7 @@ use crate::{
|
||||
},
|
||||
queue::{self, DomainPart, QueueId, QuotaLimiter},
|
||||
reporting,
|
||||
scripts::plugins::lookup::VariableExists,
|
||||
};
|
||||
|
||||
use self::throttle::{Limiter, ThrottleKey, ThrottleKeyHasherBuilder};
|
||||
@@ -119,7 +119,6 @@ pub struct SieveCore {
|
||||
pub sign: Vec<Arc<DkimSigner>>,
|
||||
pub directories: AHashMap<String, Arc<Directory>>,
|
||||
pub lookup_stores: AHashMap<String, LookupStore>,
|
||||
pub lookup: AHashMap<String, Lookup>,
|
||||
pub default_lookup_store: Option<LookupStore>,
|
||||
pub default_directory: Option<Arc<Directory>>,
|
||||
}
|
||||
@@ -161,7 +160,7 @@ pub struct TlsConnectors {
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Lookup {
|
||||
Store(Arc<store::Lookup>),
|
||||
Store(LookupStore),
|
||||
Directory(directory::Lookup),
|
||||
}
|
||||
|
||||
@@ -283,68 +282,35 @@ impl SessionData {
|
||||
}
|
||||
|
||||
impl Lookup {
|
||||
pub async fn contains(&self, item: impl Into<Value<'_>>) -> Option<bool> {
|
||||
pub async fn contains(&self, item: &str) -> Option<bool> {
|
||||
match self {
|
||||
Lookup::Store(lookup) => lookup
|
||||
Lookup::Store(LookupStore::Query(lookup)) => lookup
|
||||
.store
|
||||
.query::<bool>(&lookup.query, vec![item.into()])
|
||||
.await
|
||||
.ok(),
|
||||
Lookup::Store(store) => store
|
||||
.key_get::<VariableExists>(LookupKey::Key(item.to_string().into_bytes()))
|
||||
.await
|
||||
.ok()
|
||||
.map(|v| !matches!(v, LookupValue::None)),
|
||||
Lookup::Directory(lookup) => match lookup {
|
||||
directory::Lookup::DomainExists(directory) => directory
|
||||
.is_local_domain(item.into().to_str().as_ref())
|
||||
.await
|
||||
.ok(),
|
||||
directory::Lookup::EmailExists(directory) => {
|
||||
directory.rcpt(item.into().to_str().as_ref()).await.ok()
|
||||
directory::Lookup::DomainExists(directory) => {
|
||||
directory.is_local_domain(item).await.ok()
|
||||
}
|
||||
directory::Lookup::EmailExists(directory) => directory.rcpt(item).await.ok(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn lookup(&self, items: Vec<Value<'_>>) -> Option<Variable> {
|
||||
match self {
|
||||
Lookup::Store(lookup) => lookup
|
||||
.store
|
||||
.query::<Option<Row>>(&lookup.query, items)
|
||||
.await
|
||||
.ok()
|
||||
.map(|row| {
|
||||
let mut row = row.map(|row| row.values).unwrap_or_default();
|
||||
match row.len().cmp(&1) {
|
||||
Ordering::Equal if !matches!(row.first(), Some(Value::Null)) => {
|
||||
row.pop().map(into_sieve_value).unwrap()
|
||||
}
|
||||
Ordering::Less => Variable::default(),
|
||||
_ => Variable::Array(
|
||||
row.into_iter()
|
||||
.map(into_sieve_value)
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
}),
|
||||
Lookup::Directory(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query(&self, items: Vec<Value<'_>>) -> Option<Vec<Value<'static>>> {
|
||||
match self {
|
||||
Lookup::Store(lookup) => lookup
|
||||
.store
|
||||
.query::<Option<Row>>(&lookup.query, items)
|
||||
.await
|
||||
.ok()
|
||||
.map(|row| row.map(|row| row.values).unwrap_or_default()),
|
||||
Lookup::Directory(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Lookup {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Lookup::Store(a), Lookup::Store(b)) => a.query == b.query,
|
||||
(Lookup::Store(LookupStore::Query(a)), Lookup::Store(LookupStore::Query(b))) => {
|
||||
a.query == b.query
|
||||
}
|
||||
(Lookup::Store(LookupStore::Store(_)), Lookup::Store(LookupStore::Store(_))) => true,
|
||||
(Lookup::Directory(a), Lookup::Directory(b)) => matches!(
|
||||
(a, b),
|
||||
(
|
||||
@@ -389,8 +355,8 @@ pub fn to_store_value(value: &Variable) -> Value<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arc<store::Lookup>> for Lookup {
|
||||
fn from(lookup: Arc<store::Lookup>) -> Self {
|
||||
impl From<LookupStore> for Lookup {
|
||||
fn from(lookup: LookupStore) -> Self {
|
||||
Lookup::Store(lookup)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,15 +32,18 @@ use smtp_proto::{
|
||||
MAIL_BY_TRACE, MAIL_RET_FULL, MAIL_RET_HDRS, RCPT_NOTIFY_DELAY, RCPT_NOTIFY_FAILURE,
|
||||
RCPT_NOTIFY_NEVER, RCPT_NOTIFY_SUCCESS,
|
||||
};
|
||||
use store::backend::memory::MemoryStore;
|
||||
use store::{backend::memory::MemoryStore, LookupKey, LookupStore, LookupValue};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
use crate::{
|
||||
core::{Lookup, SMTP},
|
||||
core::SMTP,
|
||||
queue::{DomainPart, InstantFromTimestamp, Message},
|
||||
};
|
||||
|
||||
use super::{plugins::PluginContext, ScriptModification, ScriptParameters, ScriptResult};
|
||||
use super::{
|
||||
plugins::{lookup::VariableExists, PluginContext},
|
||||
ScriptModification, ScriptParameters, ScriptResult,
|
||||
};
|
||||
|
||||
impl SMTP {
|
||||
pub fn run_script_blocking(
|
||||
@@ -92,14 +95,18 @@ impl SMTP {
|
||||
} => {
|
||||
input = false.into();
|
||||
'outer: for list in lists {
|
||||
if let Some(list) = self.sieve.lookup.get(&list) {
|
||||
if let Some(store) = self.sieve.lookup_stores.get(&list) {
|
||||
for value in &values {
|
||||
let result = if !matches!(match_as, MatchAs::Lowercase) {
|
||||
handle.block_on(list.contains(value))
|
||||
} else {
|
||||
handle.block_on(list.contains(&value.to_lowercase()))
|
||||
};
|
||||
if let Some(true) = result {
|
||||
if let Ok(LookupValue::Value { .. }) = handle.block_on(
|
||||
store.key_get::<VariableExists>(LookupKey::Key(
|
||||
if !matches!(match_as, MatchAs::Lowercase) {
|
||||
value.clone()
|
||||
} else {
|
||||
value.to_lowercase()
|
||||
}
|
||||
.into_bytes(),
|
||||
)),
|
||||
) {
|
||||
input = true.into();
|
||||
break 'outer;
|
||||
}
|
||||
@@ -165,18 +172,13 @@ impl SMTP {
|
||||
}
|
||||
}
|
||||
Recipient::List(list) => {
|
||||
if let Some(list) = self.sieve.lookup.get(&list) {
|
||||
if let Lookup::Store(list) = list {
|
||||
if let store::LookupStore::Memory(list) = &list.store {
|
||||
if let MemoryStore::List(list) = list.as_ref() {
|
||||
for rcpt in &list.set {
|
||||
handle.block_on(
|
||||
message.add_recipient(
|
||||
rcpt,
|
||||
&self.queue.config,
|
||||
),
|
||||
);
|
||||
}
|
||||
if let Some(list) = self.sieve.lookup_stores.get(&list) {
|
||||
if let LookupStore::Memory(list) = list {
|
||||
if let MemoryStore::List(list) = list.as_ref() {
|
||||
for rcpt in &list.set {
|
||||
handle.block_on(
|
||||
message.add_recipient(rcpt, &self.queue.config),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,11 @@ use std::{
|
||||
|
||||
use mail_auth::flate2;
|
||||
use sieve::{runtime::Variable, FunctionMap};
|
||||
use store::{Deserialize, LookupKey, LookupValue};
|
||||
use store::{Deserialize, LookupKey, LookupValue, Value};
|
||||
|
||||
use crate::{
|
||||
config::scripts::{RemoteList, SieveContext},
|
||||
core::to_store_value,
|
||||
core::into_sieve_value,
|
||||
USER_AGENT,
|
||||
};
|
||||
|
||||
@@ -61,32 +61,6 @@ pub fn register_local_domain(plugin_id: u32, fnc_map: &mut FunctionMap<SieveCont
|
||||
|
||||
pub fn exec(ctx: PluginContext<'_>) -> Variable {
|
||||
let store = match &ctx.arguments[0] {
|
||||
Variable::String(v) if v.contains('/') => {
|
||||
if let Some(lookup) = ctx.core.sieve.lookup.get(v.as_ref()) {
|
||||
return match &ctx.arguments[1] {
|
||||
Variable::Array(items) => {
|
||||
for item in items.iter() {
|
||||
if !item.is_empty()
|
||||
&& ctx
|
||||
.handle
|
||||
.block_on(lookup.contains(to_store_value(item)))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return true.into();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
v if !v.is_empty() => ctx
|
||||
.handle
|
||||
.block_on(lookup.contains(to_store_value(v)))
|
||||
.unwrap_or(false),
|
||||
_ => false,
|
||||
}
|
||||
.into();
|
||||
}
|
||||
None
|
||||
}
|
||||
Variable::String(v) if !v.is_empty() => ctx.core.sieve.lookup_stores.get(v.as_ref()),
|
||||
_ => ctx.core.sieve.default_lookup_store.as_ref(),
|
||||
};
|
||||
@@ -133,23 +107,6 @@ pub fn exec(ctx: PluginContext<'_>) -> Variable {
|
||||
|
||||
pub fn exec_get(ctx: PluginContext<'_>) -> Variable {
|
||||
let store = match &ctx.arguments[0] {
|
||||
Variable::String(v) if v.contains('/') => {
|
||||
if let Some(lookup) = ctx.core.sieve.lookup.get(v.as_ref()) {
|
||||
let items = match &ctx.arguments[1] {
|
||||
Variable::Array(l) => l.iter().map(to_store_value).collect(),
|
||||
v if !v.is_empty() => vec![to_store_value(v)],
|
||||
_ => vec![],
|
||||
};
|
||||
return if !items.is_empty() {
|
||||
ctx.handle
|
||||
.block_on(lookup.lookup(items))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
Variable::default()
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
Variable::String(v) if !v.is_empty() => ctx.core.sieve.lookup_stores.get(v.as_ref()),
|
||||
_ => ctx.core.sieve.default_lookup_store.as_ref(),
|
||||
};
|
||||
@@ -467,10 +424,10 @@ pub fn exec_local_domain(ctx: PluginContext<'_>) -> Variable {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct VariableWrapper(Variable);
|
||||
pub struct VariableWrapper(Variable);
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct VariableExists;
|
||||
pub struct VariableExists;
|
||||
|
||||
impl Deserialize for VariableWrapper {
|
||||
fn deserialize(bytes: &[u8]) -> store::Result<Self> {
|
||||
@@ -493,3 +450,15 @@ impl VariableWrapper {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value<'static>> for VariableExists {
|
||||
fn from(_: Value<'static>) -> Self {
|
||||
VariableExists
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Value<'static>> for VariableWrapper {
|
||||
fn from(value: Value<'static>) -> Self {
|
||||
VariableWrapper(into_sieve_value(value))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user