mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2024-11-28 09:07:32 +00:00
IMAP IDLE support for command pipelining (fixes #765)
This commit is contained in:
parent
180a856cb4
commit
51c8f7b279
7 changed files with 36 additions and 24 deletions
|
@ -95,7 +95,7 @@ impl Capability {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn all_capabilities(is_authenticated: bool, is_tls: bool) -> Vec<Capability> {
|
||||
pub fn all_capabilities(is_authenticated: bool, offer_tls: bool) -> Vec<Capability> {
|
||||
let mut capabilities = vec![
|
||||
Capability::IMAP4rev2,
|
||||
Capability::IMAP4rev1,
|
||||
|
@ -140,7 +140,7 @@ impl Capability {
|
|||
Capability::Auth(Mechanism::Plain),
|
||||
]);
|
||||
}
|
||||
if !is_tls {
|
||||
if offer_tls {
|
||||
capabilities.push(Capability::StartTLS);
|
||||
}
|
||||
|
||||
|
|
|
@ -52,11 +52,7 @@ pub struct ImapInstance {
|
|||
}
|
||||
|
||||
pub struct Inner {
|
||||
pub greeting_plain: Vec<u8>,
|
||||
pub greeting_tls: Vec<u8>,
|
||||
|
||||
pub rate_limiter: DashMap<u32, Arc<ConcurrencyLimiters>>,
|
||||
|
||||
pub cache_account: LruCache<AccountId, Arc<Account>>,
|
||||
pub cache_mailbox: LruCache<MailboxId, Arc<MailboxState>>,
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ use jmap::JMAP;
|
|||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio_rustls::server::TlsStream;
|
||||
|
||||
use crate::{GREETING_WITHOUT_TLS, GREETING_WITH_TLS};
|
||||
|
||||
use super::{ImapSessionManager, Session, State};
|
||||
|
||||
impl SessionManager for ImapSessionManager {
|
||||
|
@ -116,11 +118,13 @@ impl<T: SessionStream> Session<T> {
|
|||
manager: ImapSessionManager,
|
||||
) -> Result<Session<T>, ()> {
|
||||
// Write greeting
|
||||
let (is_tls, greeting) = if session.stream.is_tls() {
|
||||
(true, &manager.imap.imap_inner.greeting_tls)
|
||||
let is_tls = session.stream.is_tls();
|
||||
let greeting = if !is_tls && session.instance.acceptor.is_tls() {
|
||||
&GREETING_WITH_TLS
|
||||
} else {
|
||||
(false, &manager.imap.imap_inner.greeting_plain)
|
||||
&GREETING_WITHOUT_TLS
|
||||
};
|
||||
|
||||
if let Err(err) = session.stream.write_all(greeting).await {
|
||||
trc::event!(
|
||||
Network(trc::NetworkEvent::WriteError),
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
*/
|
||||
|
||||
use core::{ImapInstance, Inner, IMAP};
|
||||
use std::{collections::hash_map::RandomState, sync::Arc};
|
||||
use std::{
|
||||
collections::hash_map::RandomState,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use imap_proto::{protocol::capability::Capability, ResponseCode, StatusResponse};
|
||||
|
@ -20,6 +23,22 @@ pub mod op;
|
|||
|
||||
static SERVER_GREETING: &str = "Stalwart IMAP4rev2 at your service.";
|
||||
|
||||
pub(crate) static GREETING_WITH_TLS: LazyLock<Vec<u8>> = LazyLock::new(|| {
|
||||
StatusResponse::ok(SERVER_GREETING)
|
||||
.with_code(ResponseCode::Capability {
|
||||
capabilities: Capability::all_capabilities(false, true),
|
||||
})
|
||||
.into_bytes()
|
||||
});
|
||||
|
||||
pub(crate) static GREETING_WITHOUT_TLS: LazyLock<Vec<u8>> = LazyLock::new(|| {
|
||||
StatusResponse::ok(SERVER_GREETING)
|
||||
.with_code(ResponseCode::Capability {
|
||||
capabilities: Capability::all_capabilities(false, false),
|
||||
})
|
||||
.into_bytes()
|
||||
});
|
||||
|
||||
impl IMAP {
|
||||
pub async fn init(config: &mut Config, jmap_instance: JmapInstance) -> ImapInstance {
|
||||
let shard_amount = config
|
||||
|
@ -29,16 +48,6 @@ impl IMAP {
|
|||
let capacity = config.property("cache.capacity").unwrap_or(100);
|
||||
|
||||
let inner = Inner {
|
||||
greeting_plain: StatusResponse::ok(SERVER_GREETING)
|
||||
.with_code(ResponseCode::Capability {
|
||||
capabilities: Capability::all_capabilities(false, false),
|
||||
})
|
||||
.into_bytes(),
|
||||
greeting_tls: StatusResponse::ok(SERVER_GREETING)
|
||||
.with_code(ResponseCode::Capability {
|
||||
capabilities: Capability::all_capabilities(false, true),
|
||||
})
|
||||
.into_bytes(),
|
||||
rate_limiter: DashMap::with_capacity_and_hasher_and_shard_amount(
|
||||
capacity,
|
||||
RandomState::default(),
|
||||
|
|
|
@ -140,7 +140,10 @@ impl<T: SessionStream> Session<T> {
|
|||
self.write_bytes(
|
||||
StatusResponse::ok("Authentication successful")
|
||||
.with_code(ResponseCode::Capability {
|
||||
capabilities: Capability::all_capabilities(true, self.is_tls),
|
||||
capabilities: Capability::all_capabilities(
|
||||
true,
|
||||
!self.is_tls && self.instance.acceptor.is_tls(),
|
||||
),
|
||||
})
|
||||
.with_tag(tag)
|
||||
.into_bytes(),
|
||||
|
|
|
@ -39,7 +39,7 @@ impl<T: SessionStream> Session<T> {
|
|||
Response {
|
||||
capabilities: Capability::all_capabilities(
|
||||
self.state.is_authenticated(),
|
||||
self.is_tls,
|
||||
!self.is_tls && self.instance.acceptor.is_tls(),
|
||||
),
|
||||
}
|
||||
.serialize(),
|
||||
|
|
|
@ -69,10 +69,10 @@ impl<T: SessionStream> Session<T> {
|
|||
);
|
||||
|
||||
let op_start = Instant::now();
|
||||
let mut buf = vec![0; 1024];
|
||||
let mut buf = vec![0; 4];
|
||||
loop {
|
||||
tokio::select! {
|
||||
result = tokio::time::timeout(self.jmap.core.imap.timeout_idle, self.stream_rx.read(&mut buf)) => {
|
||||
result = tokio::time::timeout(self.jmap.core.imap.timeout_idle, self.stream_rx.read_exact(&mut buf)) => {
|
||||
match result {
|
||||
Ok(Ok(bytes_read)) => {
|
||||
if bytes_read > 0 {
|
||||
|
|
Loading…
Reference in a new issue