├── .gitignore ├── Device.go ├── Imaging └── types.go ├── LICENSE ├── README.md ├── analytics └── types.go ├── api ├── api.go └── get_structs.go ├── device └── types.go ├── doc.go ├── docs ├── ONVIF-Core-Specification.pdf ├── README.md ├── img │ ├── GetServiceCapabilities.png │ ├── exmp_ContinuousMove.png │ ├── exmp_CreateUsers.png │ ├── exmp_GetCapabilities.png │ └── exmp_GetProfiles.png └── wsdl │ ├── WS-BaseNotification.wsdl │ ├── WS-Resource.wsdl │ ├── accesscontrol.wsdl │ ├── accessrules.wsdl │ ├── actionengine.wsdl │ ├── advancedsecurity.wsdl │ ├── analytics.wsdl │ ├── analyticsdevice.wsdl │ ├── credential.wsdl │ ├── deviceio.wsdl │ ├── devicemgmt.wsdl │ ├── display.wsdl │ ├── doorcontrol.wsdl │ ├── event-vs.wsdl │ ├── event.wsdl │ ├── imaging.wsdl │ ├── media.wsdl │ ├── media2.wsdl │ ├── provisioning.wsdl │ ├── ptz.wsdl │ ├── receiver.wsdl │ ├── recording.wsdl │ ├── replay.wsdl │ ├── schedule.wsdl │ ├── search.wsdl │ └── thermal.wsdl ├── event ├── operation.go └── types.go ├── examples ├── DeviceService.go └── discovery_test.go ├── go.mod ├── go.sum ├── gosoap ├── README.md ├── soap-builder.go ├── ws-action.go └── ws-security.go ├── media └── types.go ├── media2 └── types.go ├── networking └── networking.go ├── ptz └── types.go ├── ws-discovery ├── networking.go └── ws-discovery.go └── xsd ├── built_in.go ├── iso8601 └── iso8601_duration.go └── onvif ├── onvif.go ├── r-2.xsd ├── t-1.xsd ├── ws-addr.xsd └── xml.xsd /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .idea 3 | .idea/* 4 | 5 | -------------------------------------------------------------------------------- /Device.go: -------------------------------------------------------------------------------- 1 | package onvif 2 | 3 | import ( 4 | "encoding/xml" 5 | "errors" 6 | "fmt" 7 | "io/ioutil" 8 | "net/http" 9 | "reflect" 10 | "strconv" 11 | "strings" 12 | "time" 13 | 14 | "github.com/beevik/etree" 15 | "github.com/fanap-infra/onvif/device" 16 | "github.com/fanap-infra/onvif/gosoap" 17 | "github.com/fanap-infra/onvif/networking" 18 | wsdiscovery "github.com/fanap-infra/onvif/ws-discovery" 19 | ) 20 | 21 | // Xlmns XML Scheam 22 | var Xlmns = map[string]string{ 23 | "onvif": "http://www.onvif.org/ver10/schema", 24 | "tds": "http://www.onvif.org/ver10/device/wsdl", 25 | "trt": "http://www.onvif.org/ver10/media/wsdl", 26 | "tev": "http://www.onvif.org/ver10/events/wsdl", 27 | "tptz": "http://www.onvif.org/ver20/ptz/wsdl", 28 | "timg": "http://www.onvif.org/ver20/imaging/wsdl", 29 | "tan": "http://www.onvif.org/ver20/analytics/wsdl", 30 | "xmime": "http://www.w3.org/2005/05/xmlmime", 31 | "wsnt": "http://docs.oasis-open.org/wsn/b-2", 32 | "xop": "http://www.w3.org/2004/08/xop/include", 33 | "wsa": "http://www.w3.org/2005/08/addressing", 34 | "wstop": "http://docs.oasis-open.org/wsn/t-1", 35 | "wsntw": "http://docs.oasis-open.org/wsn/bw-2", 36 | "wsrf-rw": "http://docs.oasis-open.org/wsrf/rw-2", 37 | "wsaw": "http://www.w3.org/2006/05/addressing/wsdl", 38 | } 39 | 40 | // DeviceType alias for int 41 | type DeviceType int 42 | 43 | // Onvif Device Tyoe 44 | const ( 45 | NVD DeviceType = iota 46 | NVS 47 | NVA 48 | NVT 49 | ) 50 | 51 | func (devType DeviceType) String() string { 52 | stringRepresentation := []string{ 53 | "NetworkVideoDisplay", 54 | "NetworkVideoStorage", 55 | "NetworkVideoAnalytics", 56 | "NetworkVideoTransmitter", 57 | } 58 | i := uint8(devType) 59 | switch { 60 | case i <= uint8(NVT): 61 | return stringRepresentation[i] 62 | default: 63 | return strconv.Itoa(int(i)) 64 | } 65 | } 66 | 67 | // deviceInfo struct contains general information about ONVIF device 68 | type deviceInfo struct { 69 | Manufacturer string 70 | Model string 71 | FirmwareVersion string 72 | SerialNumber string 73 | HardwareId string 74 | } 75 | 76 | // Device for a new device of onvif and deviceInfo 77 | // struct represents an abstract ONVIF device. 78 | // It contains methods, which helps to communicate with ONVIF device 79 | type Device struct { 80 | xaddr string 81 | login string 82 | password string 83 | endpoints map[string]string 84 | info deviceInfo 85 | } 86 | 87 | // GetServices return available endpoints 88 | func (dev *Device) GetServices() map[string]string { 89 | return dev.endpoints 90 | } 91 | 92 | func readResponse(resp *http.Response) string { 93 | b, err := ioutil.ReadAll(resp.Body) 94 | if err != nil { 95 | panic(err) 96 | } 97 | return string(b) 98 | } 99 | 100 | // GetAvailableDevicesAtSpecificEthernetInterface ... 101 | func GetAvailableDevicesAtSpecificEthernetInterface(interfaceName string) []Device { 102 | /* 103 | Call an ws-discovery Probe Message to Discover NVT type Devices 104 | */ 105 | devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:" + NVT.String()}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) 106 | nvtDevices := make([]Device, 0) 107 | ////fmt.Println(devices) 108 | for _, j := range devices { 109 | doc := etree.NewDocument() 110 | if err := doc.ReadFromString(j); err != nil { 111 | fmt.Errorf("%s", err.Error()) 112 | return nil 113 | } 114 | ////fmt.Println(j) 115 | endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") 116 | for _, xaddr := range endpoints { 117 | // fmt.Println(xaddr.Tag,strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] ) 118 | xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] 119 | fmt.Println(xaddr) 120 | c := 0 121 | for c = 0; c < len(nvtDevices); c++ { 122 | if nvtDevices[c].xaddr == xaddr { 123 | fmt.Println(nvtDevices[c].xaddr, "==", xaddr) 124 | break 125 | } 126 | } 127 | if c < len(nvtDevices) { 128 | continue 129 | } 130 | dev, err := NewDevice(strings.Split(xaddr, " ")[0]) 131 | // fmt.Println(dev) 132 | if err != nil { 133 | fmt.Println("Error", xaddr) 134 | fmt.Println(err) 135 | continue 136 | } else { 137 | ////fmt.Println(dev) 138 | nvtDevices = append(nvtDevices, *dev) 139 | } 140 | } 141 | ////fmt.Println(j) 142 | //nvtDevices[i] = NewDevice() 143 | } 144 | return nvtDevices 145 | } 146 | 147 | func (dev *Device) getSupportedServices(resp *http.Response) { 148 | //resp, err := dev.CallMethod(device.GetCapabilities{Category:"All"}) 149 | //if err != nil { 150 | // log.Println(err.Error()) 151 | //return 152 | //} else { 153 | doc := etree.NewDocument() 154 | 155 | data, _ := ioutil.ReadAll(resp.Body) 156 | 157 | if err := doc.ReadFromBytes(data); err != nil { 158 | // log.Println(err.Error()) 159 | return 160 | } 161 | services := doc.FindElements("./Envelope/Body/GetCapabilitiesResponse/Capabilities/*/XAddr") 162 | for _, j := range services { 163 | ////fmt.Println(j.Text()) 164 | ////fmt.Println(j.Parent().Tag) 165 | dev.addEndpoint(j.Parent().Tag, j.Text()) 166 | } 167 | //} 168 | } 169 | 170 | // NewDevice function construct a ONVIF Device entity 171 | func NewDevice(xaddr string) (*Device, error) { 172 | dev := new(Device) 173 | dev.xaddr = xaddr 174 | dev.endpoints = make(map[string]string) 175 | dev.addEndpoint("Device", "http://"+xaddr+"/onvif/device_service") 176 | 177 | getCapabilities := device.GetCapabilities{Category: "All"} 178 | 179 | resp, err := dev.CallMethod(getCapabilities) 180 | // fmt.Println(resp.Request.Host) 181 | // fmt.Println(readResponse(resp)) 182 | if err != nil || resp.StatusCode != http.StatusOK { 183 | // panic(errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services")) 184 | return nil, errors.New("camera is not available at " + xaddr + " or it does not support ONVIF services") 185 | } 186 | 187 | dev.getSupportedServices(resp) 188 | return dev, nil 189 | } 190 | 191 | func (dev *Device) addEndpoint(Key, Value string) { 192 | // use lowCaseKey 193 | // make key having ability to handle Mixed Case for Different vendor devcie (e.g. Events EVENTS, events) 194 | lowCaseKey := strings.ToLower(Key) 195 | dev.endpoints[lowCaseKey] = Value 196 | } 197 | 198 | // Authenticate function authenticate client in the ONVIF device. 199 | // Function takes and params. 200 | // You should use this function to allow authorized requests to the ONVIF Device 201 | // To change auth data call this function again. 202 | func (dev *Device) Authenticate(username, password string) { 203 | dev.login = username 204 | dev.password = password 205 | } 206 | 207 | // GetEndpoint returns specific ONVIF service endpoint address 208 | func (dev *Device) GetEndpoint(name string) string { 209 | return dev.endpoints[name] 210 | } 211 | 212 | func buildMethodSOAP(msg string) (gosoap.SoapMessage, error) { 213 | doc := etree.NewDocument() 214 | if err := doc.ReadFromString(msg); err != nil { 215 | // log.Println("Got error") 216 | 217 | return "", err 218 | } 219 | element := doc.Root() 220 | 221 | soap := gosoap.NewEmptySOAP() 222 | soap.AddBodyContent(element) 223 | // soap.AddRootNamespace("onvif", "http://www.onvif.org/ver10/device/wsdl") 224 | 225 | return soap, nil 226 | } 227 | 228 | // getEndpoint functions get the target service endpoint in a better way 229 | func (dev Device) getEndpoint(endpoint string) (string, error) { 230 | // common condition, endpointMark in map we use this. 231 | if endpointURL, bFound := dev.endpoints[endpoint]; bFound { 232 | return endpointURL, nil 233 | } 234 | 235 | // but ,if we have endpoint like event、analytic 236 | // and sametime the Targetkey like : events、analytics 237 | // we use fuzzy way to find the best match url 238 | var endpointURL string 239 | for targetKey := range dev.endpoints { 240 | if strings.Contains(targetKey, endpoint) { 241 | endpointURL = dev.endpoints[targetKey] 242 | return endpointURL, nil 243 | } 244 | } 245 | return endpointURL, errors.New("target endpoint service not found") 246 | } 247 | 248 | // CallMethod functions call an method, defined struct. 249 | // You should use Authenticate method to call authorized requests. 250 | func (dev Device) CallMethod(method interface{}) (*http.Response, error) { 251 | pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/") 252 | pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) 253 | // TODO: refactor and cleanup code 254 | if pkg == "media2" { 255 | pkg = "media" 256 | } 257 | 258 | endpoint, err := dev.getEndpoint(pkg) 259 | if err != nil { 260 | return nil, err 261 | } 262 | return dev.callMethodDo(endpoint, method) 263 | } 264 | 265 | // CallMethodXML ... 266 | func (dev Device) CallMethodXML(method interface{}, xml string) (*http.Response, error) { 267 | pkgPath := strings.Split(reflect.TypeOf(method).PkgPath(), "/") 268 | pkg := strings.ToLower(pkgPath[len(pkgPath)-1]) 269 | 270 | endpoint, err := dev.getEndpoint(pkg) 271 | if err != nil { 272 | return nil, err 273 | } 274 | return networking.SendSoap(dev.login, dev.password, endpoint, xml) 275 | } 276 | 277 | // CallMethod functions call an method, defined struct with authentication data 278 | func (dev Device) callMethodDo(endpoint string, method interface{}) (*http.Response, error) { 279 | /* 280 | Converting struct to xml string representation 281 | */ 282 | output, err := xml.MarshalIndent(method, " ", " ") 283 | if err != nil { 284 | // log.Printf("error: %v\n", err.Error()) 285 | return nil, err 286 | } 287 | //fmt.Println(gosoap.SoapMessage(string(output)).StringIndent()) 288 | /* 289 | Build an SOAP request with 290 | */ 291 | soap, err := buildMethodSOAP(string(output)) 292 | if err != nil { 293 | // log.Printf("error: %v\n", err.Error()) 294 | return nil, err 295 | } 296 | 297 | //fmt.Println(soap.StringIndent()) 298 | /* 299 | Adding namespaces and WS-Security headers 300 | */ 301 | soap.AddRootNamespaces(Xlmns) 302 | 303 | // fmt.Println(soap.StringIndent()) 304 | // Header handling 305 | soap.AddAction() 306 | 307 | // Auth Handling 308 | if dev.login != "" && dev.password != "" { 309 | soap.AddWSSecurity(dev.login, dev.password) 310 | } 311 | //fmt.Println(soap.StringIndent()) 312 | /* 313 | Sending request and returns the response 314 | */ 315 | // return networking.SendSoap(dev.login, dev.password, endpoint, soap.String()) 316 | return networking.SendSoapWithDigestAndHttpReq(dev.login, dev.password, endpoint, soap.String(), 10*time.Second) 317 | } 318 | -------------------------------------------------------------------------------- /Imaging/types.go: -------------------------------------------------------------------------------- 1 | package imaging 2 | 3 | import ( 4 | "github.com/fanap-infra/onvif/xsd" 5 | "github.com/fanap-infra/onvif/xsd/onvif" 6 | ) 7 | 8 | type GetServiceCapabilities struct { 9 | XMLName string `xml:"timg:GetServiceCapabilities"` 10 | } 11 | 12 | type GetImagingSettings struct { 13 | XMLName string `xml:"timg:GetImagingSettings"` 14 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 15 | } 16 | 17 | type SetImagingSettings struct { 18 | XMLName string `xml:"timg:SetImagingSettings"` 19 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 20 | ImagingSettings onvif.ImagingSettings20 `xml:"timg:ImagingSettings"` 21 | ForcePersistence xsd.Boolean `xml:"timg:ForcePersistence"` 22 | } 23 | 24 | type GetOptions struct { 25 | XMLName string `xml:"timg:GetOptions"` 26 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 27 | } 28 | 29 | type Move struct { 30 | XMLName string `xml:"timg:Move"` 31 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 32 | Focus onvif.FocusMove `xml:"timg:Focus"` 33 | } 34 | 35 | type GetMoveOptions struct { 36 | XMLName string `xml:"timg:GetMoveOptions"` 37 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 38 | } 39 | 40 | type Stop struct { 41 | XMLName string `xml:"timg:Stop"` 42 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 43 | } 44 | 45 | type GetStatus struct { 46 | XMLName string `xml:"timg:GetStatus"` 47 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 48 | } 49 | 50 | type GetPresets struct { 51 | XMLName string `xml:"timg:GetPresets"` 52 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 53 | } 54 | 55 | type GetCurrentPreset struct { 56 | XMLName string `xml:"timg:GetCurrentPreset"` 57 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 58 | } 59 | 60 | type SetCurrentPreset struct { 61 | XMLName string `xml:"timg:SetCurrentPreset"` 62 | VideoSourceToken onvif.ReferenceToken `xml:"timg:VideoSourceToken"` 63 | PresetToken onvif.ReferenceToken `xml:"timg:PresetToken"` 64 | } 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # onvif protocol 2 | 3 | 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. 4 | 5 | ## Installation 6 | 7 | To install the library, use **go get**: 8 | 9 | ```go 10 | go get github.com/fanap-infra/onvif 11 | 12 | ``` 13 | 14 | ## Supported services 15 | 16 | The following services are implemented: 17 | 18 | - Device 19 | - Media 20 | - PTZ 21 | - Imaging 22 | - Event 23 | - Discovery 24 | - Auth(More Options) 25 | - Soap 26 | 27 | ## Using 28 | 29 | ### General concept 30 | 31 | 1. Connecting to the device 32 | 2. Authentication (if necessary) 33 | 3. Defining Data Types 34 | 4. Carrying out the required method 35 | 36 | #### Connecting to the device 37 | 38 | 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: 39 | 40 | ```go 41 | dev, err := onvif.NewDevice("192.168.13.42:1234") 42 | ``` 43 | 44 | \*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.\*** 45 | 46 | #### Authentication 47 | 48 | If any function of the ONVIF services requires authentication, you must use the `Authenticate` method. 49 | 50 | ```go 51 | device := onvif.NewDevice("192.168.13.42:1234") 52 | device.Authenticate("username", "password") 53 | ``` 54 | 55 | #### Defining Data Types 56 | 57 | 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: 58 | 59 | ```go 60 | capabilities := device.GetCapabilities{Category:"All"} 61 | ``` 62 | 63 | Why does the `GetCapabilities` structure have the Category field and why is the value of this field `All`? 64 | 65 | The figure below shows the documentation for the [GetCapabilities](https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl). 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'`. 66 | 67 | ![Device GetCapabilities](docs/img/exmp_GetCapabilities.png) 68 | 69 | An example of defining the data type of `GetServiceCapabilities` function in [PTZ](https://www.onvif.org/ver20/ptz/wsdl/ptz.wsdl): 70 | 71 | ```go 72 | ptzCapabilities := ptz.GetServiceCapabilities{} 73 | ``` 74 | 75 | The figure below shows that `GetServiceCapabilities` does not accept any arguments. 76 | 77 | ![PTZ GetServiceCapabilities](docs/img/GetServiceCapabilities.png) 78 | 79 | _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._ 80 | 81 | An example of how to define the data type of the CreateUsers function in [Devicemgmt](https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl): 82 | 83 | ```go 84 | createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}} 85 | ``` 86 | 87 | 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. 88 | 89 | ![Device CreateUsers](docs/img/exmp_CreateUsers.png) 90 | 91 | #### Carrying out the required method 92 | 93 | To perform any function of one of the ONVIF services whose structure has been defined, you must use the `CallMethod` of the device object. 94 | 95 | ```go 96 | createUsers := device.CreateUsers{User: onvif.User{Username:"admin", Password:"qwerty", UserLevel:"User"}} 97 | device := onvif.NewDevice("192.168.13.42:1234") 98 | device.Authenticate("username", "password") 99 | resp, err := dev.CallMethod(createUsers) 100 | ``` 101 | 102 | ## Great Thanks 103 | 104 | Enhanced and Improved from: [goonvif](https://github.com/yakovlevdmv/goonvif) 105 | -------------------------------------------------------------------------------- /analytics/types.go: -------------------------------------------------------------------------------- 1 | package analytics 2 | 3 | import ( 4 | "github.com/fanap-infra/onvif/xsd" 5 | "github.com/fanap-infra/onvif/xsd/onvif" 6 | ) 7 | 8 | type GetSupportedRules struct { 9 | XMLName string `xml:"tan:GetSupportedRules"` 10 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 11 | } 12 | 13 | type CreateRules struct { 14 | XMLName string `xml:"tan:CreateRules"` 15 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 16 | Rule onvif.Config `xml:"tan:Rule"` 17 | } 18 | 19 | type DeleteRules struct { 20 | XMLName string `xml:"tan:DeleteRules"` 21 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 22 | RuleName xsd.String `xml:"tan:RuleName"` 23 | } 24 | 25 | type GetRules struct { 26 | XMLName string `xml:"tan:GetRules"` 27 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 28 | } 29 | 30 | type GetRuleOptions struct { 31 | XMLName string `xml:"tan:GetRuleOptions"` 32 | RuleType xsd.QName `xml:"tan:RuleType"` 33 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 34 | } 35 | 36 | type ModifyRules struct { 37 | XMLName string `xml:"tan:ModifyRules"` 38 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 39 | Rule onvif.Config `xml:"tan:Rule"` 40 | } 41 | 42 | type GetServiceCapabilities struct { 43 | XMLName string `xml:"tan:GetServiceCapabilities"` 44 | } 45 | 46 | type GetSupportedAnalyticsModules struct { 47 | XMLName string `xml:"tan:GetSupportedAnalyticsModules"` 48 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 49 | } 50 | 51 | type GetAnalyticsModuleOptions struct { 52 | XMLName string `xml:"tan:GetAnalyticsModuleOptions"` 53 | Type xsd.QName `xml:"tan:Type"` 54 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 55 | } 56 | 57 | type CreateAnalyticsModules struct { 58 | XMLName string `xml:"tev:CreateAnalyticsModules"` 59 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 60 | AnalyticsModule onvif.Config `xml:"tan:AnalyticsModule"` 61 | } 62 | 63 | type DeleteAnalyticsModules struct { 64 | XMLName string `xml:"tan:DeleteAnalyticsModules"` 65 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 66 | AnalyticsModuleName xsd.String `xml:"tan:AnalyticsModuleName"` 67 | } 68 | 69 | type GetAnalyticsModules struct { 70 | XMLName string `xml:"tan:GetAnalyticsModules"` 71 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 72 | } 73 | 74 | type ModifyAnalyticsModules struct { 75 | XMLName string `xml:"tan:ModifyAnalyticsModules"` 76 | ConfigurationToken onvif.ReferenceToken `xml:"tan:ConfigurationToken"` 77 | AnalyticsModule onvif.Config `xml:"tan:AnalyticsModule"` 78 | } 79 | -------------------------------------------------------------------------------- /api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "net/http" 8 | "path" 9 | "reflect" 10 | "regexp" 11 | "strings" 12 | 13 | "github.com/beevik/etree" 14 | "github.com/fanap-infra/onvif" 15 | "github.com/fanap-infra/onvif/gosoap" 16 | "github.com/fanap-infra/onvif/networking" 17 | wsdiscovery "github.com/fanap-infra/onvif/ws-discovery" 18 | "github.com/gin-gonic/gin" 19 | ) 20 | 21 | func RunApi() { 22 | router := gin.Default() 23 | 24 | router.POST("/:service/:method", func(c *gin.Context) { 25 | c.Header("Access-Control-Allow-Origin", "*") 26 | //c.Header("Access-Control-Allow-Headers", "access-control-allow-origin, access-control-allow-headers") 27 | 28 | serviceName := c.Param("service") 29 | methodName := c.Param("method") 30 | username := c.GetHeader("username") 31 | pass := c.GetHeader("password") 32 | xaddr := c.GetHeader("xaddr") 33 | acceptedData, err := c.GetRawData() 34 | if err != nil { 35 | fmt.Println(err) 36 | } 37 | 38 | message, err := callNecessaryMethod(serviceName, methodName, string(acceptedData), username, pass, xaddr) 39 | if err != nil { 40 | c.XML(http.StatusBadRequest, err.Error()) 41 | } else { 42 | c.String(http.StatusOK, message) 43 | } 44 | }) 45 | 46 | router.GET("/discovery", func(context *gin.Context) { 47 | context.Header("Access-Control-Allow-Origin", "*") 48 | context.Header("Access-Control-Allow-Headers", "access-control-allow-origin, access-control-allow-headers") 49 | 50 | interfaceName := context.GetHeader("interface") 51 | 52 | var response = "[" 53 | devices := wsdiscovery.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) 54 | 55 | for _, j := range devices { 56 | doc := etree.NewDocument() 57 | if err := doc.ReadFromString(j); err != nil { 58 | context.XML(http.StatusBadRequest, err.Error()) 59 | } else { 60 | 61 | endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") 62 | scopes := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/Scopes") 63 | 64 | flag := false 65 | 66 | for _, xaddr := range endpoints { 67 | xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] 68 | if strings.Contains(response, xaddr) { 69 | flag = true 70 | break 71 | } 72 | response += "{" 73 | response += `"url":"` + xaddr + `",` 74 | } 75 | if flag { 76 | break 77 | } 78 | for _, scope := range scopes { 79 | re := regexp.MustCompile(`onvif:\/\/www\.onvif\.org\/name\/[A-Za-z0-9-]+`) 80 | match := re.FindStringSubmatch(scope.Text()) 81 | response += `"name":"` + path.Base(match[0]) + `"` 82 | } 83 | response += "}," 84 | 85 | } 86 | 87 | } 88 | response = strings.TrimRight(response, ",") 89 | response += "]" 90 | if response != "" { 91 | context.String(http.StatusOK, response) 92 | } 93 | }) 94 | 95 | router.Run() 96 | } 97 | 98 | //func soapHandling(tp interface{}, tags* map[string]string) { 99 | // ifaceValue := reflect.ValueOf(tp).Elem() 100 | // typeOfStruct := ifaceValue.Type() 101 | // if ifaceValue.Kind() != reflect.Struct { 102 | // return 103 | // } 104 | // for i := 0; i < ifaceValue.NumField(); i++ { 105 | // field := ifaceValue.Field(i) 106 | // tg, err := typeOfStruct.FieldByName(typeOfStruct.Field(i).Name) 107 | // if err == false { 108 | // fmt.Println(err) 109 | // } 110 | // (*tags)[typeOfStruct.Field(i).Name] = string(tg.Tag) 111 | // 112 | // subStruct := reflect.New(reflect.TypeOf( field.Interface() )) 113 | // soapHandling(subStruct.Interface(), tags) 114 | // } 115 | //} 116 | 117 | func callNecessaryMethod(serviceName, methodName, acceptedData, username, password, xaddr string) (string, error) { 118 | var methodStruct interface{} 119 | var err error 120 | 121 | switch strings.ToLower(serviceName) { 122 | case "device": 123 | methodStruct, err = getDeviceStructByName(methodName) 124 | case "ptz": 125 | methodStruct, err = getPTZStructByName(methodName) 126 | case "media": 127 | methodStruct, err = getMediaStructByName(methodName) 128 | default: 129 | return "", errors.New("there is no such service") 130 | } 131 | if err != nil { //done 132 | return "", err 133 | } 134 | 135 | resp, err := xmlAnalize(methodStruct, &acceptedData) 136 | if err != nil { 137 | return "", err 138 | } 139 | 140 | endpoint, err := getEndpoint(serviceName, xaddr) 141 | if err != nil { 142 | return "", err 143 | } 144 | 145 | soap := gosoap.NewEmptySOAP() 146 | soap.AddStringBodyContent(*resp) 147 | soap.AddRootNamespaces(onvif.Xlmns) 148 | // soap.AddWSSecurity(username, password) 149 | 150 | servResp, err := networking.SendSoap(endpoint, soap.String()) 151 | if err != nil { 152 | return "", err 153 | } 154 | 155 | rsp, err := ioutil.ReadAll(servResp.Body) 156 | if err != nil { 157 | return "", err 158 | } 159 | 160 | return string(rsp), nil 161 | } 162 | 163 | func getEndpoint(service, xaddr string) (string, error) { 164 | dev, err := onvif.NewDevice(xaddr) 165 | if err != nil { 166 | return "", err 167 | } 168 | pkg := strings.ToLower(service) 169 | 170 | var endpoint string 171 | switch pkg { 172 | case "device": 173 | endpoint = dev.GetEndpoint("Device") 174 | case "event": 175 | endpoint = dev.GetEndpoint("Event") 176 | case "imaging": 177 | endpoint = dev.GetEndpoint("Imaging") 178 | case "media": 179 | endpoint = dev.GetEndpoint("Media") 180 | case "ptz": 181 | endpoint = dev.GetEndpoint("PTZ") 182 | } 183 | return endpoint, nil 184 | } 185 | 186 | func xmlAnalize(methodStruct interface{}, acceptedData *string) (*string, error) { 187 | test := make([]map[string]string, 0) //tags 188 | testunMarshal := make([][]interface{}, 0) //data 189 | var mas []string //idnt 190 | 191 | soapHandling(methodStruct, &test) 192 | test = mapProcessing(test) 193 | 194 | doc := etree.NewDocument() 195 | if err := doc.ReadFromString(*acceptedData); err != nil { 196 | return nil, err 197 | } 198 | etr := doc.FindElements("./*") 199 | xmlUnmarshal(etr, &testunMarshal, &mas) 200 | ident(&mas) 201 | 202 | document := etree.NewDocument() 203 | var el *etree.Element 204 | var idntIndex = 0 205 | 206 | for lstIndex := 0; lstIndex < len(testunMarshal); { 207 | lst := (testunMarshal)[lstIndex] 208 | elemName, attr, value, err := xmlMaker(&lst, &test, lstIndex) 209 | if err != nil { 210 | return nil, err 211 | } 212 | 213 | if mas[lstIndex] == "Push" && lstIndex == 0 { //done 214 | el = document.CreateElement(elemName) 215 | el.SetText(value) 216 | if len(attr) != 0 { 217 | for key, value := range attr { 218 | el.CreateAttr(key, value) 219 | } 220 | } 221 | } else if mas[idntIndex] == "Push" { 222 | pushTmp := etree.NewElement(elemName) 223 | pushTmp.SetText(value) 224 | if len(attr) != 0 { 225 | for key, value := range attr { 226 | pushTmp.CreateAttr(key, value) 227 | } 228 | } 229 | el.AddChild(pushTmp) 230 | el = pushTmp 231 | } else if mas[idntIndex] == "PushPop" { 232 | popTmp := etree.NewElement(elemName) 233 | popTmp.SetText(value) 234 | if len(attr) != 0 { 235 | for key, value := range attr { 236 | popTmp.CreateAttr(key, value) 237 | } 238 | } 239 | if el == nil { 240 | document.AddChild(popTmp) 241 | } else { 242 | el.AddChild(popTmp) 243 | } 244 | } else if mas[idntIndex] == "Pop" { 245 | el = el.Parent() 246 | lstIndex -= 1 247 | } 248 | idntIndex += 1 249 | lstIndex += 1 250 | } 251 | 252 | resp, err := document.WriteToString() 253 | if err != nil { 254 | return nil, err 255 | } 256 | 257 | return &resp, err 258 | } 259 | 260 | func xmlMaker(lst *[]interface{}, tags *[]map[string]string, lstIndex int) (string, map[string]string, string, error) { 261 | var elemName, value string 262 | attr := make(map[string]string) 263 | for tgIndx, tg := range *tags { 264 | if tgIndx == lstIndex { 265 | for index, elem := range *lst { 266 | if reflect.TypeOf(elem).String() == "[]etree.Attr" { 267 | conversion := elem.([]etree.Attr) 268 | for _, i := range conversion { 269 | attr[i.Key] = i.Value 270 | } 271 | } else { 272 | conversion := elem.(string) 273 | if index == 0 && lstIndex == 0 { 274 | res, err := xmlProcessing(tg["XMLName"]) 275 | if err != nil { 276 | return "", nil, "", err 277 | } 278 | elemName = res 279 | } else if index == 0 { 280 | res, err := xmlProcessing(tg[conversion]) 281 | if err != nil { 282 | return "", nil, "", err 283 | } 284 | elemName = res 285 | } else { 286 | value = conversion 287 | } 288 | } 289 | } 290 | } 291 | } 292 | return elemName, attr, value, nil 293 | } 294 | 295 | func xmlProcessing(tg string) (string, error) { 296 | r, _ := regexp.Compile(`"(.*?)"`) 297 | str := r.FindStringSubmatch(tg) 298 | if len(str) == 0 { 299 | return "", errors.New("out of range") 300 | } 301 | attr := strings.Index(str[1], ",attr") 302 | omit := strings.Index(str[1], ",omitempty") 303 | attrOmit := strings.Index(str[1], ",attr,omitempty") 304 | omitAttr := strings.Index(str[1], ",omitempty,attr") 305 | 306 | if attr > -1 && attrOmit == -1 && omitAttr == -1 { 307 | return str[1][0:attr], nil 308 | } else if omit > -1 && attrOmit == -1 && omitAttr == -1 { 309 | return str[1][0:omit], nil 310 | } else if attr == -1 && omit == -1 { 311 | return str[1], nil 312 | } else if attrOmit > -1 { 313 | return str[1][0:attrOmit], nil 314 | } else { 315 | return str[1][0:omitAttr], nil 316 | } 317 | 318 | return "", errors.New("something went wrong") 319 | } 320 | 321 | func mapProcessing(mapVar []map[string]string) []map[string]string { 322 | for indx := 0; indx < len(mapVar); indx++ { 323 | element := mapVar[indx] 324 | for _, value := range element { 325 | if value == "" { 326 | mapVar = append(mapVar[:indx], mapVar[indx+1:]...) 327 | indx-- 328 | } 329 | if strings.Index(value, ",attr") != -1 { 330 | mapVar = append(mapVar[:indx], mapVar[indx+1:]...) 331 | indx-- 332 | } 333 | } 334 | } 335 | return mapVar 336 | } 337 | 338 | func soapHandling(tp interface{}, tags *[]map[string]string) { 339 | s := reflect.ValueOf(tp).Elem() 340 | typeOfT := s.Type() 341 | if s.Kind() != reflect.Struct { 342 | return 343 | } 344 | for i := 0; i < s.NumField(); i++ { 345 | f := s.Field(i) 346 | tmp, err := typeOfT.FieldByName(typeOfT.Field(i).Name) 347 | if err == false { 348 | fmt.Println(err) 349 | } 350 | *tags = append(*tags, map[string]string{typeOfT.Field(i).Name: string(tmp.Tag)}) 351 | subStruct := reflect.New(reflect.TypeOf(f.Interface())) 352 | soapHandling(subStruct.Interface(), tags) 353 | } 354 | } 355 | 356 | func xmlUnmarshal(elems []*etree.Element, data *[][]interface{}, mas *[]string) { 357 | for _, elem := range elems { 358 | *data = append(*data, []interface{}{elem.Tag, elem.Attr, elem.Text()}) 359 | *mas = append(*mas, "Push") 360 | xmlUnmarshal(elem.FindElements("./*"), data, mas) 361 | *mas = append(*mas, "Pop") 362 | } 363 | } 364 | 365 | func ident(mas *[]string) { 366 | var buffer string 367 | for _, j := range *mas { 368 | buffer += j + " " 369 | } 370 | buffer = strings.Replace(buffer, "Push Pop ", "PushPop ", -1) 371 | buffer = strings.TrimSpace(buffer) 372 | *mas = strings.Split(buffer, " ") 373 | } 374 | -------------------------------------------------------------------------------- /api/get_structs.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/fanap-infra/onvif/device" 7 | "github.com/fanap-infra/onvif/media" 8 | "github.com/fanap-infra/onvif/ptz" 9 | ) 10 | 11 | func getPTZStructByName(name string) (interface{}, error) { 12 | switch name { 13 | case "GetServiceCapabilities": 14 | return &ptz.GetServiceCapabilities{}, nil 15 | case "GetNodes": 16 | return &ptz.GetNodes{}, nil 17 | case "GetNode": 18 | return &ptz.GetNode{}, nil 19 | case "GetConfiguration": 20 | return &ptz.GetConfiguration{}, nil 21 | case "GetConfigurations": 22 | return &ptz.GetConfigurations{}, nil 23 | case "SetConfiguration": 24 | return &ptz.SetConfiguration{}, nil 25 | case "GetConfigurationOptions": 26 | return &ptz.GetConfigurationOptions{}, nil 27 | case "SendAuxiliaryCommand": 28 | return &ptz.SendAuxiliaryCommand{}, nil 29 | case "GetPresets": 30 | return &ptz.GetPresets{}, nil 31 | case "SetPreset": 32 | return &ptz.SetPreset{}, nil 33 | case "RemovePreset": 34 | return &ptz.RemovePreset{}, nil 35 | case "GotoPreset": 36 | return &ptz.GotoPreset{}, nil 37 | case "GotoHomePosition": 38 | return &ptz.GotoHomePosition{}, nil 39 | case "SetHomePosition": 40 | return &ptz.SetHomePosition{}, nil 41 | case "ContinuousMove": 42 | return &ptz.ContinuousMove{}, nil 43 | case "RelativeMove": 44 | return &ptz.RelativeMove{}, nil 45 | case "GetStatus": 46 | return &ptz.GetStatus{}, nil 47 | case "AbsoluteMove": 48 | return &ptz.AbsoluteMove{}, nil 49 | case "GeoMove": 50 | return &ptz.GeoMove{}, nil 51 | case "Stop": 52 | return &ptz.Stop{}, nil 53 | case "GetPresetTours": 54 | return &ptz.GetPresetTours{}, nil 55 | case "GetPresetTour": 56 | return &ptz.GetPresetTour{}, nil 57 | case "GetPresetTourOptions": 58 | return &ptz.GetPresetTourOptions{}, nil 59 | case "CreatePresetTour": 60 | return &ptz.CreatePresetTour{}, nil 61 | case "ModifyPresetTour": 62 | return &ptz.ModifyPresetTour{}, nil 63 | case "OperatePresetTour": 64 | return &ptz.OperatePresetTour{}, nil 65 | case "RemovePresetTour": 66 | return &ptz.RemovePresetTour{}, nil 67 | case "GetCompatibleConfigurations": 68 | return &ptz.GetCompatibleConfigurations{}, nil 69 | default: 70 | return nil, errors.New("there is no such method in the PTZ service") 71 | } 72 | } 73 | 74 | func getDeviceStructByName(name string) (interface{}, error) { 75 | switch name { 76 | case "GetServices": 77 | return &device.GetServices{}, nil 78 | case "GetServiceCapabilities": 79 | return &device.GetServiceCapabilities{}, nil 80 | case "GetDeviceInformation": 81 | return &device.GetDeviceInformation{}, nil 82 | case "SetSystemDateAndTime": 83 | return &device.SetSystemDateAndTime{}, nil 84 | case "GetSystemDateAndTime": 85 | return &device.GetSystemDateAndTime{}, nil 86 | case "SetSystemFactoryDefault": 87 | return &device.SetSystemFactoryDefault{}, nil 88 | case "UpgradeSystemFirmware": 89 | return &device.UpgradeSystemFirmware{}, nil 90 | case "SystemReboot": 91 | return &device.SystemReboot{}, nil 92 | case "RestoreSystem": 93 | return &device.RestoreSystem{}, nil 94 | case "GetSystemBackup": 95 | return &device.GetSystemBackup{}, nil 96 | case "GetSystemLog": 97 | return &device.GetSystemLog{}, nil 98 | case "GetSystemSupportInformation": 99 | return &device.GetSystemSupportInformation{}, nil 100 | case "GetScopes": 101 | return &device.GetScopes{}, nil 102 | case "SetScopes": 103 | return &device.SetScopes{}, nil 104 | case "AddScopes": 105 | return &device.AddScopes{}, nil 106 | case "RemoveScopes": 107 | return &device.RemoveScopes{}, nil 108 | case "GetDiscoveryMode": 109 | return &device.GetDiscoveryMode{}, nil 110 | case "SetDiscoveryMode": 111 | return &device.SetDiscoveryMode{}, nil 112 | case "GetRemoteDiscoveryMode": 113 | return &device.GetRemoteDiscoveryMode{}, nil 114 | case "SetRemoteDiscoveryMode": 115 | return &device.SetRemoteDiscoveryMode{}, nil 116 | case "GetDPAddresses": 117 | return &device.GetDPAddresses{}, nil 118 | case "SetDPAddresses": 119 | return &device.SetDPAddresses{}, nil 120 | case "GetEndpointReference": 121 | return &device.GetEndpointReference{}, nil 122 | case "GetRemoteUser": 123 | return &device.GetRemoteUser{}, nil 124 | case "SetRemoteUser": 125 | return &device.SetRemoteUser{}, nil 126 | case "GetUsers": 127 | return &device.GetUsers{}, nil 128 | case "CreateUsers": 129 | return &device.CreateUsers{}, nil 130 | case "DeleteUsers": 131 | return &device.DeleteUsers{}, nil 132 | case "SetUser": 133 | return &device.SetUser{}, nil 134 | case "GetWsdlUrl": 135 | return &device.GetWsdlUrl{}, nil 136 | case "GetCapabilities": 137 | return &device.GetCapabilities{}, nil 138 | case "GetHostname": 139 | return &device.GetHostname{}, nil 140 | case "SetHostname": 141 | return &device.SetHostname{}, nil 142 | case "SetHostnameFromDHCP": 143 | return &device.SetHostnameFromDHCP{}, nil 144 | case "GetDNS": 145 | return &device.GetDNS{}, nil 146 | case "SetDNS": 147 | return &device.SetDNS{}, nil 148 | case "GetNTP": 149 | return &device.GetNTP{}, nil 150 | case "SetNTP": 151 | return &device.SetNTP{}, nil 152 | case "GetDynamicDNS": 153 | return &device.GetDynamicDNS{}, nil 154 | case "SetDynamicDNS": 155 | return &device.SetDynamicDNS{}, nil 156 | case "GetNetworkInterfaces": 157 | return &device.GetNetworkInterfaces{}, nil 158 | case "SetNetworkInterfaces": 159 | return &device.SetNetworkInterfaces{}, nil 160 | case "GetNetworkProtocols": 161 | return &device.GetNetworkProtocols{}, nil 162 | case "SetNetworkProtocols": 163 | return &device.SetNetworkProtocols{}, nil 164 | case "GetNetworkDefaultGateway": 165 | return &device.GetNetworkDefaultGateway{}, nil 166 | case "SetNetworkDefaultGateway": 167 | return &device.SetNetworkDefaultGateway{}, nil 168 | case "GetZeroConfiguration": 169 | return &device.GetZeroConfiguration{}, nil 170 | case "SetZeroConfiguration": 171 | return &device.SetZeroConfiguration{}, nil 172 | case "GetIPAddressFilter": 173 | return &device.GetIPAddressFilter{}, nil 174 | case "SetIPAddressFilter": 175 | return &device.SetIPAddressFilter{}, nil 176 | case "AddIPAddressFilter": 177 | return &device.AddIPAddressFilter{}, nil 178 | case "RemoveIPAddressFilter": 179 | return &device.RemoveIPAddressFilter{}, nil 180 | case "GetAccessPolicy": 181 | return &device.GetAccessPolicy{}, nil 182 | case "SetAccessPolicy": 183 | return &device.SetAccessPolicy{}, nil 184 | case "CreateCertificate": 185 | return &device.CreateCertificate{}, nil 186 | case "GetCertificates": 187 | return &device.GetCertificates{}, nil 188 | case "GetCertificatesStatus": 189 | return &device.GetCertificatesStatus{}, nil 190 | case "SetCertificatesStatus": 191 | return &device.SetCertificatesStatus{}, nil 192 | case "DeleteCertificates": 193 | return &device.DeleteCertificates{}, nil 194 | case "GetPkcs10Request": 195 | return &device.GetPkcs10Request{}, nil 196 | case "LoadCertificates": 197 | return &device.LoadCertificates{}, nil 198 | case "GetClientCertificateMode": 199 | return &device.GetClientCertificateMode{}, nil 200 | case "SetClientCertificateMode": 201 | return &device.SetClientCertificateMode{}, nil 202 | case "GetRelayOutputs": 203 | return &device.GetRelayOutputs{}, nil 204 | case "SetRelayOutputSettings": 205 | return &device.SetRelayOutputSettings{}, nil 206 | case "SetRelayOutputState": 207 | return &device.SetRelayOutputState{}, nil 208 | case "SendAuxiliaryCommand": 209 | return &device.SendAuxiliaryCommand{}, nil 210 | case "GetCACertificates": 211 | return &device.GetCACertificates{}, nil 212 | case "LoadCertificateWithPrivateKey": 213 | return &device.LoadCertificateWithPrivateKey{}, nil 214 | case "GetCertificateInformation": 215 | return &device.GetCertificateInformation{}, nil 216 | case "LoadCACertificates": 217 | return &device.LoadCACertificates{}, nil 218 | case "CreateDot1XConfiguration": 219 | return &device.CreateDot1XConfiguration{}, nil 220 | case "SetDot1XConfiguration": 221 | return &device.SetDot1XConfiguration{}, nil 222 | case "GetDot1XConfiguration": 223 | return &device.GetDot1XConfiguration{}, nil 224 | case "GetDot1XConfigurations": 225 | return &device.GetDot1XConfigurations{}, nil 226 | case "DeleteDot1XConfiguration": 227 | return &device.DeleteDot1XConfiguration{}, nil 228 | case "GetDot11Capabilities": 229 | return &device.GetDot11Capabilities{}, nil 230 | case "GetDot11Status": 231 | return &device.GetDot11Status{}, nil 232 | case "ScanAvailableDot11Networks": 233 | return &device.ScanAvailableDot11Networks{}, nil 234 | case "GetSystemUris": 235 | return &device.GetSystemUris{}, nil 236 | case "StartFirmwareUpgrade": 237 | return &device.StartFirmwareUpgrade{}, nil 238 | case "StartSystemRestore": 239 | return &device.StartSystemRestore{}, nil 240 | case "GetStorageConfigurations": 241 | return &device.GetStorageConfigurations{}, nil 242 | case "CreateStorageConfiguration": 243 | return &device.CreateStorageConfiguration{}, nil 244 | case "GetStorageConfiguration": 245 | return &device.GetStorageConfiguration{}, nil 246 | case "SetStorageConfiguration": 247 | return &device.SetStorageConfiguration{}, nil 248 | case "DeleteStorageConfiguration": 249 | return &device.DeleteStorageConfiguration{}, nil 250 | case "GetGeoLocation": 251 | return &device.GetGeoLocation{}, nil 252 | case "SetGeoLocation": 253 | return &device.SetGeoLocation{}, nil 254 | case "DeleteGeoLocation": 255 | return &device.DeleteGeoLocation{}, nil 256 | default: 257 | return nil, errors.New("there is no such method in the Device service") 258 | } 259 | } 260 | 261 | func getMediaStructByName(name string) (interface{}, error) { 262 | switch name { 263 | case "GetServiceCapabilities": 264 | return &media.GetServiceCapabilities{}, nil 265 | case "GetVideoSources": 266 | return &media.GetVideoSources{}, nil 267 | case "GetAudioSources": 268 | return &media.GetAudioSources{}, nil 269 | case "GetAudioOutputs": 270 | return &media.GetAudioOutputs{}, nil 271 | case "CreateProfile": 272 | return &media.CreateProfile{}, nil 273 | case "GetProfile": 274 | return &media.GetProfile{}, nil 275 | case "GetProfiles": 276 | return &media.GetProfiles{}, nil 277 | case "AddVideoEncoderConfiguration": 278 | return &media.AddVideoEncoderConfiguration{}, nil 279 | case "RemoveVideoEncoderConfiguration": 280 | return &media.RemoveVideoEncoderConfiguration{}, nil 281 | case "AddVideoSourceConfiguration": 282 | return &media.AddVideoSourceConfiguration{}, nil 283 | case "RemoveVideoSourceConfiguration": 284 | return &media.RemoveVideoSourceConfiguration{}, nil 285 | case "AddAudioEncoderConfiguration": 286 | return &media.AddAudioEncoderConfiguration{}, nil 287 | case "RemoveAudioEncoderConfiguration": 288 | return &media.RemoveAudioEncoderConfiguration{}, nil 289 | case "AddAudioSourceConfiguration": 290 | return &media.AddAudioSourceConfiguration{}, nil 291 | case "RemoveAudioSourceConfiguration": 292 | return &media.RemoveAudioSourceConfiguration{}, nil 293 | case "AddPTZConfiguration": 294 | return &media.AddPTZConfiguration{}, nil 295 | case "RemovePTZConfiguration": 296 | return &media.RemovePTZConfiguration{}, nil 297 | case "AddVideoAnalyticsConfiguration": 298 | return &media.AddVideoAnalyticsConfiguration{}, nil 299 | case "RemoveVideoAnalyticsConfiguration": 300 | return &media.RemoveVideoAnalyticsConfiguration{}, nil 301 | case "AddMetadataConfiguration": 302 | return &media.AddMetadataConfiguration{}, nil 303 | case "RemoveMetadataConfiguration": 304 | return &media.RemoveMetadataConfiguration{}, nil 305 | case "AddAudioOutputConfiguration": 306 | return &media.AddAudioOutputConfiguration{}, nil 307 | case "RemoveAudioOutputConfiguration": 308 | return &media.RemoveAudioOutputConfiguration{}, nil 309 | case "AddAudioDecoderConfiguration": 310 | return &media.AddAudioDecoderConfiguration{}, nil 311 | case "RemoveAudioDecoderConfiguration": 312 | return &media.RemoveAudioDecoderConfiguration{}, nil 313 | case "DeleteProfile": 314 | return &media.DeleteProfile{}, nil 315 | case "GetVideoSourceConfigurations": 316 | return &media.GetVideoSourceConfigurations{}, nil 317 | case "GetVideoEncoderConfigurations": 318 | return &media.GetVideoEncoderConfigurations{}, nil 319 | case "GetAudioSourceConfigurations": 320 | return &media.GetAudioSourceConfigurations{}, nil 321 | case "GetAudioEncoderConfigurations": 322 | return &media.GetAudioEncoderConfigurations{}, nil 323 | case "GetVideoAnalyticsConfigurations": 324 | return &media.GetVideoAnalyticsConfigurations{}, nil 325 | case "GetMetadataConfigurations": 326 | return &media.GetMetadataConfigurations{}, nil 327 | case "GetAudioOutputConfigurations": 328 | return &media.GetAudioOutputConfigurations{}, nil 329 | case "GetAudioDecoderConfigurations": 330 | return &media.GetAudioDecoderConfigurations{}, nil 331 | case "GetVideoSourceConfiguration": 332 | return &media.GetVideoSourceConfiguration{}, nil 333 | case "GetVideoEncoderConfiguration": 334 | return &media.GetVideoEncoderConfiguration{}, nil 335 | case "GetAudioSourceConfiguration": 336 | return &media.GetAudioSourceConfiguration{}, nil 337 | case "GetAudioEncoderConfiguration": 338 | return &media.GetAudioEncoderConfiguration{}, nil 339 | case "GetVideoAnalyticsConfiguration": 340 | return &media.GetVideoAnalyticsConfiguration{}, nil 341 | case "GetMetadataConfiguration": 342 | return &media.GetMetadataConfiguration{}, nil 343 | case "GetAudioOutputConfiguration": 344 | return &media.GetAudioOutputConfiguration{}, nil 345 | case "GetAudioDecoderConfiguration": 346 | return &media.GetAudioDecoderConfiguration{}, nil 347 | case "GetCompatibleVideoEncoderConfigurations": 348 | return &media.GetCompatibleVideoEncoderConfigurations{}, nil 349 | case "GetCompatibleVideoSourceConfigurations": 350 | return &media.GetCompatibleVideoSourceConfigurations{}, nil 351 | case "GetCompatibleAudioEncoderConfigurations": 352 | return &media.GetCompatibleAudioEncoderConfigurations{}, nil 353 | case "GetCompatibleAudioSourceConfigurations": 354 | return &media.GetCompatibleAudioSourceConfigurations{}, nil 355 | case "GetCompatibleVideoAnalyticsConfigurations": 356 | return &media.GetCompatibleVideoAnalyticsConfigurations{}, nil 357 | case "GetCompatibleMetadataConfigurations": 358 | return &media.GetCompatibleMetadataConfigurations{}, nil 359 | case "GetCompatibleAudioOutputConfigurations": 360 | return &media.GetCompatibleAudioOutputConfigurations{}, nil 361 | case "GetCompatibleAudioDecoderConfigurations": 362 | return &media.GetCompatibleAudioDecoderConfigurations{}, nil 363 | case "SetVideoSourceConfiguration": 364 | return &media.SetVideoSourceConfiguration{}, nil 365 | case "SetVideoEncoderConfiguration": 366 | return &media.SetVideoEncoderConfiguration{}, nil 367 | case "SetAudioSourceConfiguration": 368 | return &media.SetAudioSourceConfiguration{}, nil 369 | case "SetAudioEncoderConfiguration": 370 | return &media.SetAudioEncoderConfiguration{}, nil 371 | case "SetVideoAnalyticsConfiguration": 372 | return &media.SetVideoAnalyticsConfiguration{}, nil 373 | case "SetMetadataConfiguration": 374 | return &media.SetMetadataConfiguration{}, nil 375 | case "SetAudioOutputConfiguration": 376 | return &media.SetAudioOutputConfiguration{}, nil 377 | case "SetAudioDecoderConfiguration": 378 | return &media.SetAudioDecoderConfiguration{}, nil 379 | case "GetVideoSourceConfigurationOptions": 380 | return &media.GetVideoSourceConfigurationOptions{}, nil 381 | case "GetVideoEncoderConfigurationOptions": 382 | return &media.GetVideoEncoderConfigurationOptions{}, nil 383 | case "GetAudioSourceConfigurationOptions": 384 | return &media.GetAudioSourceConfigurationOptions{}, nil 385 | case "GetAudioEncoderConfigurationOptions": 386 | return &media.GetAudioEncoderConfigurationOptions{}, nil 387 | case "GetMetadataConfigurationOptions": 388 | return &media.GetMetadataConfigurationOptions{}, nil 389 | case "GetAudioOutputConfigurationOptions": 390 | return &media.GetAudioOutputConfigurationOptions{}, nil 391 | case "GetAudioDecoderConfigurationOptions": 392 | return &media.GetAudioDecoderConfigurationOptions{}, nil 393 | case "GetGuaranteedNumberOfVideoEncoderInstances": 394 | return &media.GetGuaranteedNumberOfVideoEncoderInstances{}, nil 395 | case "GetStreamUri": 396 | return &media.GetStreamUri{}, nil 397 | case "StartMulticastStreaming": 398 | return &media.StartMulticastStreaming{}, nil 399 | case "StopMulticastStreaming": 400 | return &media.StopMulticastStreaming{}, nil 401 | case "SetSynchronizationPoint": 402 | return &media.SetSynchronizationPoint{}, nil 403 | case "GetSnapshotUri": 404 | return &media.GetSnapshotUri{}, nil 405 | case "GetVideoSourceModes": 406 | return &media.GetVideoSourceModes{}, nil 407 | case "SetVideoSourceMode": 408 | return &media.SetVideoSourceMode{}, nil 409 | case "GetOSDs": 410 | return &media.GetOSDs{}, nil 411 | case "GetOSD": 412 | return &media.GetOSD{}, nil 413 | case "GetOSDOptions": 414 | return &media.GetOSDOptions{}, nil 415 | case "SetOSD": 416 | return &media.SetOSD{}, nil 417 | case "CreateOSD": 418 | return &media.CreateOSD{}, nil 419 | case "DeleteOSD": 420 | return &media.DeleteOSD{}, nil 421 | default: 422 | return nil, errors.New("there is no such method in the Media service") 423 | } 424 | 425 | } 426 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | //Package onvif is developed to provide an ONVIF client implementation on Go programming language 2 | package onvif 3 | -------------------------------------------------------------------------------- /docs/ONVIF-Core-Specification.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/ONVIF-Core-Specification.pdf -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | some specs 2 | 3 | + [ONVIF-Core-Specification -2019](./ONVIF-Core-Specification.pdf) 4 | -------------------------------------------------------------------------------- /docs/img/GetServiceCapabilities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/img/GetServiceCapabilities.png -------------------------------------------------------------------------------- /docs/img/exmp_ContinuousMove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/img/exmp_ContinuousMove.png -------------------------------------------------------------------------------- /docs/img/exmp_CreateUsers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/img/exmp_CreateUsers.png -------------------------------------------------------------------------------- /docs/img/exmp_GetCapabilities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/img/exmp_GetCapabilities.png -------------------------------------------------------------------------------- /docs/img/exmp_GetProfiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanap-infra/onvif/b6bc19cd5d2e191cb8a0052ebe63326f3be1f795/docs/img/exmp_GetProfiles.png -------------------------------------------------------------------------------- /docs/wsdl/WS-BaseNotification.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | -------------------------------------------------------------------------------- /docs/wsdl/WS-Resource.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/wsdl/display.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | The capabilities for the display service is returned in the Capabilities element. 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Indication that the SetLayout command supports only predefined layouts. 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | Token of the Video Output whose Layout is requested 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | Current layout of the video output. 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Token of the Video Output whose Layout shall be changed. 74 | 75 | 76 | 77 | 78 | Layout to be set 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | Token of the Video Output whose options are requested 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | The LayoutOptions describe the fixed and predefined layouts of a device. If the device does 113 | not offer fixed layouts and allows setting the layout free this element is empty. 114 | 115 | 116 | 117 | 118 | decoding and encoding capabilities of the device 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | Reference Token of the Video Output whose Pane Configurations are requested 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | Contains a list of defined Panes of the specified VideoOutput. Each VideoOutput has at least one PaneConfiguration. 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | Reference Token of the Video Output the requested pane belongs to 157 | 158 | 159 | 160 | 161 | Reference Token of the Pane whose Configuration is requested 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | returns the configuration of the requested pane. 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | Token of the video output whose panes to set. 188 | 189 | 190 | 191 | 192 | Pane Configuration to be set. 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | Token of the video output whose panes to set. 213 | 214 | 215 | 216 | 217 | Pane Configuration to be set. 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | Token of the video output where the pane shall be created. 239 | 240 | 241 | 242 | 243 | Configuration of the pane to be created. 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | Token of the new pane configuration. 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | Token of the video output where the pane shall be deleted. 270 | 271 | 272 | 273 | 274 | Token of the pane to be deleted. 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | Returns the capabilities of the display service. The result is returned in a typed answer. 354 | 355 | 356 | 357 | 358 | Return the current layout of a video output. The Layout assigns a pane configuration to a certain area of the display. The layout settings 359 | directly affect a specific video output. The layout consists of a list of PaneConfigurations and 360 | their associated display areas. 361 | 362 | 363 | 364 | 365 | Change the layout of a display (e.g. change from 366 | single view to split screen view).The Layout assigns a pane configuration to a certain area of the display. The layout settings 367 | directly affect a specific video output. The layout consists of a list of PaneConfigurations and 368 | their associated display areas.
369 | A device implementation shall be tolerant against rounding errors when matching a layout against its fixed set of layouts by accepting differences of at least one percent. 370 |
371 | 372 | 373 |
374 | 375 | The Display Options contain the supported layouts (LayoutOptions) and the decoding and 376 | encoding capabilities (CodingCapabilities) of the device. The GetDisplayOptions command 377 | returns both, Layout and Coding Capabilities, of a VideoOutput. 378 | 379 | 380 | 381 | 382 | List all currently defined panes of a device for a specified video output 383 | (regardless if this pane is visible at a moment). A Pane is a display area on the monitor that is attached to a video output. A pane has a 384 | PaneConfiguration that describes which entities are associated with the pane. A client has to configure the pane according to the connection to be established by setting the 385 | AudioOutput and/or AudioSourceToken. If a Token is not set, the corresponding session will 386 | not be established. 387 | 388 | 389 | 390 | 391 | Retrieve the pane configuration for a pane token. 392 | 393 | 394 | 395 | 396 | Modify one or more configurations of the specified video output. 397 | This method will only modify the provided configurations and leave the others unchanged. 398 | Use DeletePaneConfiguration to remove pane configurations. 399 | 400 | 401 | 402 | 403 | This command changes the configuration of the specified pane (tbd) 404 | 405 | 406 | 407 | 408 | Create a new pane configuration describing the streaming and coding settings for a display area.
409 | This optional method is only supported by devices that signal support of dynamic pane creation via their capabilities.
410 | The content of the Token field may be ignored by the device. 411 |
412 | 413 | 414 |
415 | 416 | Delete a pane configuration. A service must respond with an error if the pane configuration 417 | is in use by the current layout.
418 | This optional method is only supported by devices that signal support of dynamic pane creation via their capabilities. 419 |
420 | 421 | 422 |
423 |
424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 |
518 | -------------------------------------------------------------------------------- /docs/wsdl/receiver.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | The capabilities for the receiver service is returned in the Capabilities element. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Indicates that the device can receive RTP multicast streams. 41 | 42 | 43 | 44 | 45 | Indicates that the device can receive RTP/TCP streams 46 | 47 | 48 | 49 | 50 | Indicates that the device can receive RTP/RTSP/TCP streams. 51 | 52 | 53 | 54 | 55 | The maximum number of receivers supported by the device. 56 | 57 | 58 | 59 | 60 | The maximum allowed length for RTSP URIs (Minimum and default value is 128 octet). 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | A list of all receivers that currently exist on the device. 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | The token of the receiver to be retrieved. 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | The details of the receiver. 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | The initial configuration for the new receiver. 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | The details of the receiver that was created. 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | The token of the receiver to be deleted. 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | The token of the receiver to be configured. 151 | 152 | 153 | 154 | 155 | The new configuration for the receiver. 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | The token of the receiver to be changed. 173 | 174 | 175 | 176 | 177 | The new receiver mode. Options available are: 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | The token of the receiver to be queried. 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | Description of the current receiver state. 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | Returns the capabilities of the receiver service. The result is returned in a typed answer. 264 | 265 | 266 | 267 | 268 | 269 | Lists all receivers currently present on a device. This operation is mandatory. 270 | 271 | 272 | 273 | 274 | 275 | 276 | Retrieves the details of a specific receiver. This operation is mandatory. 277 | 278 | 279 | 280 | 281 | 282 | 283 | Creates a new receiver. This operation is mandatory, although the service may 284 | raise a fault if the receiver cannot be created. 285 | 286 | 287 | 288 | 289 | 290 | 291 | Deletes an existing receiver. A receiver may be deleted only if it is not 292 | currently in use; otherwise a fault shall be raised. 293 | This operation is mandatory. 294 | 295 | 296 | 297 | 298 | 299 | 300 | Configures an existing receiver. This operation is mandatory. 301 | 302 | 303 | 304 | 305 | 306 | 307 | Sets the mode of the receiver without affecting the rest of its configuration. 308 | This operation is mandatory. 309 | 310 | 311 | 312 | 313 | 314 | 315 | Determines whether the receiver is currently disconnected, connected or 316 | attempting to connect. 317 | This operation is mandatory. 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | -------------------------------------------------------------------------------- /docs/wsdl/replay.wsdl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | The capabilities for the replay service is returned in the Capabilities element. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Indicator that the Device supports reverse playback as defined in the ONVIF Streaming Specification. 41 | 42 | 43 | 44 | 45 | The list contains two elements defining the minimum and maximum valid values supported as session timeout in seconds. 46 | 47 | 48 | 49 | 50 | Indicates support for RTP/RTSP/TCP. 51 | 52 | 53 | 54 | 55 | If playback streaming over websocket supported, websocket URI is provided. The scheme and IP part shall match the one used in the request (e.g. the GetServices request). 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | Specifies the connection parameters to be used for the stream. The URI that is returned may depend on these parameters. 68 | 69 | 70 | 71 | 72 | The identifier of the recording to be streamed. 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | The URI to which the client should connect in order to stream the recording. 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | Description of the new replay configuration parameters. 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | The current replay configuration parameters. 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | Returns the capabilities of the replay service. The result is returned in a typed answer. 152 | 153 | 154 | 155 | 156 | 157 | Requests a URI that can be used to initiate playback of a recorded stream 158 | using RTSP as the control protocol. The URI is valid only as it is 159 | specified in the response. 160 | This operation is mandatory. 161 | 162 | 163 | 164 | 165 | 166 | 167 | Returns the current configuration of the replay service. 168 | This operation is mandatory. 169 | 170 | 171 | 172 | 173 | 174 | 175 | Changes the current configuration of the replay service. 176 | This operation is mandatory. 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /event/operation.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "github.com/fanap-infra/onvif/xsd" 5 | ) 6 | 7 | //GetServiceCapabilities action 8 | type GetServiceCapabilities struct { 9 | XMLName string `xml:"tev:GetServiceCapabilities"` 10 | } 11 | 12 | //GetServiceCapabilitiesResponse type 13 | type GetServiceCapabilitiesResponse struct { 14 | Capabilities Capabilities 15 | } 16 | 17 | //SubscriptionPolicy action 18 | type SubscriptionPolicy struct { //tev http://www.onvif.org/ver10/events/wsdl 19 | ChangedOnly xsd.Boolean `xml:"ChangedOnly,attr"` 20 | } 21 | 22 | //Subscribe action for subscribe event topic 23 | type Subscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd 24 | XMLName struct{} `xml:"wsnt:Subscribe"` 25 | ConsumerReference EndpointReferenceType `xml:"wsnt:ConsumerReference"` 26 | Filter FilterType `xml:"wsnt:Filter"` 27 | SubscriptionPolicy SubscriptionPolicy `xml:"wsnt:SubscriptionPolicy"` 28 | InitialTerminationTime TerminationTime `xml:"wsnt:InitialTerminationTime"` 29 | } 30 | 31 | //SubscribeResponse message for subscribe event topic 32 | type SubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd 33 | ConsumerReference EndpointReferenceType `xml:"wsnt:ConsumerReference"` 34 | CurrentTime CurrentTime `xml:"wsnt:CurrentTime"` 35 | TerminationTime TerminationTime `xml:"wsnt:TerminationTime"` 36 | } 37 | 38 | //Renew action for refresh event topic subscription 39 | type Renew struct { //http://docs.oasis-open.org/wsn/b-2.xsd 40 | TerminationTime AbsoluteOrRelativeTimeType `xml:"wsnt:TerminationTime"` 41 | } 42 | 43 | //RenewResponse for Renew action 44 | type RenewResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd 45 | TerminationTime TerminationTime `xml:"wsnt:TerminationTime"` 46 | CurrentTime CurrentTime `xml:"wsnt:CurrentTime"` 47 | } 48 | 49 | //Unsubscribe action for Unsubscribe event topic 50 | type Unsubscribe struct { //http://docs.oasis-open.org/wsn/b-2.xsd 51 | Any string 52 | } 53 | 54 | //UnsubscribeResponse message for Unsubscribe event topic 55 | type UnsubscribeResponse struct { //http://docs.oasis-open.org/wsn/b-2.xsd 56 | Any string 57 | } 58 | 59 | //CreatePullPointSubscription action 60 | //BUG(r) Bad AbsoluteOrRelativeTimeType type 61 | type CreatePullPointSubscription struct { 62 | XMLName string `xml:"tev:CreatePullPointSubscription"` 63 | Filter FilterType `xml:"tev:Filter"` 64 | InitialTerminationTime AbsoluteOrRelativeTimeType `xml:"wsnt:InitialTerminationTime"` 65 | SubscriptionPolicy SubscriptionPolicy `xml:"wsnt:sSubscriptionPolicy"` 66 | } 67 | 68 | //CreatePullPointSubscriptionResponse action 69 | type CreatePullPointSubscriptionResponse struct { 70 | SubscriptionReference EndpointReferenceType 71 | CurrentTime CurrentTime 72 | TerminationTime TerminationTime 73 | } 74 | 75 | //GetEventProperties action 76 | type GetEventProperties struct { 77 | XMLName string `xml:"tev:GetEventProperties"` 78 | } 79 | 80 | //GetEventPropertiesResponse action 81 | type GetEventPropertiesResponse struct { 82 | TopicNamespaceLocation xsd.AnyURI 83 | FixedTopicSet FixedTopicSet 84 | TopicSet TopicSet 85 | TopicExpressionDialect TopicExpressionDialect 86 | MessageContentFilterDialect xsd.AnyURI 87 | ProducerPropertiesFilterDialect xsd.AnyURI 88 | MessageContentSchemaLocation xsd.AnyURI 89 | } 90 | 91 | //Port type PullPointSubscription 92 | 93 | //PullMessages Action 94 | type PullMessages struct { 95 | XMLName string `xml:"tev:PullMessages"` 96 | Timeout xsd.Duration `xml:"tev:Timeout"` 97 | MessageLimit xsd.Int `xml:"tev:MessageLimit"` 98 | } 99 | 100 | //PullMessagesResponse response type 101 | type PullMessagesResponse struct { 102 | CurrentTime CurrentTime 103 | TerminationTime TerminationTime 104 | NotificationMessage NotificationMessage 105 | } 106 | 107 | //PullMessagesFaultResponse response type 108 | type PullMessagesFaultResponse struct { 109 | MaxTimeout xsd.Duration 110 | MaxMessageLimit xsd.Int 111 | } 112 | 113 | //Seek action 114 | type Seek struct { 115 | XMLName string `xml:"tev:Seek"` 116 | UtcTime xsd.DateTime `xml:"tev:UtcTime"` 117 | Reverse xsd.Boolean `xml:"tev:Reverse"` 118 | } 119 | 120 | //SeekResponse action 121 | type SeekResponse struct { 122 | } 123 | 124 | //SetSynchronizationPoint action 125 | type SetSynchronizationPoint struct { 126 | XMLName string `xml:"tev:SetSynchronizationPoint"` 127 | } 128 | 129 | //SetSynchronizationPointResponse action 130 | type SetSynchronizationPointResponse struct { 131 | } 132 | -------------------------------------------------------------------------------- /event/types.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "github.com/fanap-infra/onvif/xsd" 5 | ) 6 | 7 | //Address Alias 8 | type Address xsd.String 9 | 10 | //CurrentTime alias 11 | type CurrentTime xsd.DateTime //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 12 | //TerminationTime alias 13 | type TerminationTime xsd.DateTime //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 14 | //FixedTopicSet alias 15 | type FixedTopicSet xsd.Boolean //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 16 | 17 | //Documentation alias 18 | type Documentation xsd.AnyType //wstop http://docs.oasis-open.org/wsn/t-1.xsd 19 | 20 | //TopicExpressionDialect alias 21 | type TopicExpressionDialect xsd.AnyURI 22 | 23 | //Message alias 24 | type Message xsd.AnyType 25 | 26 | //ActionType for AttributedURIType 27 | type ActionType AttributedURIType 28 | 29 | //AttributedURIType in ws-addr 30 | type AttributedURIType xsd.AnyURI //wsa https://www.w3.org/2005/08/addressing/ws-addr.xsd 31 | 32 | //AbsoluteOrRelativeTimeType 33 | type AbsoluteOrRelativeTimeType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 34 | xsd.DateTime 35 | xsd.Duration 36 | } 37 | 38 | //EndpointReferenceType in ws-addr 39 | type EndpointReferenceType struct { //wsa http://www.w3.org/2005/08/addressing/ws-addr.xsd 40 | Address AttributedURIType `xml:"wsnt:Address"` 41 | ReferenceParameters ReferenceParametersType `xml:"wsnt:ReferenceParameters"` 42 | Metadata MetadataType `xml:"wsnt:Metadata"` 43 | } 44 | 45 | // FilterType struct 46 | type FilterType struct { 47 | TopicExpression TopicExpressionType `xml:"wsnt:TopicExpression"` 48 | MessageContent QueryExpressionType `xml:"wsnt:MessageContent"` 49 | } 50 | 51 | //EndpointReference alais 52 | type EndpointReference EndpointReferenceType 53 | 54 | //ReferenceParametersType in ws-addr 55 | type ReferenceParametersType struct { //wsa https://www.w3.org/2005/08/addressing/ws-addr.xsd 56 | //Here can be anyAttribute 57 | } 58 | 59 | //Metadata in ws-addr 60 | type Metadata MetadataType //wsa https://www.w3.org/2005/08/addressing/ws-addr.xsd 61 | 62 | //MetadataType in ws-addr 63 | type MetadataType struct { //wsa https://www.w3.org/2005/08/addressing/ws-addr.xsd 64 | 65 | //Here can be anyAttribute 66 | } 67 | 68 | //TopicSet alias 69 | type TopicSet TopicSetType //wstop http://docs.oasis-open.org/wsn/t-1.xsd 70 | 71 | //TopicSetType alias 72 | type TopicSetType struct { //wstop http://docs.oasis-open.org/wsn/t-1.xsd 73 | ExtensibleDocumented 74 | //here can be any element 75 | } 76 | 77 | //ExtensibleDocumented struct 78 | type ExtensibleDocumented struct { //wstop http://docs.oasis-open.org/wsn/t-1.xsd 79 | Documentation Documentation //к xsd-документе documentation с маленькой буквы начинается 80 | //here can be anyAttribute 81 | } 82 | 83 | //ProducerReference Alias 84 | type ProducerReference EndpointReferenceType 85 | 86 | //SubscriptionReference Alias 87 | type SubscriptionReference EndpointReferenceType 88 | 89 | //NotificationMessageHolderType Alias 90 | type NotificationMessageHolderType struct { 91 | SubscriptionReference SubscriptionReference //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 92 | Topic Topic 93 | ProducerReference ProducerReference 94 | Message Message 95 | } 96 | 97 | //NotificationMessage Alias 98 | type NotificationMessage NotificationMessageHolderType //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 99 | 100 | //QueryExpressionType struct for wsnt:MessageContent 101 | type QueryExpressionType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 102 | Dialect xsd.AnyURI `xml:"Dialect,attr"` 103 | MessageKind xsd.String `xml:",chardata"` // boolean(ncex:Producer="15") 104 | } 105 | 106 | //MessageContentType Alias 107 | type MessageContentType QueryExpressionType 108 | 109 | //QueryExpression Alias 110 | type QueryExpression QueryExpressionType 111 | 112 | //TopicExpressionType struct for wsnt:TopicExpression 113 | type TopicExpressionType struct { //wsnt http://docs.oasis-open.org/wsn/b-2.xsd 114 | Dialect xsd.AnyURI `xml:"Dialect,attr"` 115 | TopicKinds xsd.String `xml:",chardata"` 116 | } 117 | 118 | //Topic Alias 119 | type Topic TopicExpressionType 120 | 121 | // Capabilities of event 122 | type Capabilities struct { //tev 123 | WSSubscriptionPolicySupport xsd.Boolean `xml:"WSSubscriptionPolicySupport,attr"` 124 | WSPullPointSupport xsd.Boolean `xml:"WSPullPointSupport,attr"` 125 | WSPausableSubscriptionManagerInterfaceSupport xsd.Boolean `xml:"WSPausableSubscriptionManagerInterfaceSupport,attr"` 126 | MaxNotificationProducers xsd.Int `xml:"MaxNotificationProducers,attr"` 127 | MaxPullPoints xsd.Int `xml:"MaxPullPoints,attr"` 128 | PersistentNotificationStorage xsd.Boolean `xml:"PersistentNotificationStorage,attr"` 129 | } 130 | 131 | //ResourceUnknownFault response type 132 | type ResourceUnknownFault struct { 133 | } 134 | 135 | //InvalidFilterFault response type 136 | type InvalidFilterFault struct { 137 | } 138 | 139 | //TopicExpressionDialectUnknownFault response type 140 | type TopicExpressionDialectUnknownFault struct { 141 | } 142 | 143 | //InvalidTopicExpressionFault response type 144 | type InvalidTopicExpressionFault struct { 145 | } 146 | 147 | //TopicNotSupportedFault response type 148 | type TopicNotSupportedFault struct { 149 | } 150 | 151 | //InvalidProducerPropertiesExpressionFault response type 152 | type InvalidProducerPropertiesExpressionFault struct { 153 | } 154 | 155 | //InvalidMessageContentExpressionFault response type 156 | type InvalidMessageContentExpressionFault struct { 157 | } 158 | 159 | //UnacceptableInitialTerminationTimeFault response type 160 | type UnacceptableInitialTerminationTimeFault struct { 161 | } 162 | 163 | //UnrecognizedPolicyRequestFault response type 164 | type UnrecognizedPolicyRequestFault struct { 165 | } 166 | 167 | //UnsupportedPolicyRequestFault response type 168 | type UnsupportedPolicyRequestFault struct { 169 | } 170 | 171 | //NotifyMessageNotSupportedFault response type 172 | type NotifyMessageNotSupportedFault struct { 173 | } 174 | 175 | //SubscribeCreationFailedFault response type 176 | type SubscribeCreationFailedFault struct { 177 | } 178 | -------------------------------------------------------------------------------- /examples/DeviceService.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "net/http" 8 | 9 | goonvif "github.com/fanap-infra/onvif" 10 | "github.com/fanap-infra/onvif/device" 11 | "github.com/fanap-infra/onvif/gosoap" 12 | "github.com/fanap-infra/onvif/xsd/onvif" 13 | ) 14 | 15 | const ( 16 | login = "admin" 17 | password = "Supervisor" 18 | ) 19 | 20 | func readResponse(resp *http.Response) string { 21 | b, err := ioutil.ReadAll(resp.Body) 22 | if err != nil { 23 | panic(err) 24 | } 25 | return string(b) 26 | } 27 | 28 | func main() { 29 | //Getting an camera instance 30 | dev, err := goonvif.NewDevice("192.168.13.14:80") 31 | if err != nil { 32 | panic(err) 33 | } 34 | //Authorization 35 | dev.Authenticate(login, password) 36 | 37 | //Preparing commands 38 | systemDateAndTyme := device.GetSystemDateAndTime{} 39 | getCapabilities := device.GetCapabilities{Category: "All"} 40 | createUser := device.CreateUsers{User: onvif.User{ 41 | Username: "TestUser", 42 | Password: "TestPassword", 43 | UserLevel: "User", 44 | }, 45 | } 46 | 47 | //Commands execution 48 | systemDateAndTymeResponse, err := dev.CallMethod(systemDateAndTyme) 49 | if err != nil { 50 | log.Println(err) 51 | } else { 52 | fmt.Println(readResponse(systemDateAndTymeResponse)) 53 | } 54 | getCapabilitiesResponse, err := dev.CallMethod(getCapabilities) 55 | if err != nil { 56 | log.Println(err) 57 | } else { 58 | fmt.Println(readResponse(getCapabilitiesResponse)) 59 | } 60 | createUserResponse, err := dev.CallMethod(createUser) 61 | if err != nil { 62 | log.Println(err) 63 | } else { 64 | /* 65 | You could use https://github.com/fanap-infra/onvif/gosoap for pretty printing response 66 | */ 67 | fmt.Println(gosoap.SoapMessage(readResponse(createUserResponse)).StringIndent()) 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /examples/discovery_test.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | "path" 8 | "regexp" 9 | "strings" 10 | "testing" 11 | 12 | "github.com/beevik/etree" 13 | "github.com/fanap-infra/onvif" 14 | "github.com/fanap-infra/onvif/device" 15 | discover "github.com/fanap-infra/onvif/ws-discovery" 16 | ) 17 | 18 | func TestGetAvailableDevicesAtSpecificEthernetInterface(t *testing.T) { 19 | 20 | // client() 21 | // runDiscovery("en0") 22 | s := onvif.GetAvailableDevicesAtSpecificEthernetInterface("en0") 23 | 24 | log.Printf("%s", s) 25 | } 26 | 27 | func client() { 28 | dev, err := onvif.NewDevice("192.168.3.10") 29 | if err != nil { 30 | panic(err) 31 | } 32 | dev.Authenticate("admin", "zsyy12345") 33 | 34 | log.Printf("output %+v", dev.GetServices()) 35 | 36 | res, err := dev.CallMethod(device.GetUsers{}) 37 | bs, _ := ioutil.ReadAll(res.Body) 38 | log.Printf("output %+v %s", res.StatusCode, bs) 39 | } 40 | 41 | // Host host 42 | type Host struct { 43 | URL string `json:"url"` 44 | Name string `json:"name"` 45 | } 46 | 47 | func runDiscovery(interfaceName string) { 48 | var hosts []*Host 49 | devices := discover.SendProbe(interfaceName, nil, []string{"dn:NetworkVideoTransmitter"}, map[string]string{"dn": "http://www.onvif.org/ver10/network/wsdl"}) 50 | for _, j := range devices { 51 | doc := etree.NewDocument() 52 | if err := doc.ReadFromString(j); err != nil { 53 | log.Printf("error %s", err) 54 | } else { 55 | 56 | endpoints := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/XAddrs") 57 | scopes := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/Scopes") 58 | 59 | flag := false 60 | 61 | host := &Host{} 62 | 63 | for _, xaddr := range endpoints { 64 | xaddr := strings.Split(strings.Split(xaddr.Text(), " ")[0], "/")[2] 65 | host.URL = xaddr 66 | } 67 | if flag { 68 | break 69 | } 70 | for _, scope := range scopes { 71 | re := regexp.MustCompile(`onvif:\/\/www\.onvif\.org\/name\/[A-Za-z0-9-]+`) 72 | match := re.FindStringSubmatch(scope.Text()) 73 | host.Name = path.Base(match[0]) 74 | } 75 | 76 | hosts = append(hosts, host) 77 | 78 | } 79 | 80 | } 81 | 82 | bys, _ := json.Marshal(hosts) 83 | log.Printf("done %s", bys) 84 | } 85 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/fanap-infra/onvif 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/beevik/etree v1.1.0 7 | github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae 8 | github.com/gin-gonic/gin v1.6.2 9 | github.com/gofrs/uuid v3.2.0+incompatible 10 | github.com/videonext/onvif v0.0.0-20200826090911-d0d7546064b0 11 | github.com/xinsnake/go-http-digest-auth-client v0.6.0 12 | golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 13 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd 14 | ) 15 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs= 2 | github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= 3 | github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= 4 | github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae h1:3KvK2DmA7TxQ6PZ2f0rWbdqjgJhRcqgbY70bBeE4clI= 9 | github.com/elgs/gostrgen v0.0.0-20161222160715-9d61ae07eeae/go.mod h1:wruC5r2gHdr/JIUs5Rr1V45YtsAzKXZxAnn/5rPC97g= 10 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 11 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 12 | github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= 13 | github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 14 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 15 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 16 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 17 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 18 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 19 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 20 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 21 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 22 | github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= 23 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 24 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 25 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 26 | github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= 27 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 28 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 29 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 30 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 31 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 32 | github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= 33 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 34 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 35 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 36 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 37 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 38 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 39 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 40 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 41 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 42 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 43 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 44 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 45 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 46 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 47 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 48 | github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4= 49 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 50 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 51 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 52 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 53 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 54 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 55 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 56 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 57 | github.com/videonext/onvif v0.0.0-20200826090911-d0d7546064b0 h1:Bo682TXhSPsGaDPvZQ56R5tr5hTyOE5owdygVJ+P1Qg= 58 | github.com/videonext/onvif v0.0.0-20200826090911-d0d7546064b0/go.mod h1:6dBZF29oj/o/ElU7g2Q+JFWi07VbHUyfvQ3YDk6aWj8= 59 | github.com/xinsnake/go-http-digest-auth-client v0.6.0 h1:nrYFWDrB2F7VwYlNravXZS0nOtg9axlATH3Jns55/F0= 60 | github.com/xinsnake/go-http-digest-auth-client v0.6.0/go.mod h1:QK1t1v7ylyGb363vGWu+6Irh7gyFj+N7+UZzM0L6g8I= 61 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= 62 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 63 | golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U= 64 | golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 65 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= 68 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 70 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 71 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 72 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc= 73 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 74 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 75 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 76 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 77 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 78 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 79 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 80 | -------------------------------------------------------------------------------- /gosoap/README.md: -------------------------------------------------------------------------------- 1 | # gosoap 2 | -------------------------------------------------------------------------------- /gosoap/soap-builder.go: -------------------------------------------------------------------------------- 1 | package gosoap 2 | 3 | import ( 4 | "encoding/xml" 5 | "log" 6 | 7 | "github.com/beevik/etree" 8 | ) 9 | 10 | //SoapMessage type from string 11 | type SoapMessage string 12 | 13 | // NewEmptySOAP return new SoapMessage 14 | func NewEmptySOAP() SoapMessage { 15 | doc := buildSoapRoot() 16 | //doc.IndentTabs() 17 | 18 | res, _ := doc.WriteToString() 19 | 20 | return SoapMessage(res) 21 | } 22 | 23 | //NewSOAP Get a new soap message 24 | func NewSOAP(headContent []*etree.Element, bodyContent []*etree.Element, namespaces map[string]string) SoapMessage { 25 | doc := buildSoapRoot() 26 | //doc.IndentTabs() 27 | 28 | res, _ := doc.WriteToString() 29 | 30 | return SoapMessage(res) 31 | } 32 | 33 | func (msg SoapMessage) String() string { 34 | return string(msg) 35 | } 36 | 37 | //StringIndent handle indent 38 | func (msg SoapMessage) StringIndent() string { 39 | doc := etree.NewDocument() 40 | 41 | if err := doc.ReadFromString(msg.String()); err != nil { 42 | log.Println(err.Error()) 43 | } 44 | 45 | doc.IndentTabs() 46 | res, _ := doc.WriteToString() 47 | 48 | return res 49 | } 50 | 51 | //Body return body from Envelope 52 | func (msg SoapMessage) Body() string { 53 | 54 | doc := etree.NewDocument() 55 | 56 | if err := doc.ReadFromString(msg.String()); err != nil { 57 | log.Println(err.Error()) 58 | } 59 | bodyTag := doc.Root().SelectElement("Body").ChildElements()[0] 60 | doc.SetRoot(bodyTag) 61 | doc.IndentTabs() 62 | 63 | res, _ := doc.WriteToString() 64 | 65 | return res 66 | } 67 | 68 | //AddStringBodyContent for Envelope 69 | func (msg *SoapMessage) AddStringBodyContent(data string) { 70 | doc := etree.NewDocument() 71 | 72 | if err := doc.ReadFromString(data); err != nil { 73 | log.Println(err.Error()) 74 | } 75 | 76 | element := doc.Root() 77 | 78 | doc = etree.NewDocument() 79 | if err := doc.ReadFromString(msg.String()); err != nil { 80 | log.Println(err.Error()) 81 | } 82 | //doc.FindElement("./Envelope/Body").AddChild(element) 83 | bodyTag := doc.Root().SelectElement("Body") 84 | bodyTag.AddChild(element) 85 | 86 | //doc.IndentTabs() 87 | res, _ := doc.WriteToString() 88 | 89 | *msg = SoapMessage(res) 90 | } 91 | 92 | //AddBodyContent for Envelope 93 | func (msg *SoapMessage) AddBodyContent(element *etree.Element) { 94 | doc := etree.NewDocument() 95 | if err := doc.ReadFromString(msg.String()); err != nil { 96 | log.Println(err.Error()) 97 | } 98 | //doc.FindElement("./Envelope/Body").AddChild(element) 99 | bodyTag := doc.Root().SelectElement("Body") 100 | bodyTag.AddChild(element) 101 | 102 | //doc.IndentTabs() 103 | res, _ := doc.WriteToString() 104 | 105 | *msg = SoapMessage(res) 106 | } 107 | 108 | //AddBodyContents for Envelope body 109 | func (msg *SoapMessage) AddBodyContents(elements []*etree.Element) { 110 | doc := etree.NewDocument() 111 | if err := doc.ReadFromString(msg.String()); err != nil { 112 | log.Println(err.Error()) 113 | } 114 | 115 | bodyTag := doc.Root().SelectElement("Body") 116 | 117 | if len(elements) != 0 { 118 | for _, j := range elements { 119 | bodyTag.AddChild(j) 120 | } 121 | } 122 | 123 | //doc.IndentTabs() 124 | res, _ := doc.WriteToString() 125 | 126 | *msg = SoapMessage(res) 127 | } 128 | 129 | //AddStringHeaderContent for Envelope body 130 | func (msg *SoapMessage) AddStringHeaderContent(data string) error { 131 | doc := etree.NewDocument() 132 | 133 | if err := doc.ReadFromString(data); err != nil { 134 | //log.Println(err.Error()) 135 | return err 136 | } 137 | 138 | element := doc.Root() 139 | 140 | doc = etree.NewDocument() 141 | if err := doc.ReadFromString(msg.String()); err != nil { 142 | //log.Println(err.Error()) 143 | return err 144 | } 145 | 146 | bodyTag := doc.Root().SelectElement("Header") 147 | bodyTag.AddChild(element) 148 | 149 | //doc.IndentTabs() 150 | res, _ := doc.WriteToString() 151 | 152 | *msg = SoapMessage(res) 153 | 154 | return nil 155 | } 156 | 157 | //AddHeaderContent for Envelope body 158 | func (msg *SoapMessage) AddHeaderContent(element *etree.Element) { 159 | doc := etree.NewDocument() 160 | if err := doc.ReadFromString(msg.String()); err != nil { 161 | log.Println(err.Error()) 162 | } 163 | 164 | bodyTag := doc.Root().SelectElement("Header") 165 | bodyTag.AddChild(element) 166 | 167 | //doc.IndentTabs() 168 | res, _ := doc.WriteToString() 169 | 170 | *msg = SoapMessage(res) 171 | } 172 | 173 | //AddHeaderContents for Envelope body 174 | func (msg *SoapMessage) AddHeaderContents(elements []*etree.Element) { 175 | doc := etree.NewDocument() 176 | if err := doc.ReadFromString(msg.String()); err != nil { 177 | log.Println(err.Error()) 178 | } 179 | 180 | headerTag := doc.Root().SelectElement("Header") 181 | 182 | if len(elements) != 0 { 183 | for _, j := range elements { 184 | headerTag.AddChild(j) 185 | } 186 | } 187 | 188 | //doc.IndentTabs() 189 | res, _ := doc.WriteToString() 190 | 191 | *msg = SoapMessage(res) 192 | } 193 | 194 | //AddRootNamespace for Envelope body 195 | func (msg *SoapMessage) AddRootNamespace(key, value string) { 196 | doc := etree.NewDocument() 197 | if err := doc.ReadFromString(msg.String()); err != nil { 198 | log.Println(err.Error()) 199 | } 200 | doc.Root().CreateAttr("xmlns:"+key, value) 201 | //doc.IndentTabs() 202 | res, _ := doc.WriteToString() 203 | 204 | *msg = SoapMessage(res) 205 | } 206 | 207 | //AddRootNamespaces for Envelope body 208 | func (msg *SoapMessage) AddRootNamespaces(namespaces map[string]string) { 209 | for key, value := range namespaces { 210 | msg.AddRootNamespace(key, value) 211 | } 212 | 213 | /* 214 | doc := etree.NewDocument() 215 | if err := doc.ReadFromString(msg.String()); err != nil { 216 | //log.Println(err.Error()) 217 | return err 218 | } 219 | 220 | for key, value := range namespaces { 221 | doc.Root().CreateAttr("xmlns:" + key, value) 222 | } 223 | 224 | doc.IndentTabs() 225 | res, _ := doc.WriteToString() 226 | 227 | *msg = SoapMessage(res)*/ 228 | } 229 | 230 | func buildSoapRoot() *etree.Document { 231 | doc := etree.NewDocument() 232 | 233 | doc.CreateProcInst("xml", `version="1.0" encoding="UTF-8"`) 234 | 235 | env := doc.CreateElement("soap-env:Envelope") 236 | env.CreateElement("soap-env:Header") 237 | env.CreateElement("soap-env:Body") 238 | 239 | env.CreateAttr("xmlns:soap-env", "http://www.w3.org/2003/05/soap-envelope") 240 | env.CreateAttr("xmlns:soap-enc", "http://www.w3.org/2003/05/soap-encoding") 241 | 242 | return doc 243 | } 244 | 245 | //AddWSSecurity Header for soapMessage 246 | func (msg *SoapMessage) AddWSSecurity(username, password string) { 247 | //doc := etree.NewDocument() 248 | //if err := doc.ReadFromString(msg.String()); err != nil { 249 | // log.Println(err.Error()) 250 | //} 251 | /* 252 | Getting an WS-Security struct representation 253 | */ 254 | auth := NewSecurity(username, password) 255 | 256 | /* 257 | Adding WS-Security namespaces to root element of SOAP message 258 | */ 259 | //msg.AddRootNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext1.0.xsd") 260 | //msg.AddRootNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility1.0.xsd") 261 | 262 | soapReq, err := xml.MarshalIndent(auth, "", " ") 263 | if err != nil { 264 | //log.Printf("error: %v\n", err.Error()) 265 | panic(err) 266 | } 267 | 268 | /* 269 | Adding WS-Security struct to SOAP header 270 | */ 271 | msg.AddStringHeaderContent(string(soapReq)) 272 | 273 | //doc.IndentTabs() 274 | //res, _ := doc.WriteToString() 275 | // 276 | //*msg = SoapMessage(res) 277 | } 278 | 279 | //AddAction Header handling for soapMessage 280 | func (msg *SoapMessage) AddAction() { 281 | 282 | doc := etree.NewDocument() 283 | if err := doc.ReadFromString(msg.String()); err != nil { 284 | log.Println(err.Error()) 285 | } 286 | // opetaionTag := doc.Root().SelectElement("Body") 287 | 288 | // firstElemnt := opetaionTag.Child[0] 289 | 290 | // soapReq, err := xml.MarshalIndent(action, "", " ") 291 | // if err != nil { 292 | // //log.Printf("error: %v\n", err.Error()) 293 | // panic(err) 294 | // } 295 | // /* 296 | // Adding WS-Security struct to SOAP header 297 | // */ 298 | // msg.AddStringHeaderContent(string(soapReq)) 299 | 300 | // //doc.IndentTabs() 301 | // //res, _ := doc.WriteToString() 302 | // // 303 | // //*msg = SoapMessage(res) 304 | } 305 | -------------------------------------------------------------------------------- /gosoap/ws-action.go: -------------------------------------------------------------------------------- 1 | package gosoap 2 | 3 | import ( 4 | "encoding/xml" 5 | ) 6 | 7 | //Xlmns XML Scheam 8 | var actionHeaders = map[string]string{ 9 | "wsnt:Subscribe": "http://docs.oasis-open.org/wsn/bw-2/NotificationProducer/SubscribeRequest", 10 | "ResumeSubscription": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/ResumeSubscriptionRequest", 11 | "PauseSubscription": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/PauseSubscriptionRequest", 12 | "Unsubscribe": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/UnsubscribeRequest", 13 | "RenewRequest": "http://docs.oasis-open.org/wsn/bw-2/PausableSubscriptionManager/RenewRequest", 14 | } 15 | 16 | /************************* 17 | Action Type in Header 18 | *************************/ 19 | 20 | //Action type 21 | type Action struct { 22 | //XMLName xml.Name `xml:"wsse:Security"` 23 | XMLName xml.Name `xml:"wsa:Action"` 24 | Operation string `xml:",chardata"` 25 | } 26 | 27 | /* 28 | 29 | http://docs.oasis-open.org/wsn/bw-2/NotificationProducer/SubscribeRequest 30 | 31 | */ 32 | 33 | //NewAction get a new Action Section 34 | func NewAction(key, value string) Action { 35 | 36 | /** Generating Nonce sequence **/ 37 | auth := Action{ 38 | 39 | // Created: time.Now().UTC().Format(time.RFC3339Nano), 40 | } 41 | 42 | return auth 43 | } 44 | -------------------------------------------------------------------------------- /gosoap/ws-security.go: -------------------------------------------------------------------------------- 1 | package gosoap 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/base64" 6 | "encoding/xml" 7 | "time" 8 | 9 | "github.com/elgs/gostrgen" 10 | ) 11 | 12 | /************************* 13 | WS-Security types 14 | *************************/ 15 | const ( 16 | passwordType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest" 17 | encodingType = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" 18 | ) 19 | 20 | //Security type :XMLName xml.Name `xml:"http://purl.org/rss/1.0/modules/content/ encoded"` 21 | type Security struct { 22 | //XMLName xml.Name `xml:"wsse:Security"` 23 | XMLName xml.Name `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd Security"` 24 | Auth wsAuth 25 | } 26 | 27 | type password struct { 28 | //XMLName xml.Name `xml:"wsse:Password"` 29 | Type string `xml:"Type,attr"` 30 | Password string `xml:",chardata"` 31 | } 32 | 33 | type nonce struct { 34 | //XMLName xml.Name `xml:"wsse:Nonce"` 35 | Type string `xml:"EncodingType,attr"` 36 | Nonce string `xml:",chardata"` 37 | } 38 | 39 | type wsAuth struct { 40 | XMLName xml.Name `xml:"UsernameToken"` 41 | Username string `xml:"Username"` 42 | Password password `xml:"Password"` 43 | Nonce nonce `xml:"Nonce"` 44 | Created string `xml:"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd Created"` 45 | } 46 | 47 | /* 48 | 49 | 50 | admin 51 | edBuG+qVavQKLoWuGWQdPab4IBE= 52 | S7wO1ZFTh0KXv2CR7bd2ZXkLAAAAAA== 53 | 2018-04-10T18:04:25.836Z 54 | 55 | 56 | */ 57 | 58 | //NewSecurity get a new security 59 | func NewSecurity(username, passwd string) Security { 60 | /** Generating Nonce sequence **/ 61 | charsToGenerate := 32 62 | charSet := gostrgen.Lower | gostrgen.Digit 63 | 64 | nonceSeq, _ := gostrgen.RandGen(charsToGenerate, charSet, "", "") 65 | auth := Security{ 66 | Auth: wsAuth{ 67 | Username: username, 68 | Password: password{ 69 | Type: passwordType, 70 | Password: generateToken(username, nonceSeq, time.Now().UTC(), passwd), 71 | }, 72 | Nonce: nonce{ 73 | Type: encodingType, 74 | Nonce: nonceSeq, 75 | }, 76 | Created: time.Now().UTC().Format("2020-06-13T06:04:43+00:00"), 77 | }, 78 | } 79 | 80 | return auth 81 | } 82 | 83 | //Digest = B64ENCODE( SHA1( B64DECODE( Nonce ) + Date + Password ) ) 84 | func generateToken(Username string, Nonce string, Created time.Time, Password string) string { 85 | 86 | sDec, _ := base64.StdEncoding.DecodeString(Nonce) 87 | 88 | hasher := sha1.New() 89 | //hasher.Write([]byte((base64.StdEncoding.EncodeToString([]byte(Nonce)) + Created.Format(time.RFC3339) + Password))) 90 | hasher.Write([]byte(string(sDec) + Created.Format("2020-06-13T06:04:43+00:00") + Password)) 91 | 92 | return base64.StdEncoding.EncodeToString(hasher.Sum(nil)) 93 | } 94 | -------------------------------------------------------------------------------- /networking/networking.go: -------------------------------------------------------------------------------- 1 | package networking 2 | 3 | import ( 4 | "bytes" 5 | "net/http" 6 | "strings" 7 | "time" 8 | 9 | dac "github.com/xinsnake/go-http-digest-auth-client" 10 | ) 11 | 12 | // SendSoap send soap message 13 | func SendSoap(username, password, endpoint, message string) (*http.Response, error) { 14 | dr := dac.NewRequest(username, password, "POST", endpoint, message) 15 | resp, err := dr.Execute() 16 | if err != nil { 17 | return resp, err 18 | } 19 | return resp, nil 20 | } 21 | 22 | func SendSoapWithDigestAndHttpReq(username, password, endpoint, message string, timeout time.Duration) (*http.Response, error) { 23 | t := dac.NewTransport(username, password) 24 | t.HTTPClient = &http.Client{ 25 | Timeout: timeout, 26 | } 27 | req, err := http.NewRequest("POST", endpoint, strings.NewReader(message)) 28 | 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | resp, err := t.RoundTrip(req) 34 | if err != nil { 35 | return resp, err 36 | } 37 | return resp, nil 38 | } 39 | 40 | // SendSoapWithTimeout send soap message with timeOut 41 | func SendSoapWithTimeout(endpoint string, message []byte, timeout time.Duration) (*http.Response, error) { 42 | httpClient := &http.Client{ 43 | Timeout: timeout, 44 | } 45 | 46 | return httpClient.Post(endpoint, "application/soap+xml; charset=utf-8", bytes.NewReader(message)) 47 | } 48 | -------------------------------------------------------------------------------- /ptz/types.go: -------------------------------------------------------------------------------- 1 | package ptz 2 | 3 | import ( 4 | "github.com/fanap-infra/onvif/xsd" 5 | "github.com/fanap-infra/onvif/xsd/onvif" 6 | ) 7 | 8 | type Capabilities struct { 9 | EFlip xsd.Boolean `xml:"EFlip,attr"` 10 | Reverse xsd.Boolean `xml:"Reverse,attr"` 11 | GetCompatibleConfigurations xsd.Boolean `xml:"GetCompatibleConfigurations,attr"` 12 | MoveStatus xsd.Boolean `xml:"MoveStatus,attr"` 13 | StatusPosition xsd.Boolean `xml:"StatusPosition,attr"` 14 | } 15 | 16 | //PTZ main types 17 | 18 | type GetServiceCapabilities struct { 19 | XMLName string `xml:"tptz:GetServiceCapabilities"` 20 | } 21 | 22 | type GetServiceCapabilitiesResponse struct { 23 | Capabilities Capabilities 24 | } 25 | 26 | type GetNodes struct { 27 | XMLName string `xml:"tptz:GetNodes"` 28 | } 29 | 30 | type GetNodesResponse struct { 31 | PTZNode onvif.PTZNode 32 | } 33 | 34 | type GetNode struct { 35 | XMLName string `xml:"tptz:GetNode"` 36 | NodeToken onvif.ReferenceToken `xml:"tptz:NodeToken"` 37 | } 38 | 39 | type GetNodeResponse struct { 40 | PTZNode onvif.PTZNode 41 | } 42 | 43 | type GetConfiguration struct { 44 | XMLName string `xml:"tptz:GetConfiguration"` 45 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 46 | } 47 | 48 | type GetConfigurationResponse struct { 49 | PTZConfiguration onvif.PTZConfiguration 50 | } 51 | 52 | type GetConfigurations struct { 53 | XMLName string `xml:"tptz:GetConfigurations"` 54 | } 55 | 56 | type GetConfigurationsResponse struct { 57 | PTZConfiguration onvif.PTZConfiguration 58 | } 59 | 60 | type SetConfiguration struct { 61 | XMLName string `xml:"tptz:SetConfiguration"` 62 | PTZConfiguration onvif.PTZConfiguration `xml:"tptz:PTZConfiguration"` 63 | ForcePersistence xsd.Boolean `xml:"tptz:ForcePersistence"` 64 | } 65 | 66 | type SetConfigurationResponse struct { 67 | } 68 | 69 | type GetConfigurationOptions struct { 70 | XMLName string `xml:"tptz:GetConfigurationOptions"` 71 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 72 | } 73 | 74 | type GetConfigurationOptionsResponse struct { 75 | PTZConfigurationOptions onvif.PTZConfigurationOptions 76 | } 77 | 78 | type SendAuxiliaryCommand struct { 79 | XMLName string `xml:"tptz:SendAuxiliaryCommand"` 80 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 81 | AuxiliaryData onvif.AuxiliaryData `xml:"tptz:AuxiliaryData"` 82 | } 83 | 84 | type SendAuxiliaryCommandResponse struct { 85 | AuxiliaryResponse onvif.AuxiliaryData 86 | } 87 | 88 | type GetPresets struct { 89 | XMLName string `xml:"tptz:GetPresets"` 90 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 91 | } 92 | 93 | type GetPresetsResponse struct { 94 | Preset onvif.PTZPreset 95 | } 96 | 97 | type SetPreset struct { 98 | XMLName string `xml:"tptz:SetPreset"` 99 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 100 | PresetName xsd.String `xml:"tptz:PresetName"` 101 | PresetToken onvif.ReferenceToken `xml:"tptz:PresetToken"` 102 | } 103 | 104 | type SetPresetResponse struct { 105 | PresetToken onvif.ReferenceToken 106 | } 107 | 108 | type RemovePreset struct { 109 | XMLName string `xml:"tptz:RemovePreset"` 110 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 111 | PresetToken onvif.ReferenceToken `xml:"tptz:PresetToken"` 112 | } 113 | 114 | type RemovePresetResponse struct { 115 | } 116 | 117 | type GotoPreset struct { 118 | XMLName string `xml:"tptz:GotoPreset"` 119 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 120 | PresetToken onvif.ReferenceToken `xml:"tptz:PresetToken"` 121 | Speed onvif.PTZSpeed `xml:"tptz:Speed"` 122 | } 123 | 124 | type GotoPresetResponse struct { 125 | } 126 | 127 | type GotoHomePosition struct { 128 | XMLName string `xml:"tptz:GotoHomePosition"` 129 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 130 | Speed onvif.PTZSpeed `xml:"tptz:Speed"` 131 | } 132 | 133 | type GotoHomePositionResponse struct { 134 | } 135 | 136 | type SetHomePosition struct { 137 | XMLName string `xml:"tptz:SetHomePosition"` 138 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 139 | } 140 | 141 | type SetHomePositionResponse struct { 142 | } 143 | 144 | type ContinuousMove struct { 145 | XMLName string `xml:"tptz:ContinuousMove"` 146 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 147 | Velocity onvif.PTZSpeed `xml:"tptz:Velocity"` 148 | Timeout xsd.Duration `xml:"tptz:Timeout"` 149 | } 150 | 151 | type ContinuousMoveResponse struct { 152 | } 153 | 154 | type RelativeMove struct { 155 | XMLName string `xml:"tptz:RelativeMove"` 156 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 157 | Translation onvif.PTZVector `xml:"tptz:Translation"` 158 | Speed onvif.PTZSpeed `xml:"tptz:Speed"` 159 | } 160 | 161 | type RelativeMoveResponse struct { 162 | } 163 | 164 | type GetStatus struct { 165 | XMLName string `xml:"tptz:GetStatus"` 166 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 167 | } 168 | 169 | type GetStatusResponse struct { 170 | PTZStatus onvif.PTZStatus 171 | } 172 | 173 | type AbsoluteMove struct { 174 | XMLName string `xml:"tptz:AbsoluteMove"` 175 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 176 | Position onvif.PTZVector `xml:"tptz:Position"` 177 | Speed onvif.PTZSpeed `xml:"tptz:Speed"` 178 | } 179 | 180 | type AbsoluteMoveResponse struct { 181 | } 182 | 183 | type GeoMove struct { 184 | XMLName string `xml:"tptz:GeoMove"` 185 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 186 | Target onvif.GeoLocation `xml:"tptz:Target"` 187 | Speed onvif.PTZSpeed `xml:"tptz:Speed"` 188 | AreaHeight xsd.Float `xml:"tptz:AreaHeight"` 189 | AreaWidth xsd.Float `xml:"tptz:AreaWidth"` 190 | } 191 | 192 | type GeoMoveResponse struct { 193 | } 194 | 195 | type Stop struct { 196 | XMLName string `xml:"tptz:Stop"` 197 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 198 | PanTilt xsd.Boolean `xml:"tptz:PanTilt"` 199 | Zoom xsd.Boolean `xml:"tptz:Zoom"` 200 | } 201 | 202 | type StopResponse struct { 203 | } 204 | 205 | type GetPresetTours struct { 206 | XMLName string `xml:"tptz:GetPresetTours"` 207 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 208 | } 209 | 210 | type GetPresetToursResponse struct { 211 | PresetTour onvif.PresetTour 212 | } 213 | 214 | type GetPresetTour struct { 215 | XMLName string `xml:"tptz:GetPresetTour"` 216 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 217 | PresetTourToken onvif.ReferenceToken `xml:"tptz:PresetTourToken"` 218 | } 219 | 220 | type GetPresetTourResponse struct { 221 | PresetTour onvif.PresetTour 222 | } 223 | 224 | type GetPresetTourOptions struct { 225 | XMLName string `xml:"tptz:GetPresetTourOptions"` 226 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 227 | PresetTourToken onvif.ReferenceToken `xml:"tptz:PresetTourToken"` 228 | } 229 | 230 | type GetPresetTourOptionsResponse struct { 231 | Options onvif.PTZPresetTourOptions 232 | } 233 | 234 | type CreatePresetTour struct { 235 | XMLName string `xml:"tptz:CreatePresetTour"` 236 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 237 | } 238 | 239 | type CreatePresetTourResponse struct { 240 | PresetTourToken onvif.ReferenceToken 241 | } 242 | 243 | type ModifyPresetTour struct { 244 | XMLName string `xml:"tptz:ModifyPresetTour"` 245 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 246 | PresetTour onvif.PresetTour `xml:"tptz:PresetTour"` 247 | } 248 | 249 | type ModifyPresetTourResponse struct { 250 | } 251 | 252 | type OperatePresetTour struct { 253 | XMLName string `xml:"tptz:OperatePresetTour"` 254 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 255 | PresetTourToken onvif.ReferenceToken `xml:"onvif:PresetTourToken"` 256 | Operation onvif.PTZPresetTourOperation `xml:"onvif:Operation"` 257 | } 258 | 259 | type OperatePresetTourResponse struct { 260 | } 261 | 262 | type RemovePresetTour struct { 263 | XMLName string `xml:"tptz:RemovePresetTour"` 264 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 265 | PresetTourToken onvif.ReferenceToken `xml:"tptz:PresetTourToken"` 266 | } 267 | 268 | type RemovePresetTourResponse struct { 269 | } 270 | 271 | type GetCompatibleConfigurations struct { 272 | XMLName string `xml:"tptz:GetCompatibleConfigurations"` 273 | ProfileToken onvif.ReferenceToken `xml:"tptz:ProfileToken"` 274 | } 275 | 276 | type GetCompatibleConfigurationsResponse struct { 277 | PTZConfiguration onvif.PTZConfiguration 278 | } 279 | -------------------------------------------------------------------------------- /ws-discovery/networking.go: -------------------------------------------------------------------------------- 1 | package wsdiscovery 2 | 3 | /******************************************************* 4 | * Copyright (C) 2018 Palanjyan Zhorzhik 5 | * 6 | * This file is part of ws-discovery project. 7 | * 8 | * ws-discovery can be copied and/or distributed without the express 9 | * permission of Palanjyan Zhorzhik 10 | *******************************************************/ 11 | 12 | import ( 13 | "context" 14 | "fmt" 15 | "log" 16 | "net" 17 | "strings" 18 | "sync/atomic" 19 | "syscall" 20 | "time" 21 | 22 | "golang.org/x/sys/unix" 23 | 24 | "github.com/beevik/etree" 25 | "github.com/gofrs/uuid" 26 | "golang.org/x/net/ipv4" 27 | ) 28 | 29 | const bufSize = 8192 30 | 31 | var ttl = uint64(2) 32 | 33 | //CheckConnection ... 34 | func CheckConnection(ip, port string) (bool, error) { 35 | servAddr := ip + ":" + port 36 | 37 | d := net.Dialer{Timeout: 2 * time.Second} 38 | conn, err := d.Dial("tcp", servAddr) 39 | if err != nil { 40 | return false, err 41 | } 42 | defer conn.Close() 43 | 44 | return true, nil 45 | } 46 | 47 | //SendProbeToSpecificDevice ... 48 | func SendProbeToSpecificDevice(ip string, scopes, types []string, namespaces map[string]string) string { 49 | uuidV4 := uuid.Must(uuid.NewV4()) 50 | probeSOAP := buildProbeMessage(uuidV4.String(), scopes, types, namespaces) 51 | response := writeUDP(ip, 3702, probeSOAP.String()) 52 | return response 53 | } 54 | 55 | func writeUDP(ip string, port int, data string) string { 56 | 57 | address := net.UDPAddr{Port: port, IP: net.ParseIP(ip)} 58 | 59 | fmt.Printf("Client to contact server at %v\n", address) 60 | 61 | conn, err := net.DialUDP("udp", nil, &address) 62 | 63 | if err != nil { 64 | fmt.Printf("can not dial UDP, address: %s, error: %s\n", address.IP, err.Error()) 65 | return "" 66 | } 67 | 68 | fmt.Printf("Connected: %T, %v\n", conn, conn) 69 | 70 | fmt.Printf("Local address: %v\n", conn.LocalAddr()) 71 | fmt.Printf("Remote address: %v\n", conn.RemoteAddr()) 72 | 73 | b := []byte(data) 74 | readBytes := make([]byte, 4000) 75 | 76 | _, wrerr := conn.Write(b) 77 | 78 | if wrerr != nil { 79 | fmt.Printf("conn.Write(), address: %s, error: %s\n", address.IP, wrerr.Error()) 80 | return "" 81 | } 82 | 83 | conn.SetReadDeadline(time.Now().Add(1 * time.Second)) 84 | cc, rderr := conn.Read(readBytes) 85 | if rderr != nil { 86 | fmt.Printf("conn.Read(), address: %s, error: %s\n", address.IP, rderr.Error()) 87 | return "" 88 | } 89 | 90 | if err = conn.Close(); err != nil { 91 | log.Printf("can not close socket connection, address: %s, error %s", address.IP, err.Error()) 92 | } 93 | 94 | if cc < 1 { 95 | log.Printf("can not read data from socket, address: %s, error %s", address.IP, err.Error()) 96 | return "" 97 | } 98 | 99 | doc := etree.NewDocument() 100 | if err := doc.ReadFromString(string(readBytes[0:cc])); err != nil { 101 | log.Printf("can not parse byte array, address: %s, error %s", address.IP, err.Error()) 102 | return "" 103 | } 104 | 105 | uuid := doc.Root().FindElements("./Body/ProbeMatches/ProbeMatch/EndpointReference/Address") 106 | uuidStr := strings.Split(uuid[0].Text(), ":")[2] 107 | 108 | return uuidStr 109 | } 110 | 111 | //SendProbe to device 112 | func SendProbe(interfaceName string, scopes, types []string, namespaces map[string]string) []string { 113 | // Creating UUID Version 4 114 | uuidV4 := uuid.Must(uuid.NewV4()) 115 | // fmt.Printf("UUIDv4: %s\n", uuidV4) 116 | 117 | probeSOAP := buildProbeMessage(uuidV4.String(), scopes, types, namespaces) 118 | //probeSOAP = ` 119 | // 120 | //
121 | //http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe 122 | //uuid:78a2ed98-bc1f-4b08-9668-094fcba81e35 123 | //http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous 124 | //urn:schemas-xmlsoap-org:ws:2005:04:discovery 125 | //
126 | // 127 | //dp0:NetworkVideoTransmitter 128 | // 129 | // 130 | //
` 131 | 132 | return sendUDPMulticast(probeSOAP.String(), interfaceName) 133 | 134 | } 135 | 136 | func reusePort(network, address string, conn syscall.RawConn) error { 137 | return conn.Control(func(descriptor uintptr) { 138 | err := unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1) 139 | if err != nil { 140 | return 141 | } 142 | 143 | err = unix.SetsockoptInt(int(descriptor), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1) 144 | if err != nil { 145 | return 146 | } 147 | }) 148 | } 149 | 150 | func sendUDPMulticast(msg string, interfaceName string) []string { 151 | var result []string 152 | data := []byte(msg) 153 | iface, err := net.InterfaceByName(interfaceName) 154 | if err != nil { 155 | fmt.Println(err) 156 | } 157 | group := net.IPv4(239, 255, 255, 250) 158 | config := &net.ListenConfig{Control: reusePort} 159 | const network = "udp4" 160 | const address = "0.0.0.0:1024" 161 | c, err := config.ListenPacket(context.Background(), network, address) 162 | if err != nil { 163 | fmt.Println("can not ListenPacket, ", err) 164 | } 165 | defer c.Close() 166 | 167 | p := ipv4.NewPacketConn(c) 168 | if err := p.JoinGroup(iface, &net.UDPAddr{IP: group}); err != nil { 169 | fmt.Println("can not JoinGroup, ", err) 170 | } 171 | 172 | dst := &net.UDPAddr{IP: group, Port: 3702} 173 | for _, ifi := range []*net.Interface{iface} { 174 | if err := p.SetMulticastInterface(ifi); err != nil { 175 | fmt.Println("can not SetMulticastInterface, ", err) 176 | } 177 | err = p.SetMulticastTTL(int(ttl)) 178 | if err != nil { 179 | fmt.Println("can not SetMulticastTTL, ", err) 180 | } 181 | if _, err := p.WriteTo(data, nil, dst); err != nil { 182 | fmt.Println("can not WriteTo, ", err) 183 | } 184 | } 185 | 186 | if err := p.SetReadDeadline(time.Now().Add(time.Second * 2)); err != nil { 187 | fmt.Println("can not set read dead line, ", err) 188 | return result 189 | } 190 | 191 | for { 192 | b := make([]byte, bufSize) 193 | n, _, _, err := p.ReadFrom(b) 194 | if err != nil { 195 | break 196 | } 197 | result = append(result, string(b[0:n])) 198 | } 199 | return result 200 | } 201 | 202 | func SetPacketTTL(packetTTL uint) { 203 | atomic.StoreUint64(&ttl, uint64(packetTTL)) 204 | } 205 | -------------------------------------------------------------------------------- /ws-discovery/ws-discovery.go: -------------------------------------------------------------------------------- 1 | package wsdiscovery 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/beevik/etree" 7 | "github.com/fanap-infra/onvif/gosoap" 8 | ) 9 | 10 | func buildProbeMessage(uuidV4 string, scopes, types []string, nmsp map[string]string) gosoap.SoapMessage { 11 | //Список namespace 12 | namespaces := make(map[string]string) 13 | namespaces["a"] = "http://schemas.xmlsoap.org/ws/2004/08/addressing" 14 | //namespaces["d"] = "http://schemas.xmlsoap.org/ws/2005/04/discovery" 15 | 16 | probeMessage := gosoap.NewEmptySOAP() 17 | 18 | probeMessage.AddRootNamespaces(namespaces) 19 | //if len(nmsp) != 0 { 20 | // probeMessage.AddRootNamespaces(nmsp) 21 | //} 22 | 23 | //fmt.Println(probeMessage.String()) 24 | 25 | //Содержимое Head 26 | var headerContent []*etree.Element 27 | 28 | action := etree.NewElement("a:Action") 29 | action.SetText("http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe") 30 | action.CreateAttr("mustUnderstand", "1") 31 | 32 | msgID := etree.NewElement("a:MessageID") 33 | msgID.SetText("uuid:" + uuidV4) 34 | 35 | replyTo := etree.NewElement("a:ReplyTo") 36 | replyTo.CreateElement("a:Address").SetText("http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous") 37 | 38 | to := etree.NewElement("a:To") 39 | to.SetText("urn:schemas-xmlsoap-org:ws:2005:04:discovery") 40 | to.CreateAttr("mustUnderstand", "1") 41 | 42 | headerContent = append(headerContent, action, msgID, replyTo, to) 43 | probeMessage.AddHeaderContents(headerContent) 44 | 45 | //Содержимое Body 46 | probe := etree.NewElement("Probe") 47 | probe.CreateAttr("xmlns", "http://schemas.xmlsoap.org/ws/2005/04/discovery") 48 | 49 | if len(types) != 0 { 50 | typesTag := etree.NewElement("d:Types") 51 | if len(nmsp) != 0 { 52 | for key, value := range nmsp { 53 | typesTag.CreateAttr("xmlns:"+key, value) 54 | } 55 | } 56 | typesTag.CreateAttr("xmlns:d", "http://schemas.xmlsoap.org/ws/2005/04/discovery") 57 | //typesTag.CreateAttr("xmlns:dp0", "http://www.onvif.org/ver10/network/wsdl") 58 | var typesString string 59 | for _, j := range types { 60 | typesString += j 61 | typesString += " " 62 | } 63 | 64 | typesTag.SetText(strings.TrimSpace(typesString)) 65 | 66 | probe.AddChild(typesTag) 67 | } 68 | 69 | if len(scopes) != 0 { 70 | scopesTag := etree.NewElement("d:Scopes") 71 | var scopesString string 72 | for _, j := range scopes { 73 | scopesString += j 74 | scopesString += " " 75 | } 76 | scopesTag.SetText(strings.TrimSpace(scopesString)) 77 | 78 | probe.AddChild(scopesTag) 79 | } 80 | 81 | probeMessage.AddBodyContent(probe) 82 | 83 | return probeMessage 84 | } 85 | -------------------------------------------------------------------------------- /xsd/built_in.go: -------------------------------------------------------------------------------- 1 | package xsd 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/hex" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "net/url" 10 | "regexp" 11 | "strings" 12 | "time" 13 | 14 | "github.com/fanap-infra/onvif/xsd/iso8601" 15 | ) 16 | 17 | /* 18 | TODO: XML SOURCE: https://www.w3.org/2001/05/datatypes.xsd 19 | */ 20 | 21 | // AnyType alias for string 22 | type AnyType string 23 | 24 | // AnySimpleType alias for string 25 | type AnySimpleType string 26 | 27 | /*********************************************************** 28 | Non-derived types 29 | ***********************************************************/ 30 | 31 | /* 32 | The string datatype represents character strings in XML. 33 | The ·value space· of string is the set of finite-length sequences of characters. 34 | String has the following constraining facets: 35 | • length 36 | • minLength 37 | • maxLength 38 | • pattern 39 | • enumeration 40 | • whiteSpace 41 | 42 | More info: https://www.w3.org/TR/xmlschema-2/#string 43 | 44 | //TODO: valid/invalid character declaration and process restrictions 45 | */ 46 | type String string 47 | 48 | /* 49 | Construct an instance of xsd String type 50 | */ 51 | func (tp String) NewString(data string) String { 52 | return String(data) 53 | } 54 | 55 | /* 56 | Boolean has the ·value space· required to support the mathematical concept of binary-valued logic: {true, false}. 57 | Boolean has the following ·constraining facets·: 58 | • pattern 59 | • whiteSpace 60 | 61 | More info: https://www.w3.org/TR/xmlschema-2/#boolean 62 | 63 | //TODO: process restrictions 64 | */ 65 | type Boolean bool 66 | 67 | /* 68 | Construct an instance of xsd Boolean type 69 | */ 70 | func (tp Boolean) NewBool(data bool) Boolean { 71 | return Boolean(data) 72 | } 73 | 74 | /* 75 | Float is patterned after the IEEE single-precision 32-bit floating point type 76 | Float has the following ·constraining facets·: 77 | • pattern 78 | • enumeration 79 | • whiteSpace 80 | • maxInclusive 81 | • maxExclusive 82 | • minInclusive 83 | • minExclusive 84 | 85 | More info: https://www.w3.org/TR/xmlschema-2/#float 86 | 87 | //TODO: process restrictions 88 | */ 89 | type Float float32 90 | 91 | /* 92 | Construct an instance of xsd Float type 93 | */ 94 | func (tp Float) NewFloat(data float32) Float { 95 | return Float(data) 96 | } 97 | 98 | /* 99 | The double datatype is patterned after the IEEE double-precision 64-bit floating point type 100 | Double has the following ·constraining facets·: 101 | • pattern 102 | • enumeration 103 | • whiteSpace 104 | • maxInclusive 105 | • maxExclusive 106 | • minInclusive 107 | • minExclusive 108 | 109 | More info: https://www.w3.org/TR/xmlschema-2/#double 110 | 111 | //TODO: process restrictions 112 | */ 113 | type Double float64 114 | 115 | /* 116 | Construct an instance of xsd Double type 117 | */ 118 | func (tp Double) NewDouble(data float64) Double { 119 | return Double(data) 120 | } 121 | 122 | /* 123 | The type decimal represents a decimal number of arbitrary precision. 124 | Schema processors vary in the number of significant digits they support, 125 | but a conforming processor must support a minimum of 18 significant digits. 126 | The format of xsd:decimal is a sequence of digits optionally preceded by a sign ("+" or "-") 127 | and optionally containing a period. The value may start or end with a period. 128 | If the fractional part is 0 then the period and trailing zeros may be omitted. 129 | Leading and trailing zeros are permitted, but they are not considered significant. 130 | That is, the decimal values 3.0 and 3.0000 are considered equal. 131 | 132 | Source: http://www.datypic.com/sc/xsd/t-xsd_decimal.html 133 | 134 | Decimal has the following ·constraining facets·: 135 | • totalDigits 136 | • fractionDigits 137 | • pattern 138 | • whiteSpace 139 | • enumeration 140 | • maxInclusive 141 | • maxExclusive 142 | • minInclusive 143 | • minExclusive 144 | 145 | More info: https://www.w3.org/TR/xmlschema-2/#decimal 146 | 147 | //TODO: process restrictions, valid/invalid characters(commas are not permitted; the decimal separator must be a period) 148 | 149 | */ 150 | type Decimal string 151 | 152 | /* 153 | Construct an instance of xsd Decimal type 154 | */ 155 | func (tp Decimal) NewDecimal(data string) Decimal { 156 | return Decimal(data) 157 | } 158 | 159 | /* 160 | Duration is the [ISO 8601] extended format PnYn MnDTnH nMnS, 161 | where nY represents the number of years, nM the number of months, 162 | nD the number of days, 'T' is the date/time separator, 163 | nH the number of hours, nM the number of minutes and nS the number of seconds. 164 | The number of seconds can include decimal digits to arbitrary precision. 165 | PnYnMnDTnHnMnS 166 | 167 | Duration has the following ·constraining facets·: 168 | 169 | • pattern 170 | • enumeration 171 | • whiteSpace 172 | • maxInclusive 173 | • maxExclusive 174 | • minInclusive 175 | • minExclusive 176 | 177 | More info: https://www.w3.org/TR/xmlschema-2/#duration 178 | 179 | TODO: process restrictions 180 | TODO: Look at time.Duration go type 181 | */ 182 | 183 | //Duration alias for AnySimpleType 184 | type Duration AnySimpleType 185 | 186 | /* 187 | Construct an instance of xsd duration type 188 | */ 189 | func (tp Duration) NewDateTime(years, months, days, hours, minutes, seconds string) Duration { 190 | i, err := iso8601.NewDuration( 191 | years, 192 | months, 193 | days, 194 | hours, 195 | minutes, 196 | seconds, 197 | ) 198 | 199 | if err != nil { 200 | log.Fatalln(err) 201 | } 202 | 203 | //fmt.Println(i.ISO8601Duration()) 204 | 205 | return Duration(i.ISO8601Duration()) 206 | } 207 | 208 | /* 209 | DateTime values may be viewed as objects with integer-valued year, month, day, hour 210 | and minute properties, a decimal-valued second property, and a boolean timezoned property. 211 | 212 | 213 | The ·lexical space· of dateTime consists of finite-length sequences of characters of the form: 214 | '-'? yyyy '-' mm '-' dd 'T' hh ':' mm ':' ss ('.' s+)? (zzzzzz)?, 215 | 216 | 217 | DateTime has the following ·constraining facets·: 218 | 219 | • pattern 220 | • enumeration 221 | • whiteSpace 222 | • maxInclusive 223 | • maxExclusive 224 | • minInclusive 225 | • minExclusive 226 | 227 | More info: https://www.w3.org/TR/xmlschema-2/#dateTime 228 | 229 | TODO: decide good type for time with proper format 230 | TODO: process restrictions 231 | */ 232 | type DateTime AnySimpleType 233 | 234 | /* 235 | Construct an instance of xsd dateTime type 236 | */ 237 | func (tp DateTime) NewDateTime(time time.Time) DateTime { 238 | return DateTime(time.Format("2002-10-10T12:00:00-05:00")) 239 | } 240 | 241 | /* 242 | Time represents an instant of time that recurs every day. 243 | The ·value space· of time is the space of time of day values 244 | as defined in § 5.3 of [ISO 8601]. Specifically, it is a set 245 | of zero-duration daily time instances. 246 | 247 | Time has the following ·constraining facets·: 248 | 249 | • pattern 250 | • enumeration 251 | • whiteSpace 252 | • maxInclusive 253 | • maxExclusive 254 | • minInclusive 255 | • minExclusive 256 | 257 | More info: https://www.w3.org/TR/xmlschema-2/#time 258 | 259 | TODO: process restrictions 260 | */ 261 | type Time AnySimpleType 262 | 263 | /* 264 | Construct an instance of xsd time type 265 | */ 266 | func (tp DateTime) NewTime(time time.Time) DateTime { 267 | return DateTime(time.Format("15:04:05")) 268 | } 269 | 270 | /* 271 | The ·value space· of date consists of top-open intervals of 272 | exactly one day in length on the timelines of dateTime, beginning 273 | on the beginning moment of each day (in each timezone), 274 | i.e. '00:00:00', up to but not including '24:00:00' 275 | (which is identical with '00:00:00' of the next day). 276 | For nontimezoned values, the top-open intervals disjointly 277 | cover the nontimezoned timeline, one per day. For timezoned 278 | values, the intervals begin at every minute and therefore overlap. 279 | */ 280 | type Date AnySimpleType 281 | 282 | /* 283 | Construct an instance of xsd date type 284 | */ 285 | func (tp Date) NewDate(time time.Time) Date { 286 | return Date(time.Format("2004-04-12-05:00")) 287 | } 288 | 289 | /* 290 | The type xsd:gYearMonth represents a specific month of a specific 291 | year. The letter g signifies "Gregorian." The format of 292 | xsd:gYearMonth is CCYY-MM. No left truncation is allowed on 293 | either part. To represents years later than 9999, additional 294 | digits can be added to the left of the year value. 295 | To represent years before 0001, a preceding minus sign ("-") 296 | is permitted. 297 | 298 | Source: http://www.datypic.com/sc/xsd/t-xsd_gYearMonth.html 299 | 300 | More info: https://www.w3.org/TR/xmlschema-2/#gYearMonth 301 | */ 302 | type GYearMonth AnySimpleType 303 | 304 | /* 305 | Construct an instance of xsd GYearMonth type 306 | */ 307 | func (tp GYearMonth) NewGYearMonth(time time.Time) GYearMonth { 308 | return GYearMonth(fmt.Sprintf("", time.Year(), "-", time.Month())) 309 | //return GYearMonth(time.Format("2004-04-05:00")) 310 | } 311 | 312 | /* 313 | The type xsd:gYear represents a specific calendar year. 314 | The letter g signifies "Gregorian." The format of xsd:gYear 315 | is CCYY. No left truncation is allowed. To represent years 316 | later than 9999, additional digits can be added to the left 317 | of the year value. To represent years before 0001, a preceding 318 | minus sign ("-") is allowed. 319 | 320 | Source: http://www.datypic.com/sc/xsd/t-xsd_gYear.html 321 | 322 | More info: https://www.w3.org/TR/xmlschema-2/#gYear 323 | */ 324 | type GYear AnySimpleType 325 | 326 | /* 327 | Construct an instance of xsd GYear type 328 | */ 329 | func (tp GYear) NewGYear(time time.Time) GYear { 330 | return GYear(fmt.Sprintf("", time.Year())) 331 | //return GYearMonth(time.Format("2004-04-05:00")) 332 | } 333 | 334 | /* 335 | The type xsd:gMonthDay represents a specific day that recurs 336 | every year. The letter g signifies "Gregorian." xsd:gMonthDay 337 | can be used to say, for example, that your birthday is on the 338 | 14th of April every year. The format of xsd:gMonthDay is --MM-DD. 339 | 340 | Source: http://www.datypic.com/sc/xsd/t-xsd_gMonthDay.html 341 | 342 | More info: https://www.w3.org/TR/xmlschema-2/#gMonthDay 343 | */ 344 | type GMonthDay AnySimpleType 345 | 346 | /* 347 | Construct an instance of xsd GMonthDay type 348 | */ 349 | func (tp GMonthDay) NewGMonthDay(time time.Time) GMonthDay { 350 | return GMonthDay(fmt.Sprintf("--", time.Month(), "-", time.Day())) 351 | } 352 | 353 | /* 354 | The type xsd:gDay represents a day that recurs every month. 355 | The letter g signifies "Gregorian." xsd:gDay can be used to say, 356 | for example, that checks are paid on the 5th of each month. 357 | To represent a duration of days, use the duration type instead. 358 | The format of gDay is ---DD. 359 | 360 | Source: http://www.datypic.com/sc/xsd/t-xsd_gDay.html 361 | 362 | More info: https://www.w3.org/TR/xmlschema-2/#gDay 363 | */ 364 | type GDay AnySimpleType 365 | 366 | /* 367 | Construct an instance of xsd GDay type 368 | */ 369 | func (tp GDay) NewGDay(time time.Time) GDay { 370 | return GDay(fmt.Sprintf("---", time.Day())) 371 | } 372 | 373 | /* 374 | The type xsd:gMonth represents a specific month that recurs 375 | every year. The letter g signifies "Gregorian." xsd:gMonth 376 | can be used to indicate, for example, that fiscal year-end 377 | processing occurs in September of every year. To represent 378 | a duration of months, use the duration type instead. The format 379 | of xsd:gMonth is --MM. 380 | 381 | Source: http://www.datypic.com/sc/xsd/t-xsd_gMonth.html 382 | 383 | More info: https://www.w3.org/TR/xmlschema-2/#gMonth 384 | */ 385 | type GMonth AnySimpleType 386 | 387 | func (tp GMonth) NewGMonth(time time.Time) GMonth { 388 | return GMonth(fmt.Sprintf("--", time.Month())) 389 | } 390 | 391 | /* 392 | The xsd:hexBinary type represents binary data as a sequence 393 | of binary octets. It uses hexadecimal encoding, where each 394 | binary octet is a two-character hexadecimal number. 395 | Lowercase and uppercase letters A through F are permitted. 396 | For example, 0FB8 and 0fb8 are two equal xsd:hexBinary 397 | representations consisting of two octets. 398 | 399 | Source: http://www.datypic.com/sc/xsd/t-xsd_hexBinary.html 400 | 401 | More info: https://www.w3.org/TR/xmlschema-2/#hexBinary 402 | */ 403 | type HexBinary AnySimpleType 404 | 405 | func (tp HexBinary) NewHexBinary(data []byte) HexBinary { 406 | return HexBinary(hex.EncodeToString(data)) 407 | } 408 | 409 | /* 410 | base64Binary represents Base64-encoded arbitrary binary data. 411 | The ·value space· of base64Binary is the set of finite-length sequences of binary octets. 412 | For base64Binary data the entire binary stream is encoded using the Base64 Alphabet in [RFC 2045]. 413 | 414 | base64Binary has the following ·constraining facets·: 415 | • length 416 | • minLength 417 | • maxLength 418 | • pattern 419 | • enumeration 420 | • whiteSpace 421 | 422 | More info: https://www.w3.org/TR/xmlschema-2/#base64Binary 423 | */ 424 | type Base64Binary AnySimpleType 425 | 426 | func (tp Base64Binary) NewBase64Binary(data []byte) Base64Binary { 427 | return Base64Binary(base64.StdEncoding.EncodeToString(data)) 428 | } 429 | 430 | /* 431 | anyURI represents a Uniform Resource Identifier Reference (URI). 432 | An anyURI value can be absolute or relative, and may have an optional 433 | fragment identifier (i.e., it may be a URI Reference). 434 | This type should be used to specify the intention that the 435 | value fulfills the role of a URI as defined by [RFC 2396], as amended by [RFC 2732]. 436 | 437 | anyURI has the following ·constraining facets·: 438 | • length 439 | • minLength 440 | • maxLength 441 | • pattern 442 | • enumeration 443 | • whiteSpace 444 | 445 | More info: https://www.w3.org/TR/xmlschema-2/#anyURI 446 | */ 447 | type AnyURI AnySimpleType 448 | 449 | func (tp AnyURI) NewAnyURI(data url.URL) AnyURI { 450 | return AnyURI(data.String()) 451 | } 452 | 453 | /* 454 | QName represents XML qualified names. The ·value space· of QName is the set of tuples 455 | {namespace name, local part}, where namespace name is an anyURI and local part is an NCName. 456 | The ·lexical space· of QName is the set of strings that ·match· the QName production of [Namespaces in XML]. 457 | 458 | QName has the following ·constraining facets·: 459 | • length 460 | • minLength 461 | • maxLength 462 | • pattern 463 | • enumeration 464 | • whiteSpace 465 | 466 | More info: https://www.w3.org/TR/xmlschema-2/#QName 467 | */ 468 | type QName AnySimpleType 469 | 470 | func (tp QName) NewQName(prefix, local string) QName { 471 | var result string 472 | if len(prefix) == 0 { 473 | result += local 474 | } else { 475 | result = prefix + ":" + local 476 | } 477 | return QName(result) 478 | } 479 | 480 | //TODO: NOTATION datatype 481 | //type NOTATION AnySimpleType 482 | 483 | /* 484 | Derived types 485 | */ 486 | 487 | type NormalizedString String 488 | 489 | //TODO: check normalization 490 | func (tp NormalizedString) NewNormalizedString(data string) (NormalizedString, error) { 491 | if strings.ContainsAny(data, "\r\n\t<>&") { 492 | return NormalizedString(""), errors.New("String " + data + " contains forbidden symbols") 493 | } 494 | return NormalizedString(data), nil 495 | } 496 | 497 | type Token NormalizedString 498 | 499 | func (tp Token) NewToken(data NormalizedString) (Token, error) { 500 | trailing_leading_whitespaces := regexp.MustCompile(`^[\s\p{Zs}]+|[\s\p{Zs}]+$`) 501 | multiple_whitespaces := regexp.MustCompile(`[\s\p{Zs}]{2,}`) 502 | //Removing trailing and leading whitespaces and multiple spaces 503 | /*final := re_leadclose_whtsp.ReplaceAllString(data, "") 504 | final = re_inside_whtsp.ReplaceAllString(final, " ")*/ 505 | if strings.ContainsAny(string(data), "\r\n\t<>&") || trailing_leading_whitespaces.MatchString(string(data)) || multiple_whitespaces.MatchString(string(data)) { 506 | return Token(""), errors.New("String " + string(data) + " contains forbidden symbols or whitespaces") 507 | } 508 | 509 | return Token(data), nil 510 | } 511 | 512 | type Language Token 513 | 514 | func (tp Language) NewLanguage(data Token) (Language, error) { 515 | //Pattern was given from https://www.w3.org/2001/05/datatypes.xsd 516 | rgxp := regexp.MustCompile(`([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*`) 517 | if rgxp.MatchString(string(data)) { 518 | return Language(""), errors.New("String does not match pattern ([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*") 519 | } 520 | return Language(data), nil 521 | } 522 | 523 | type NMTOKEN Token 524 | 525 | //TODO: check for valid symbols: https://www.w3.org/TR/xml/#NT-Nmtoken 526 | func (tp NMTOKEN) NewNMTOKEN(data string) NMTOKEN { 527 | return NMTOKEN(data) 528 | } 529 | 530 | type NMTOKENS []NMTOKEN 531 | 532 | func (tp NMTOKENS) NewNMTOKENS(data []NMTOKEN) NMTOKENS { 533 | result := make(NMTOKENS, len(data)) 534 | for i, j := range data { 535 | result[i] = j 536 | } 537 | return result 538 | } 539 | 540 | type Name Token 541 | 542 | //TODO: implements https://www.w3.org/TR/xml/#NT-Name 543 | func (tp Name) NewName(data Token) Name { 544 | return Name(data) 545 | } 546 | 547 | type NCName Name 548 | 549 | //TODO: https://www.w3.org/TR/REC-xml/#NT-Name and https://www.w3.org/TR/xml-names/#NT-NCName 550 | func (tp NCName) NewNCName(data Name) NCName { 551 | return NCName(data) 552 | } 553 | 554 | //TODO: improve next types to correspond to XMLSchema 555 | type ID NCName 556 | 557 | func (tp ID) NewID(data NCName) ID { 558 | return ID(data) 559 | } 560 | 561 | type IDREF NCName 562 | 563 | func (tp IDREF) NewIDREF(data NCName) IDREF { 564 | return IDREF(data) 565 | } 566 | 567 | type IDREFS []IDREF 568 | 569 | func (tp IDREFS) NewIDREFS(data []IDREF) IDREFS { 570 | result := make(IDREFS, len(data)) 571 | for i, j := range data { 572 | result[i] = j 573 | } 574 | return result 575 | } 576 | 577 | type ENTITY NCName 578 | 579 | func (tp ENTITY) NewENTITY(data NCName) ENTITY { 580 | return ENTITY(data) 581 | } 582 | 583 | type ENTITIES []ENTITY 584 | 585 | func (tp ENTITIES) NewENTITIES(data []ENTITY) ENTITIES { 586 | result := make(ENTITIES, len(data)) 587 | for i, j := range data { 588 | result[i] = j 589 | } 590 | return result 591 | } 592 | 593 | type Integer int64 594 | 595 | func (tp Integer) NewInteger(data int64) Integer { 596 | return Integer(data) 597 | } 598 | 599 | type NonPositiveInteger int64 600 | 601 | func (tp NonPositiveInteger) NewNonPositiveInteger(data int64) (NonPositiveInteger, error) { 602 | if data > 0 { 603 | return 0, errors.New("Value must be less or equal to 0") 604 | } 605 | return NonPositiveInteger(data), nil 606 | } 607 | 608 | type NegativeInteger int64 609 | 610 | func (tp NegativeInteger) NewNegativeInteger(data int64) (NegativeInteger, error) { 611 | if data >= 0 { 612 | return 0, errors.New("Value must be less than 0") 613 | } 614 | return NegativeInteger(data), nil 615 | } 616 | 617 | type Long int64 618 | 619 | func (tp Long) NewLong(data int64) Long { 620 | return Long(data) 621 | } 622 | 623 | type Int int32 624 | 625 | func (tp Int) NewInt(data int32) Int { 626 | return Int(data) 627 | } 628 | 629 | type Short int16 630 | 631 | func (tp Short) NewShort(data int16) Short { 632 | return Short(data) 633 | } 634 | 635 | type Byte int8 636 | 637 | func (tp Byte) NewByte(data int8) Byte { 638 | return Byte(data) 639 | } 640 | 641 | type NonNegativeInteger int64 642 | 643 | func (tp NonNegativeInteger) NewNonNegativeInteger(data int64) (NonNegativeInteger, error) { 644 | if data > 0 { 645 | return 0, errors.New("Value must be more or equal to 0") 646 | } 647 | return NonNegativeInteger(data), nil 648 | } 649 | 650 | type UnsignedLong uint64 651 | 652 | func (tp UnsignedLong) NewUnsignedLong(data uint64) UnsignedLong { 653 | return UnsignedLong(data) 654 | } 655 | 656 | type UnsignedInt uint32 657 | 658 | func (tp UnsignedInt) NewUnsignedInt(data uint32) UnsignedInt { 659 | return UnsignedInt(data) 660 | } 661 | 662 | type UnsignedShort uint16 663 | 664 | func (tp UnsignedShort) NewUnsignedShort(data uint16) UnsignedShort { 665 | return UnsignedShort(data) 666 | } 667 | 668 | type UnsignedByte uint8 669 | 670 | func (tp UnsignedByte) NewUnsignedByte(data uint8) UnsignedByte { 671 | return UnsignedByte(data) 672 | } 673 | 674 | type PositiveInteger int64 675 | 676 | func (tp PositiveInteger) NewPositiveInteger(data int64) (PositiveInteger, error) { 677 | if data >= 0 { 678 | return 0, errors.New("Value must be more than 0") 679 | } 680 | return PositiveInteger(data), nil 681 | } 682 | -------------------------------------------------------------------------------- /xsd/iso8601/iso8601_duration.go: -------------------------------------------------------------------------------- 1 | package iso8601 2 | 3 | import ( 4 | "errors" 5 | "regexp" 6 | ) 7 | 8 | //Duration of iso8601 9 | type Duration struct { 10 | years string //= number of years 11 | months string //= number of months 12 | days string //= number of days 13 | // Time section 14 | hours string //= the number of hours 15 | minutes string //= the number of minutes 16 | seconds string //= the number of seconds 17 | } 18 | 19 | //NewDuration return duration 20 | func NewDuration(years, months, days, hours, minutes, seconds string) (*Duration, error) { 21 | // Pattern for Years, Months, Days, Hours and Minutes components 22 | pattern1 := "^$|[0-9]+" 23 | // Pattern for Seconds component 24 | pattern2 := "^$|[0-9]+(\\.[0-9]+)?" 25 | 26 | matched, err := regexp.MatchString(pattern1, years) 27 | if err != nil { 28 | return nil, err 29 | } else if !matched { 30 | return nil, errors.New("years value = " + years + " does not match pattern " + pattern1) 31 | } 32 | 33 | matched, err = regexp.MatchString(pattern1, months) 34 | if err != nil { 35 | return nil, err 36 | } else if !matched { 37 | return nil, errors.New("months value = " + months + " does not match pattern " + pattern1) 38 | } 39 | 40 | matched, err = regexp.MatchString(pattern1, days) 41 | if err != nil { 42 | return nil, err 43 | } else if !matched { 44 | return nil, errors.New("months value = " + days + " does not match pattern " + pattern1) 45 | } 46 | 47 | matched, err = regexp.MatchString(pattern1, hours) 48 | if err != nil { 49 | return nil, err 50 | } else if !matched { 51 | return nil, errors.New("months value = " + hours + " does not match pattern " + pattern1) 52 | } 53 | 54 | matched, err = regexp.MatchString(pattern1, minutes) 55 | if err != nil { 56 | return nil, err 57 | } else if !matched { 58 | return nil, errors.New("months value = " + minutes + " does not match pattern " + pattern1) 59 | } 60 | 61 | matched, err = regexp.MatchString(pattern2, seconds) 62 | if err != nil { 63 | return nil, err 64 | } else if !matched { 65 | return nil, errors.New("years value = " + seconds + " does not match pattern " + pattern2) 66 | } 67 | 68 | return &Duration{years: years, months: months, hours: hours, days: days, minutes: minutes, seconds: seconds}, nil 69 | } 70 | 71 | //ISO8601Duration to string 72 | func (duration Duration) ISO8601Duration() string { 73 | var result string 74 | result += "P" // time duration designator 75 | //years 76 | if duration.years != "" { 77 | result += duration.years + "Y" 78 | } 79 | if duration.months != "" { 80 | result += duration.months + "M" 81 | } 82 | if duration.days != "" { 83 | result += duration.days + "D" 84 | } 85 | 86 | if duration.hours != "" && duration.minutes != "" && duration.seconds != "" { 87 | result += "T" 88 | if duration.hours != "" { 89 | result += duration.hours + "H" 90 | } 91 | if duration.minutes != "" { 92 | result += duration.minutes + "M" 93 | } 94 | if duration.seconds != "" { 95 | result += duration.seconds + "S" 96 | } 97 | } 98 | 99 | if len(result) == 1 { 100 | result += "T0S" 101 | } 102 | 103 | return result 104 | } 105 | -------------------------------------------------------------------------------- /xsd/onvif/r-2.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /xsd/onvif/t-1.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | TopicPathExpression ::= TopicPath ( '|' TopicPath )* 87 | TopicPath ::= RootTopic ChildTopicExpression* 88 | RootTopic ::= NamespacePrefix? ('//')? (NCName | '*') 89 | NamespacePrefix ::= NCName ':' 90 | ChildTopicExpression ::= '/' '/'? (QName | NCName | '*'| '.') 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | The pattern allows strings matching the following EBNF: 102 | ConcreteTopicPath ::= RootTopic ChildTopic* 103 | RootTopic ::= QName 104 | ChildTopic ::= '/' (QName | NCName) 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | The pattern allows strings matching the following EBNF: 116 | RootTopic ::= QName 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /xsd/onvif/ws-addr.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /xsd/onvif/xml.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |

About the XML namespace

7 |
8 |

9 | This schema document describes the XML namespace, in a form 10 | suitable for import by other schema documents. 11 |

12 |

13 | See 14 | http://www.w3.org/XML/1998/namespace.html and 15 | 16 | http://www.w3.org/TR/REC-xml for information 17 | about this namespace. 18 |

19 |

20 | Note that local names in this namespace are intended to be 21 | defined only by the World Wide Web Consortium or its subgroups. 22 | The names currently defined in this namespace are listed below. 23 | They should not be used with conflicting semantics by any Working 24 | Group, specification, or document instance. 25 |

26 |

27 | See further below in this document for more information about how to refer to this schema document from your own 28 | XSD schema documents and about the 29 | namespace-versioning policy governing this schema document. 30 |

31 |
32 |
33 |
34 |
35 | 36 | 37 | 38 |
39 |

lang (as an attribute name)

40 |

41 | denotes an attribute whose value 42 | is a language code for the natural language of the content of 43 | any element; its value is inherited. This name is reserved 44 | by virtue of its definition in the XML specification.

45 |
46 |
47 |

Notes

48 |

49 | Attempting to install the relevant ISO 2- and 3-letter 50 | codes as the enumerated possible values is probably never 51 | going to be a realistic possibility. 52 |

53 |

54 | See BCP 47 at 55 | http://www.rfc-editor.org/rfc/bcp/bcp47.txt 56 | and the IANA language subtag registry at 57 | 58 | http://www.iana.org/assignments/language-subtag-registry 59 | for further information. 60 |

61 |

62 | The union allows for the 'un-declaration' of xml:lang with 63 | the empty string. 64 |

65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
78 | 79 | 80 | 81 |
82 |

space (as an attribute name)

83 |

84 | denotes an attribute whose 85 | value is a keyword indicating what whitespace processing 86 | discipline is intended for the content of the element; its 87 | value is inherited. This name is reserved by virtue of its 88 | definition in the XML specification.

89 |
90 |
91 |
92 | 93 | 94 | 95 | 96 | 97 | 98 |
99 | 100 | 101 | 102 |
103 |

base (as an attribute name)

104 |

105 | denotes an attribute whose value 106 | provides a URI to be used as the base for interpreting any 107 | relative URIs in the scope of the element on which it 108 | appears; its value is inherited. This name is reserved 109 | by virtue of its definition in the XML Base specification.

110 |

111 | See http://www.w3.org/TR/xmlbase/ 112 | for information about this attribute. 113 |

114 |
115 |
116 |
117 |
118 | 119 | 120 | 121 |
122 |

id (as an attribute name)

123 |

124 | denotes an attribute whose value 125 | should be interpreted as if declared to be of type ID. 126 | This name is reserved by virtue of its definition in the 127 | xml:id specification.

128 |

129 | See http://www.w3.org/TR/xml-id/ 130 | for information about this attribute. 131 |

132 |
133 |
134 |
135 |
136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 |
145 |

Father (in any context at all)

146 |
147 |

148 | denotes Jon Bosak, the chair of 149 | the original XML Working Group. This name is reserved by 150 | the following decision of the W3C XML Plenary and 151 | XML Coordination groups: 152 |

153 |
154 |

155 | In appreciation for his vision, leadership and 156 | dedication the W3C XML Plenary on this 10th day of 157 | February, 2000, reserves for Jon Bosak in perpetuity 158 | the XML name "xml:Father". 159 |

160 |
161 |
162 |
163 |
164 |
165 | 166 | 167 |
168 |

169 | About this schema document 170 |

171 |
172 |

173 | This schema defines attributes and an attribute group suitable 174 | for use by schemas wishing to allow xml:base, 175 | xml:lang, xml:space or 176 | xml:id attributes on elements they define. 177 |

178 |

179 | To enable this, such a schema must import this schema for 180 | the XML namespace, e.g. as follows: 181 |

182 |
183 |           <schema . . .>
184 |            . . .
185 |            <import namespace="http://www.w3.org/XML/1998/namespace"
186 |                       schemaLocation="http://www.w3.org/2001/xml.xsd"/>
187 |      
188 |

189 | or 190 |

191 |
192 |            <import namespace="http://www.w3.org/XML/1998/namespace"
193 |                       schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
194 |      
195 |

196 | Subsequently, qualified reference to any of the attributes or the 197 | group defined below will have the desired effect, e.g. 198 |

199 |
200 |           <type . . .>
201 |            . . .
202 |            <attributeGroup ref="xml:specialAttrs"/>
203 |      
204 |

205 | will define a type which will schema-validate an instance element 206 | with any of those attributes. 207 |

208 |
209 |
210 |
211 |
212 | 213 | 214 |
215 |

216 | Versioning policy for this schema document 217 |

218 |
219 |

220 | In keeping with the XML Schema WG's standard versioning 221 | policy, this schema document will persist at 222 | 223 | http://www.w3.org/2009/01/xml.xsd. 224 |

225 |

226 | At the date of issue it can also be found at 227 | 228 | http://www.w3.org/2001/xml.xsd. 229 |

230 |

231 | The schema document at that URI may however change in the future, 232 | in order to remain compatible with the latest version of XML 233 | Schema itself, or with the XML namespace itself. In other words, 234 | if the XML Schema or XML namespaces change, the version of this 235 | document at 236 | http://www.w3.org/2001/xml.xsd 237 | 238 | will change accordingly; the version at 239 | 240 | http://www.w3.org/2009/01/xml.xsd 241 | 242 | will not change. 243 |

244 |

245 | Previous dated (and unchanging) versions of this schema 246 | document are at: 247 |

248 | 266 |
267 |
268 |
269 |
270 |
--------------------------------------------------------------------------------