Add basic DAV server
This commit is contained in:
parent
0dcf69f180
commit
3d3fd80629
6 changed files with 129 additions and 13 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -28,7 +28,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aerogramme"
|
name = "aerogramme"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"argon2",
|
"argon2",
|
||||||
|
@ -46,6 +46,8 @@ dependencies = [
|
||||||
"eml-codec",
|
"eml-codec",
|
||||||
"futures",
|
"futures",
|
||||||
"hex",
|
"hex",
|
||||||
|
"http-body-util",
|
||||||
|
"hyper 1.2.0",
|
||||||
"hyper-rustls 0.26.0",
|
"hyper-rustls 0.26.0",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"im",
|
"im",
|
||||||
|
@ -58,6 +60,7 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
"nom 7.1.3",
|
"nom 7.1.3",
|
||||||
|
"quick-xml",
|
||||||
"rand",
|
"rand",
|
||||||
"rmp-serde",
|
"rmp-serde",
|
||||||
"rpassword",
|
"rpassword",
|
||||||
|
@ -2691,6 +2694,15 @@ dependencies = [
|
||||||
"prost",
|
"prost",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.31.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.35"
|
version = "1.0.35"
|
||||||
|
|
29
Cargo.toml
29
Cargo.toml
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "aerogramme"
|
name = "aerogramme"
|
||||||
version = "0.2.2"
|
version = "0.3.0"
|
||||||
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "EUPL-1.2"
|
license = "EUPL-1.2"
|
||||||
|
@ -18,6 +18,7 @@ backtrace = "0.3"
|
||||||
console-subscriber = "0.2"
|
console-subscriber = "0.2"
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
|
thiserror = "1.0.56"
|
||||||
|
|
||||||
# language extensions
|
# language extensions
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
@ -32,13 +33,27 @@ chrono = { version = "0.4", default-features = false, features = ["alloc"] }
|
||||||
nix = { version = "0.27", features = ["signal"] }
|
nix = { version = "0.27", features = ["signal"] }
|
||||||
clap = { version = "3.1.18", features = ["derive", "env"] }
|
clap = { version = "3.1.18", features = ["derive", "env"] }
|
||||||
|
|
||||||
# serialization & parsing
|
# email protocols
|
||||||
|
eml-codec = "0.1.2"
|
||||||
|
smtp-message = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
|
||||||
|
smtp-server = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
|
||||||
|
imap-codec = { version = "2.0.0", features = ["bounded-static", "ext_condstore_qresync"] }
|
||||||
|
imap-flow = { git = "https://github.com/duesee/imap-flow.git", branch = "main" }
|
||||||
|
|
||||||
|
# http & web
|
||||||
|
http-body-util = "0.1"
|
||||||
|
hyper = "1.2"
|
||||||
|
hyper-rustls = { version = "0.26", features = ["http2"] }
|
||||||
|
hyper-util = { version = "0.1", features = ["full"] }
|
||||||
|
|
||||||
|
# serialization, compression & parsing
|
||||||
serde = "1.0.137"
|
serde = "1.0.137"
|
||||||
rmp-serde = "0.15"
|
rmp-serde = "0.15"
|
||||||
toml = "0.5"
|
toml = "0.5"
|
||||||
base64 = "0.21"
|
base64 = "0.21"
|
||||||
hex = "0.4"
|
hex = "0.4"
|
||||||
nom = "7.1"
|
nom = "7.1"
|
||||||
|
quick-xml = "0.31"
|
||||||
zstd = { version = "0.9", default-features = false }
|
zstd = { version = "0.9", default-features = false }
|
||||||
|
|
||||||
# cryptography & security
|
# cryptography & security
|
||||||
|
@ -48,8 +63,6 @@ rand = "0.8.5"
|
||||||
rustls = "0.22"
|
rustls = "0.22"
|
||||||
rustls-pemfile = "2.0"
|
rustls-pemfile = "2.0"
|
||||||
tokio-rustls = "0.25"
|
tokio-rustls = "0.25"
|
||||||
hyper-rustls = { version = "0.26", features = ["http2"] }
|
|
||||||
hyper-util = { version = "0.1", features = ["full"] }
|
|
||||||
rpassword = "7.0"
|
rpassword = "7.0"
|
||||||
|
|
||||||
# login
|
# login
|
||||||
|
@ -62,14 +75,6 @@ aws-sdk-s3 = "1"
|
||||||
aws-smithy-runtime = "1"
|
aws-smithy-runtime = "1"
|
||||||
aws-smithy-runtime-api = "1"
|
aws-smithy-runtime-api = "1"
|
||||||
|
|
||||||
# email protocols
|
|
||||||
eml-codec = "0.1.2"
|
|
||||||
smtp-message = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
|
|
||||||
smtp-server = { git = "http://github.com/Alexis211/kannader", branch = "feature/lmtp" }
|
|
||||||
imap-codec = { version = "2.0.0", features = ["bounded-static", "ext_condstore_qresync"] }
|
|
||||||
imap-flow = { git = "https://github.com/duesee/imap-flow.git", branch = "main" }
|
|
||||||
thiserror = "1.0.56"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
|
|
|
@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
||||||
pub struct CompanionConfig {
|
pub struct CompanionConfig {
|
||||||
pub pid: Option<PathBuf>,
|
pub pid: Option<PathBuf>,
|
||||||
pub imap: ImapUnsecureConfig,
|
pub imap: ImapUnsecureConfig,
|
||||||
|
// @FIXME Add DAV
|
||||||
|
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub users: LoginStaticConfig,
|
pub users: LoginStaticConfig,
|
||||||
|
@ -22,6 +23,7 @@ pub struct ProviderConfig {
|
||||||
pub imap_unsecure: Option<ImapUnsecureConfig>,
|
pub imap_unsecure: Option<ImapUnsecureConfig>,
|
||||||
pub lmtp: Option<LmtpConfig>,
|
pub lmtp: Option<LmtpConfig>,
|
||||||
pub auth: Option<AuthConfig>,
|
pub auth: Option<AuthConfig>,
|
||||||
|
pub dav_unsecure: Option<DavUnsecureConfig>,
|
||||||
pub users: UserManagement,
|
pub users: UserManagement,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +53,11 @@ pub struct ImapConfig {
|
||||||
pub key: PathBuf,
|
pub key: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct DavUnsecureConfig {
|
||||||
|
pub bind_addr: SocketAddr,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ImapUnsecureConfig {
|
pub struct ImapUnsecureConfig {
|
||||||
pub bind_addr: SocketAddr,
|
pub bind_addr: SocketAddr,
|
||||||
|
|
75
src/dav/mod.rs
Normal file
75
src/dav/mod.rs
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use hyper::service::service_fn;
|
||||||
|
use hyper::{Request, Response, body::Bytes};
|
||||||
|
use hyper::server::conn::http1 as http;
|
||||||
|
use hyper_util::rt::TokioIo;
|
||||||
|
use http_body_util::Full;
|
||||||
|
use futures::stream::{FuturesUnordered, StreamExt};
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tokio::sync::watch;
|
||||||
|
|
||||||
|
use crate::config::DavUnsecureConfig;
|
||||||
|
use crate::login::ArcLoginProvider;
|
||||||
|
|
||||||
|
pub struct Server {
|
||||||
|
bind_addr: SocketAddr,
|
||||||
|
login_provider: ArcLoginProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_unsecure(config: DavUnsecureConfig, login: ArcLoginProvider) -> Server {
|
||||||
|
Server {
|
||||||
|
bind_addr: config.bind_addr,
|
||||||
|
login_provider: login,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Server {
|
||||||
|
pub async fn run(self: Self, mut must_exit: watch::Receiver<bool>) -> Result<()> {
|
||||||
|
let tcp = TcpListener::bind(self.bind_addr).await?;
|
||||||
|
tracing::info!("DAV server listening on {:#}", self.bind_addr);
|
||||||
|
|
||||||
|
let mut connections = FuturesUnordered::new();
|
||||||
|
while !*must_exit.borrow() {
|
||||||
|
let wait_conn_finished = async {
|
||||||
|
if connections.is_empty() {
|
||||||
|
futures::future::pending().await
|
||||||
|
} else {
|
||||||
|
connections.next().await
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let (socket, remote_addr) = tokio::select! {
|
||||||
|
a = tcp.accept() => a?,
|
||||||
|
_ = wait_conn_finished => continue,
|
||||||
|
_ = must_exit.changed() => continue,
|
||||||
|
};
|
||||||
|
tracing::info!("DAV: accepted connection from {}", remote_addr);
|
||||||
|
let stream = TokioIo::new(socket);
|
||||||
|
let conn = tokio::spawn(async {
|
||||||
|
//@FIXME should create a generic "public web" server on which "routers" could be
|
||||||
|
//abitrarily bound
|
||||||
|
//@FIXME replace with a handler supporting http2 and TLS
|
||||||
|
match http::Builder::new().serve_connection(stream, service_fn(router)).await {
|
||||||
|
Err(e) => tracing::warn!(err=?e, "connection failed"),
|
||||||
|
Ok(()) => tracing::trace!("connection terminated with success"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connections.push(conn);
|
||||||
|
}
|
||||||
|
drop(tcp);
|
||||||
|
|
||||||
|
tracing::info!("DAV server shutting down, draining remaining connections...");
|
||||||
|
while connections.next().await.is_some() {}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn router(req: Request<impl hyper::body::Body>) -> Result<Response<Full<Bytes>>> {
|
||||||
|
let url_exploded: Vec<_> = req.uri().path().split(",").collect();
|
||||||
|
match url_exploded {
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
Ok(Response::new(Full::new(Bytes::from("Hello World!"))))
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ mod auth;
|
||||||
mod bayou;
|
mod bayou;
|
||||||
mod config;
|
mod config;
|
||||||
mod cryptoblob;
|
mod cryptoblob;
|
||||||
|
mod dav;
|
||||||
mod imap;
|
mod imap;
|
||||||
mod k2v_util;
|
mod k2v_util;
|
||||||
mod lmtp;
|
mod lmtp;
|
||||||
|
@ -187,6 +188,9 @@ async fn main() -> Result<()> {
|
||||||
imap_unsecure: Some(ImapUnsecureConfig {
|
imap_unsecure: Some(ImapUnsecureConfig {
|
||||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1143),
|
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1143),
|
||||||
}),
|
}),
|
||||||
|
dav_unsecure: Some(DavUnsecureConfig {
|
||||||
|
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8087),
|
||||||
|
}),
|
||||||
lmtp: Some(LmtpConfig {
|
lmtp: Some(LmtpConfig {
|
||||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
||||||
hostname: "example.tld".to_string(),
|
hostname: "example.tld".to_string(),
|
||||||
|
|
|
@ -9,6 +9,7 @@ use tokio::sync::watch;
|
||||||
|
|
||||||
use crate::auth;
|
use crate::auth;
|
||||||
use crate::config::*;
|
use crate::config::*;
|
||||||
|
use crate::dav;
|
||||||
use crate::imap;
|
use crate::imap;
|
||||||
use crate::lmtp::*;
|
use crate::lmtp::*;
|
||||||
use crate::login::ArcLoginProvider;
|
use crate::login::ArcLoginProvider;
|
||||||
|
@ -19,6 +20,7 @@ pub struct Server {
|
||||||
imap_unsecure_server: Option<imap::Server>,
|
imap_unsecure_server: Option<imap::Server>,
|
||||||
imap_server: Option<imap::Server>,
|
imap_server: Option<imap::Server>,
|
||||||
auth_server: Option<auth::AuthServer>,
|
auth_server: Option<auth::AuthServer>,
|
||||||
|
dav_unsecure_server: Option<dav::Server>,
|
||||||
pid_file: Option<PathBuf>,
|
pid_file: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ impl Server {
|
||||||
imap_unsecure_server,
|
imap_unsecure_server,
|
||||||
imap_server: None,
|
imap_server: None,
|
||||||
auth_server: None,
|
auth_server: None,
|
||||||
|
dav_unsecure_server: None,
|
||||||
pid_file: config.pid,
|
pid_file: config.pid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -57,11 +60,15 @@ impl Server {
|
||||||
let auth_server = config
|
let auth_server = config
|
||||||
.auth
|
.auth
|
||||||
.map(|auth| auth::AuthServer::new(auth, login.clone()));
|
.map(|auth| auth::AuthServer::new(auth, login.clone()));
|
||||||
|
let dav_unsecure_server = config
|
||||||
|
.dav_unsecure
|
||||||
|
.map(|dav_config| dav::new_unsecure(dav_config, login.clone()));
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
lmtp_server,
|
lmtp_server,
|
||||||
imap_unsecure_server,
|
imap_unsecure_server,
|
||||||
imap_server,
|
imap_server,
|
||||||
|
dav_unsecure_server,
|
||||||
auth_server,
|
auth_server,
|
||||||
pid_file: config.pid,
|
pid_file: config.pid,
|
||||||
})
|
})
|
||||||
|
@ -112,6 +119,12 @@ impl Server {
|
||||||
None => Ok(()),
|
None => Ok(()),
|
||||||
Some(a) => a.run(exit_signal.clone()).await,
|
Some(a) => a.run(exit_signal.clone()).await,
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async {
|
||||||
|
match self.dav_unsecure_server {
|
||||||
|
None => Ok(()),
|
||||||
|
Some(s) => s.run(exit_signal.clone()).await,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue