IMAP Acl command, rate limiting and ManageSieve server.

This commit is contained in:
mdecimus
2023-06-29 18:51:26 +02:00
parent f5048da232
commit 29f3ca284b
217 changed files with 14864 additions and 366 deletions

View File

@@ -20,6 +20,8 @@ tracing-opentelemetry = "0.18.0"
opentelemetry = { version = "0.18.0", features = ["rt-tokio"] }
opentelemetry-otlp = { version = "0.11.0", features = ["http-proto", "reqwest-client"] }
opentelemetry-semantic-conventions = { version = "0.10.0" }
dashmap = "5.4"
ahash = { version = "0.8" }
[target.'cfg(unix)'.dependencies]
privdrop = "0.5.3"

View File

@@ -287,7 +287,7 @@ impl Config {
.value_or_default(("server.listener", id, "url"), "server.url")
.failed(&format!("No 'url' directive found for listener {id:?}"))
.to_string(),
ServerProtocol::Imap | ServerProtocol::Http => self
ServerProtocol::Imap | ServerProtocol::Http | ServerProtocol::ManageSieve => self
.value_or_default(("server.listener", id, "url"), "server.url")
.unwrap_or_default()
.to_string(),
@@ -318,6 +318,8 @@ impl ParseValue for ServerProtocol {
Ok(Self::Imap)
} else if value.eq_ignore_ascii_case("http") {
Ok(Self::Http)
} else if value.eq_ignore_ascii_case("managesieve") {
Ok(Self::ManageSieve)
} else {
Err(format!(
"Invalid server protocol type {:?} for property {:?}.",

View File

@@ -71,6 +71,7 @@ pub enum ServerProtocol {
Jmap,
Imap,
Http,
ManageSieve,
}
#[derive(Debug, Default, PartialEq, Eq, Clone)]
@@ -87,6 +88,7 @@ impl Display for ServerProtocol {
ServerProtocol::Jmap => write!(f, "jmap"),
ServerProtocol::Imap => write!(f, "imap"),
ServerProtocol::Http => write!(f, "http"),
ServerProtocol::ManageSieve => write!(f, "managesieve"),
}
}
}

View File

@@ -95,6 +95,10 @@ impl RateLimiter {
self.last_refill = Instant::now();
self.tokens = self.max_requests;
}
pub fn is_active(&self) -> bool {
self.tokens < self.max_requests || self.last_refill.elapsed() < self.max_interval
}
}
impl ConcurrencyLimiter {
@@ -120,4 +124,8 @@ impl ConcurrencyLimiter {
pub fn check_is_allowed(&self) -> bool {
self.concurrent.load(Ordering::Relaxed) < self.max_concurrent
}
pub fn is_active(&self) -> bool {
self.concurrent.load(Ordering::Relaxed) > 0
}
}

View File

@@ -22,4 +22,5 @@
*/
pub mod bitmap;
pub mod ttl_dashmap;
pub mod vec_map;

View File

@@ -0,0 +1,67 @@
/*
* Copyright (c) 2020-2023, Stalwart Labs Ltd.
*
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
* https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
* <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
* option. This file may not be copied, modified, or distributed
* except according to those terms.
*/
use std::{borrow::Borrow, hash::Hash, time::Instant};
use dashmap::DashMap;
pub type TtlDashMap<K, V> = DashMap<K, LruItem<V>, ahash::RandomState>;
#[derive(Debug, Clone)]
pub struct LruItem<V> {
item: V,
valid_until: Instant,
}
pub trait TtlMap<K, V>: Sized {
fn with_capacity(capacity: usize, shard_amount: usize) -> Self;
fn get_with_ttl<Q: ?Sized>(&self, name: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq;
fn insert_with_ttl(&self, name: K, value: V, valid_until: Instant) -> V;
fn cleanup(&self);
}
impl<K: Hash + Eq, V: Clone> TtlMap<K, V> for TtlDashMap<K, V> {
fn with_capacity(capacity: usize, shard_amount: usize) -> Self {
DashMap::with_capacity_and_hasher_and_shard_amount(
capacity,
ahash::RandomState::new(),
shard_amount,
)
}
fn get_with_ttl<Q: ?Sized>(&self, name: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
match self.get(name) {
Some(entry) if entry.valid_until >= Instant::now() => entry.item.clone().into(),
_ => None,
}
}
fn insert_with_ttl(&self, name: K, item: V, valid_until: Instant) -> V {
self.insert(
name,
LruItem {
item: item.clone(),
valid_until,
},
);
item
}
fn cleanup(&self) {
self.retain(|_, entry| entry.valid_until >= Instant::now());
}
}