Critical
- Renew busy-loop on persistent failure: after a failed
renewPullPoint, the loop re-read the (now in-past)
GrantedTermination and nextRenewInterval floored to 1s, hammering
the camera at 1 Hz until reconnect. nextRenewIntervalAfterError
decouples the failure path from the stale grant and backs off
at opts.RetryBackoff. renewLoop also routes through s.now() so
test clocks can drive it deterministically.
- Lost-update race on GrantedTermination: a renew result for an
old subscription could overwrite the grant on a new one if
attemptRecreate swapped pullPoint mid-flight. A generation
counter on Stream tracks subscription rotation; the renew loop
captures the generation before the SOAP call and discards the
result if the subscription was rotated.
- Credential leak when <Security> straddled the 64 KiB error cap:
the non-greedy regex required a closing tag and missed the
truncated case. wsseSecurityRE now matches close-tag-or-EOF.
Belt-and-braces wssePasswordRE redacts <Password> elements
outside any Security wrapper.
- addHeaderChildren accepted well-formed-but-element-free input
and produced a header-less request. Now errors out.
Important
- Wrapper detection in buildRefParamsHeader was HasSuffix-based and
misfired on children named *ReferenceParameters. Replaced with the
unambiguous wrapper-only contract: input must be the full
<*:ReferenceParameters> element returned by extractReferenceParameters.
- Renamed SoapOption → SendSoapOption and WithHeader → WithSOAPHeader
to disambiguate at call sites.
- Renamed WithHeader's `xml` parameter to headerContent to avoid
shadowing the encoding/xml package name.
- Documented terminationTimeRE's "first match only" semantics so
nobody re-uses it from PullMessages context where multiple
TerminationTime elements appear.
- goleak is now a direct require (go mod tidy).
Performance
- Added gosoap.AddStringHeaderContents (plural) for multi-root
header content. AddStringHeaderContent remains as-is for
backwards compatibility with external consumers. Device.go's
addHeaderChildren workaround is gone — one etree parse per
SendSoapWithOptions call instead of two.
Migration
- Device.go's own CallOnvifFunction and the three examples now
call SendSoapWithOptions, modelling the canonical path.
Docs / tests
- Trust-boundary warning on SendSoapWithHeader/Options godoc.
- Comments on createPullPointResp/Alt explain why TerminationTime
is intentionally omitted (renew timing fixtures).
- New tests: digest retry strips WS-Security, duplicate
WithSOAPHeader is last-wins, malformed TerminationTime yields
zero, margin==base falls to base/2, empty SubscriptionReference
yields empty ref params, Security straddling cap is redacted,
bare Password redacted, wrapper-only contract is enforced.
Onvif library
Simple management of onvif IP-devices cameras. onvif is an implementation of ONVIF protocol for managing onvif IP devices. The purpose of this library is convenient and easy management of IP cameras and other devices that support ONVIF standard.
Overview
This repository is forked from: use-go/onvif
Supported services
The following services are implemented:
- Device
- Media
- PTZ
- Event
- Discovery
Using
General concept
- Connecting to the device
- Authentication (if necessary)
- Defining Data Types
- Carrying out the required method
Connecting to the device
If there is a device on the network at the address 192.168.13.42, and its ONVIF services use the 1234 port, then you can connect to the device in the following way:
dev, err := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234"})
The ONVIF port may differ depending on the device , to find out which port to use, you can go to the web interface of the device. Usually this is 80 port.
Authentication
If any function of the ONVIF services requires authentication, you must use the Authenticate method.
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
Defining Data Types
Each ONVIF service in this library has its own package, in which all data types of this service are defined, and the package name is identical to the service name and begins with a capital letter. onvif defines the structures for each function of each ONVIF service supported by this library. Define the data type of the GetCapabilities function of the Device service. This is done as follows:
capabilities := device.GetCapabilities{Category:"All"}
Why does the GetCapabilities structure have the Category field and why is the value of this field All?
The figure below shows the documentation for the GetCapabilities. It can be seen that the function takes one Category parameter and its value should be one of the following: 'All', 'Analytics',' Device ',' Events', 'Imaging', 'Media' or 'PTZ'`.
An example of defining the data type of GetServiceCapabilities function in PTZ:
ptzCapabilities := ptz.GetServiceCapabilities{}
The figure below shows that GetServiceCapabilities does not accept any arguments.
Common data types are in the xsd/onvif package. The types of data (structures) that can be shared by all services are defined in the onvif package.
An example of how to define the data type of the CreateUsers function in Devicemgmt:
createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
The figure below shows that ,in this example, the CreateUsers structure field must be a User whose data type is the User structure containing the Username, Password, UserLevel, and optional Extension fields. The User structure is in the onvif package.
Carrying out the required method
To perform any function of one of the ONVIF services whose structure has been defined, you must use the CallMethod of the device object.
createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}}
device := onvif.NewDevice(onvif.DeviceParams{Xaddr: "192.168.13.42:1234", Username: "username", Password: password})
device.Authenticate("username", "password")
resp, err := dev.CallMethod(createUsers)
Development
See here


