diff --git a/onvif/examples/camera.rs b/onvif/examples/camera.rs index 2d6cb68..883601c 100644 --- a/onvif/examples/camera.rs +++ b/onvif/examples/camera.rs @@ -1,7 +1,8 @@ -use onvif::soap; +use chrono::{NaiveDate, Utc}; +use onvif::soap::{self, client::AuthType}; use schema::{self, transport}; use structopt::StructOpt; -use tracing::debug; +use tracing::{debug, warn}; use url::Url; #[derive(StructOpt)] @@ -24,6 +25,14 @@ struct Args { #[structopt(global = true, long, default_value = "onvif/device_service")] service_path: String, + /// Auto fix time gap between PC and the camera + #[structopt(short = "t", long)] + fix_time: bool, + + /// Authorization type [Any(Default), Digest, UsernameToken] + #[structopt(short = "a", long, default_value = "Any")] + auth_type: String, + #[structopt(subcommand)] cmd: Cmd, } @@ -92,9 +101,15 @@ impl Clients { .as_ref() .ok_or_else(|| "--uri must be specified.".to_string())?; let devicemgmt_uri = base_uri.join(&args.service_path).unwrap(); + let auth_type = match args.auth_type.to_ascii_lowercase().as_str() { + "digest" => AuthType::Digest, + "usernametoken" => AuthType::UsernameToken, + _ => AuthType::Any, + }; let mut out = Self { devicemgmt: soap::client::ClientBuilder::new(&devicemgmt_uri) .credentials(creds.clone()) + .auth_type(auth_type.clone()) .build(), imaging: None, ptz: None, @@ -104,6 +119,36 @@ impl Clients { media2: None, analytics: None, }; + + let time_gap = if args.fix_time { + let device_time = + schema::devicemgmt::get_system_date_and_time(&out.devicemgmt, &Default::default()) + .await? + .system_date_and_time; + + if let Some(utc_time) = &device_time.utc_date_time { + let pc_time = Utc::now(); + let date = &utc_time.date; + let t = &utc_time.time; + let device_time = + NaiveDate::from_ymd_opt(date.year, date.month as _, date.day as _) + .unwrap() + .and_hms_opt(t.hour as _, t.minute as _, t.second as _) + .unwrap() + .and_utc(); + + let diff = device_time - pc_time; + if diff.num_seconds().abs() > 60 { + out.devicemgmt.set_fix_time_gap(Some(diff)); + } + Some(diff) + } else { + warn!("GetSystemDateAndTimeResponse doesn't have utc_data_time value!"); + None + } + } else { + None + }; let services = schema::devicemgmt::get_services(&out.devicemgmt, &Default::default()).await?; for service in &services.service { @@ -117,6 +162,8 @@ impl Clients { let svc = Some( soap::client::ClientBuilder::new(&service_url) .credentials(creds.clone()) + .auth_type(auth_type.clone()) + .fix_time_gap(time_gap) .build(), ); match service.namespace.as_str() { diff --git a/onvif/src/discovery/mod.rs b/onvif/src/discovery/mod.rs index c299adb..b224aba 100644 --- a/onvif/src/discovery/mod.rs +++ b/onvif/src/discovery/mod.rs @@ -481,7 +481,7 @@ mod tests { let our_uuid = "uuid:84ede3de-7dec-11d0-c360-F01234567890"; let bad_uuid = "uuid:84ede3de-7dec-11d0-c360-F00000000000"; - let input = vec![ + let input = [ make_xml(our_uuid, "http://addr_20 http://addr_21 http://addr_22"), make_xml(bad_uuid, "http://addr_30 http://addr_31"), ]; diff --git a/onvif/src/soap/auth/username_token.rs b/onvif/src/soap/auth/username_token.rs index a2622bf..8329df9 100644 --- a/onvif/src/soap/auth/username_token.rs +++ b/onvif/src/soap/auth/username_token.rs @@ -7,10 +7,22 @@ pub struct UsernameToken { } impl UsernameToken { - pub fn new(username: &str, password: &str) -> UsernameToken { + pub fn new( + username: &str, + password: &str, + fix_time_gap: Option, + ) -> UsernameToken { let uuid = uuid::Uuid::new_v4(); let nonce = uuid.as_bytes(); - let created = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true); + + let mut created = chrono::Utc::now(); + if let Some(time_gap) = fix_time_gap { + created = match created.checked_add_signed(time_gap) { + Some(t) => t, + None => chrono::Utc::now(), + }; + } + let created = created.to_rfc3339_opts(chrono::SecondsFormat::Millis, true); let mut concat = Vec::with_capacity(nonce.len() + created.len() + password.len()); diff --git a/onvif/src/soap/client.rs b/onvif/src/soap/client.rs index cc9b179..f8d2dc7 100644 --- a/onvif/src/soap/client.rs +++ b/onvif/src/soap/client.rs @@ -50,6 +50,7 @@ impl ClientBuilder { response_patcher: None, auth_type: AuthType::Any, timeout: ClientBuilder::DEFAULT_TIMEOUT, + fix_time_gap: None, }, } } @@ -79,6 +80,11 @@ impl ClientBuilder { self } + pub fn fix_time_gap(mut self, time_gap: Option) -> Self { + self.config.fix_time_gap = time_gap; + self + } + pub fn build(self) -> Client { let client = if let Some(client) = self.client { client @@ -121,6 +127,7 @@ struct Config { response_patcher: Option, auth_type: AuthType, timeout: Duration, + fix_time_gap: Option, } #[derive(Clone, Debug)] @@ -314,6 +321,10 @@ impl Client { self.config .credentials .as_ref() - .map(|c| UsernameToken::new(&c.username, &c.password)) + .map(|c| UsernameToken::new(&c.username, &c.password, self.config.fix_time_gap)) + } + + pub fn set_fix_time_gap(&mut self, time_gap: Option) { + self.config.fix_time_gap = time_gap; } }