mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 17:56:34 +00:00
fix(onvif): extract SOAP fault reason + log auth state + NTP setup
SOAP errors now extract fault Reason/Text/Code from XML instead of dumping raw envelope. Logs whether ONVIF password was decrypted (has_pass=true/false). Added NTP config to pi-gen (pool.ntp.org + Google/Cloudflare fallback) — WSSE PasswordDigest fails with clock skew. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
66653af360
commit
de0a76e01d
2 changed files with 46 additions and 3 deletions
|
|
@ -152,6 +152,15 @@ Inherits=betterframe-empty
|
||||||
CURSOR
|
CURSOR
|
||||||
chown bfkiosk:bfkiosk /home/bfkiosk/.icons/default/index.theme
|
chown bfkiosk:bfkiosk /home/bfkiosk/.icons/default/index.theme
|
||||||
|
|
||||||
|
# --- NTP — critical for WSSE auth (camera checks timestamp) ---
|
||||||
|
mkdir -p /etc/systemd/timesyncd.conf.d
|
||||||
|
cat > /etc/systemd/timesyncd.conf.d/betterframe.conf <<'NTP'
|
||||||
|
[Time]
|
||||||
|
NTP=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org 3.pool.ntp.org
|
||||||
|
FallbackNTP=time.google.com time.cloudflare.com
|
||||||
|
NTP
|
||||||
|
systemctl enable systemd-timesyncd 2>/dev/null || true
|
||||||
|
|
||||||
# --- Enable services, disable noise ---
|
# --- Enable services, disable noise ---
|
||||||
systemctl enable seatd
|
systemctl enable seatd
|
||||||
systemctl enable betterframe-kiosk.service
|
systemctl enable betterframe-kiosk.service
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,8 @@ fn run_subscription(
|
||||||
let pass = password.unwrap_or("");
|
let pass = password.unwrap_or("");
|
||||||
let event_url = format!("http://{host}:{port}/onvif/event_service");
|
let event_url = format!("http://{host}:{port}/onvif/event_service");
|
||||||
|
|
||||||
info!("onvif-events: cam {} ({}) subscribing at {event_url}", cam.id, cam.name);
|
let has_pass = !pass.is_empty();
|
||||||
|
info!("onvif-events: cam {} ({}) subscribing at {event_url} user={user} has_pass={has_pass}", cam.id, cam.name);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if generation.upgrade().is_none() {
|
if generation.upgrade().is_none() {
|
||||||
|
|
@ -258,12 +259,45 @@ fn soap_post(url: &str, action: &str, body: &str) -> Result<String, String> {
|
||||||
if !resp.status().is_success() {
|
if !resp.status().is_success() {
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let body = resp.text().unwrap_or_default();
|
let body = resp.text().unwrap_or_default();
|
||||||
let preview: String = body.chars().take(500).collect();
|
let fault = extract_soap_fault(&body);
|
||||||
return Err(format!("soap HTTP {status}: {preview}"));
|
return Err(format!("soap HTTP {status}: {fault}"));
|
||||||
}
|
}
|
||||||
resp.text().map_err(|e| format!("soap body: {e}"))
|
resp.text().map_err(|e| format!("soap body: {e}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extract a human-readable fault reason from SOAP XML, stripping envelope noise.
|
||||||
|
fn extract_soap_fault(xml: &str) -> String {
|
||||||
|
// Try common SOAP fault tags
|
||||||
|
for tag in &["Reason", "Text", "faultstring", "Detail", "Subcode"] {
|
||||||
|
if let Some(val) = extract_tag_ns(xml, tag) {
|
||||||
|
let trimmed = val.trim();
|
||||||
|
if !trimmed.is_empty() {
|
||||||
|
return trimmed.to_string();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Try Code/Value
|
||||||
|
if let Some(val) = extract_tag_ns(xml, "Value") {
|
||||||
|
let trimmed = val.trim();
|
||||||
|
if !trimmed.is_empty() {
|
||||||
|
return format!("Code: {trimmed}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Fallback: first 300 chars stripped of XML tags
|
||||||
|
let stripped: String = xml.replace(|c: char| c == '<', "\n<")
|
||||||
|
.lines()
|
||||||
|
.filter(|l| !l.trim_start().starts_with('<'))
|
||||||
|
.map(|l| l.trim())
|
||||||
|
.filter(|l| !l.is_empty())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" ");
|
||||||
|
if stripped.is_empty() {
|
||||||
|
xml.chars().take(300).collect()
|
||||||
|
} else {
|
||||||
|
stripped.chars().take(300).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_pullpoint(url: &str, user: &str, pass: &str) -> Result<Subscription, String> {
|
fn create_pullpoint(url: &str, user: &str, pass: &str) -> Result<Subscription, String> {
|
||||||
let header = wsse_header(user, pass);
|
let header = wsse_header(user, pass);
|
||||||
let body = soap_envelope(
|
let body = soap_envelope(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue