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]]
|
||||
name = "aerogramme"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
|
@ -46,6 +46,8 @@ dependencies = [
|
|||
"eml-codec",
|
||||
"futures",
|
||||
"hex",
|
||||
"http-body-util",
|
||||
"hyper 1.2.0",
|
||||
"hyper-rustls 0.26.0",
|
||||
"hyper-util",
|
||||
"im",
|
||||
|
@ -58,6 +60,7 @@ dependencies = [
|
|||
"log",
|
||||
"nix",
|
||||
"nom 7.1.3",
|
||||
"quick-xml",
|
||||
"rand",
|
||||
"rmp-serde",
|
||||
"rpassword",
|
||||
|
@ -2691,6 +2694,15 @@ dependencies = [
|
|||
"prost",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.31.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
|
|
29
Cargo.toml
29
Cargo.toml
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "aerogramme"
|
||||
version = "0.2.2"
|
||||
version = "0.3.0"
|
||||
authors = ["Alex Auvolat <alex@adnab.me>", "Quentin Dufour <quentin@dufour.io>"]
|
||||
edition = "2021"
|
||||
license = "EUPL-1.2"
|
||||
|
@ -18,6 +18,7 @@ backtrace = "0.3"
|
|||
console-subscriber = "0.2"
|
||||
tracing-subscriber = "0.3"
|
||||
tracing = "0.1"
|
||||
thiserror = "1.0.56"
|
||||
|
||||
# language extensions
|
||||
lazy_static = "1.4"
|
||||
|
@ -32,13 +33,27 @@ chrono = { version = "0.4", default-features = false, features = ["alloc"] }
|
|||
nix = { version = "0.27", features = ["signal"] }
|
||||
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"
|
||||
rmp-serde = "0.15"
|
||||
toml = "0.5"
|
||||
base64 = "0.21"
|
||||
hex = "0.4"
|
||||
nom = "7.1"
|
||||
quick-xml = "0.31"
|
||||
zstd = { version = "0.9", default-features = false }
|
||||
|
||||
# cryptography & security
|
||||
|
@ -48,8 +63,6 @@ rand = "0.8.5"
|
|||
rustls = "0.22"
|
||||
rustls-pemfile = "2.0"
|
||||
tokio-rustls = "0.25"
|
||||
hyper-rustls = { version = "0.26", features = ["http2"] }
|
||||
hyper-util = { version = "0.1", features = ["full"] }
|
||||
rpassword = "7.0"
|
||||
|
||||
# login
|
||||
|
@ -62,14 +75,6 @@ aws-sdk-s3 = "1"
|
|||
aws-smithy-runtime = "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]
|
||||
|
||||
[patch.crates-io]
|
||||
|
|
|
@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
|
|||
pub struct CompanionConfig {
|
||||
pub pid: Option<PathBuf>,
|
||||
pub imap: ImapUnsecureConfig,
|
||||
// @FIXME Add DAV
|
||||
|
||||
#[serde(flatten)]
|
||||
pub users: LoginStaticConfig,
|
||||
|
@ -22,6 +23,7 @@ pub struct ProviderConfig {
|
|||
pub imap_unsecure: Option<ImapUnsecureConfig>,
|
||||
pub lmtp: Option<LmtpConfig>,
|
||||
pub auth: Option<AuthConfig>,
|
||||
pub dav_unsecure: Option<DavUnsecureConfig>,
|
||||
pub users: UserManagement,
|
||||
}
|
||||
|
||||
|
@ -51,6 +53,11 @@ pub struct ImapConfig {
|
|||
pub key: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct DavUnsecureConfig {
|
||||
pub bind_addr: SocketAddr,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ImapUnsecureConfig {
|
||||
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 config;
|
||||
mod cryptoblob;
|
||||
mod dav;
|
||||
mod imap;
|
||||
mod k2v_util;
|
||||
mod lmtp;
|
||||
|
@ -187,6 +188,9 @@ async fn main() -> Result<()> {
|
|||
imap_unsecure: Some(ImapUnsecureConfig {
|
||||
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 {
|
||||
bind_addr: SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 1025),
|
||||
hostname: "example.tld".to_string(),
|
||||
|
|
|
@ -9,6 +9,7 @@ use tokio::sync::watch;
|
|||
|
||||
use crate::auth;
|
||||
use crate::config::*;
|
||||
use crate::dav;
|
||||
use crate::imap;
|
||||
use crate::lmtp::*;
|
||||
use crate::login::ArcLoginProvider;
|
||||
|
@ -19,6 +20,7 @@ pub struct Server {
|
|||
imap_unsecure_server: Option<imap::Server>,
|
||||
imap_server: Option<imap::Server>,
|
||||
auth_server: Option<auth::AuthServer>,
|
||||
dav_unsecure_server: Option<dav::Server>,
|
||||
pid_file: Option<PathBuf>,
|
||||
}
|
||||
|
||||
|
@ -34,6 +36,7 @@ impl Server {
|
|||
imap_unsecure_server,
|
||||
imap_server: None,
|
||||
auth_server: None,
|
||||
dav_unsecure_server: None,
|
||||
pid_file: config.pid,
|
||||
})
|
||||
}
|
||||
|
@ -57,11 +60,15 @@ impl Server {
|
|||
let auth_server = config
|
||||
.auth
|
||||
.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 {
|
||||
lmtp_server,
|
||||
imap_unsecure_server,
|
||||
imap_server,
|
||||
dav_unsecure_server,
|
||||
auth_server,
|
||||
pid_file: config.pid,
|
||||
})
|
||||
|
@ -112,6 +119,12 @@ impl Server {
|
|||
None => Ok(()),
|
||||
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