IMAP Acl command, rate limiting and ManageSieve server.
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 {:?}.",
|
||||
|
||||
@@ -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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,5 @@
|
||||
*/
|
||||
|
||||
pub mod bitmap;
|
||||
pub mod ttl_dashmap;
|
||||
pub mod vec_map;
|
||||
|
||||
67
crates/utils/src/map/ttl_dashmap.rs
Normal file
67
crates/utils/src/map/ttl_dashmap.rs
Normal 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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user