mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-26 15:46:35 +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
|
||||
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 ---
|
||||
systemctl enable seatd
|
||||
systemctl enable betterframe-kiosk.service
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ fn run_subscription(
|
|||
let pass = password.unwrap_or("");
|
||||
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 {
|
||||
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() {
|
||||
let status = resp.status();
|
||||
let body = resp.text().unwrap_or_default();
|
||||
let preview: String = body.chars().take(500).collect();
|
||||
return Err(format!("soap HTTP {status}: {preview}"));
|
||||
let fault = extract_soap_fault(&body);
|
||||
return Err(format!("soap HTTP {status}: {fault}"));
|
||||
}
|
||||
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> {
|
||||
let header = wsse_header(user, pass);
|
||||
let body = soap_envelope(
|
||||
|
|
|
|||
Loading…
Reference in a new issue