v0.1.4
This commit is contained in:
1 parent
19dfc51360
commit
b6e8ddb9c7
31 files changed
+1080
-32
No files matched your search
+84
-21
@@ -62,16 +62,80 @@ fn safe_host(h: &str) -> Option<&str> {
|
||||
.then_some(h)
|
||||
}
|
||||
|
||||
/// Every attempt is logged and shown in the panel (Live), including the unauthenticated
|
||||
/// first request iOS sends and failures - this is what makes "my phone still asks for the
|
||||
/// server" debuggable.
|
||||
pub async fn handler(
|
||||
State(app): State<Arc<App>>,
|
||||
ConnectInfo(peer): ConnectInfo<SocketAddr>,
|
||||
method: Method,
|
||||
uri: axum::http::Uri,
|
||||
headers: HeaderMap,
|
||||
body: Body,
|
||||
) -> Response {
|
||||
let s = app.settings();
|
||||
let started = std::time::Instant::now();
|
||||
let ip = client_ip(peer, &headers, &s).to_string();
|
||||
let hdr = |n: &str| {
|
||||
headers
|
||||
.get(n)
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.unwrap_or("")
|
||||
.chars()
|
||||
.filter(|c| !c.is_control())
|
||||
.take(120)
|
||||
.collect::<String>()
|
||||
};
|
||||
let host = if hdr("x-forwarded-host").is_empty() {
|
||||
hdr("host")
|
||||
} else {
|
||||
hdr("x-forwarded-host")
|
||||
};
|
||||
let ua = hdr("user-agent");
|
||||
let user = basic_auth(&headers, &s)
|
||||
.map(|(l, _)| l)
|
||||
.unwrap_or_else(|| "-".into());
|
||||
let size = hdr("content-length").parse::<u64>().unwrap_or(0);
|
||||
let resp = inner(app.clone(), peer, method.clone(), headers, body).await;
|
||||
let mut resp = resp;
|
||||
resp.headers_mut()
|
||||
.insert("X-ActivePush", HeaderValue::from_static("autodiscover"));
|
||||
let code = resp.status().as_u16();
|
||||
let status = match code {
|
||||
200 => "ok",
|
||||
401 => "auth required",
|
||||
429 => "locked out",
|
||||
503 => "backend unavailable",
|
||||
_ => "error",
|
||||
};
|
||||
tracing::info!(target: "active_push::autodiscover", ip = %ip, host = %host, path = %uri.path(), method = %method, http = code, user = %user, ua = %ua, "autodiscover {status}");
|
||||
app.metrics.record(Event {
|
||||
ts: now(),
|
||||
user,
|
||||
device_id: String::new(),
|
||||
device_type: "Autodiscover".into(),
|
||||
command: "Autodiscover".into(),
|
||||
ip,
|
||||
http: code,
|
||||
status: format!("{status} ({host})"),
|
||||
ms: started.elapsed().as_millis() as u64,
|
||||
bytes_in: size,
|
||||
bytes_out: 0,
|
||||
items_out: 0,
|
||||
items_in: 0,
|
||||
});
|
||||
resp
|
||||
}
|
||||
|
||||
async fn inner(
|
||||
app: Arc<App>,
|
||||
peer: SocketAddr,
|
||||
method: Method,
|
||||
headers: HeaderMap,
|
||||
body: Body,
|
||||
) -> Response {
|
||||
let s = app.settings();
|
||||
let ip = client_ip(peer, &headers, &s).to_string();
|
||||
let started = std::time::Instant::now();
|
||||
app.metrics
|
||||
.c
|
||||
.autodiscover
|
||||
@@ -101,11 +165,22 @@ pub async fn handler(
|
||||
Err(_) => return xml_response(StatusCode::OK, error_xml(600, "Invalid Request")),
|
||||
}
|
||||
};
|
||||
if schema != MOBILESYNC {
|
||||
tracing::info!(ip = %ip, schema = %schema, "autodiscover: unsupported response schema");
|
||||
if !schema.is_empty() && schema != MOBILESYNC {
|
||||
tracing::info!(ip = %ip, schema = %schema, "autodiscover: unsupported response schema (only MobileSync is served)");
|
||||
return xml_response(StatusCode::OK, error_xml(600, "Invalid Request"));
|
||||
}
|
||||
|
||||
// only the configured domains (when a list is set)
|
||||
if !s.autodiscover.domains.is_empty() {
|
||||
if let Some((_, dom)) = email.rsplit_once('@') {
|
||||
let dom = crate::autodiag::normalize_domain(dom);
|
||||
if !s.autodiscover.serves(&dom) {
|
||||
tracing::info!(ip = %ip, domain = %dom, "autodiscover: domain not in the configured list");
|
||||
return xml_response(StatusCode::OK, error_xml(600, "Invalid Request"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// authentication
|
||||
let creds = basic_auth(&headers, &s);
|
||||
let login = match (&creds, s.autodiscover.require_auth) {
|
||||
@@ -158,8 +233,12 @@ pub async fn handler(
|
||||
if email.is_empty() || !email.contains('@') {
|
||||
email = crate::backend::primary_email(&s, &login);
|
||||
}
|
||||
let url = if !s.autodiscover.server_url.is_empty() {
|
||||
s.autodiscover.server_url.clone()
|
||||
let configured = email
|
||||
.rsplit_once('@')
|
||||
.map(|(_, d)| s.autodiscover.url_for(d))
|
||||
.unwrap_or_else(|| s.autodiscover.server_url.trim().to_string());
|
||||
let url = if !configured.is_empty() {
|
||||
configured
|
||||
} else {
|
||||
let host = headers
|
||||
.get("x-forwarded-host")
|
||||
@@ -195,21 +274,5 @@ pub async fn handler(
|
||||
xml_escape(&url),
|
||||
xml_escape(&url)
|
||||
);
|
||||
tracing::info!(user = %login, ip = %ip, "autodiscover answered");
|
||||
app.metrics.record(Event {
|
||||
ts: now(),
|
||||
user: login,
|
||||
device_id: String::new(),
|
||||
device_type: "Autodiscover".into(),
|
||||
command: "Autodiscover".into(),
|
||||
ip,
|
||||
http: 200,
|
||||
status: "ok".into(),
|
||||
ms: started.elapsed().as_millis() as u64,
|
||||
bytes_in: body.len() as u64,
|
||||
bytes_out: xml.len() as u64,
|
||||
items_out: 0,
|
||||
items_in: 0,
|
||||
});
|
||||
xml_response(StatusCode::OK, xml)
|
||||
}
|
||||
Reference in new issue
Block a user