package onvif import ( "bytes" "crypto/sha1" "encoding/base64" "errors" "io/ioutil" "net/http" "net/url" "regexp" "time" "github.com/clbanning/mxj" "github.com/satori/go.uuid" ) var httpClient = &http.Client{Timeout: time.Second * 5} // SOAP contains data for SOAP request type SOAP struct { Body string XMLNs []string User string Password string TokenAge time.Duration } // SendRequest sends SOAP request to xAddr func (soap SOAP) SendRequest(xaddr string) (mxj.Map, error) { // Create SOAP request request := soap.createRequest() // Make sure URL valid and add authentication in xAddr urlXAddr, err := url.Parse(xaddr) if err != nil { return nil, err } if soap.User != "" { urlXAddr.User = url.UserPassword(soap.User, soap.Password) } // Create HTTP request buffer := bytes.NewBuffer([]byte(request)) req, err := http.NewRequest("POST", urlXAddr.String(), buffer) req.Header.Set("Content-Type", "application/soap+xml") req.Header.Set("Charset", "utf-8") // Send request resp, err := httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() // Read response body responseBody, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err } // Parse XML to map mapXML, err := mxj.NewMapXml(responseBody) if err != nil { return nil, err } // Check if SOAP returns fault fault, _ := mapXML.ValueForPathString("Envelope.Body.Fault.Reason.Text.#text") if fault != "" { return nil, errors.New(fault) } return mapXML, nil } func (soap SOAP) createRequest() string { // Create request envelope request := `` request += ` s:Receiver Error creating user token ` } else { request += "" + soapHeader + "" } } // Set request body request += "" + soap.Body + "" // Close request envelope request += "" // Clean request request = regexp.MustCompile(`\>\s+\<`).ReplaceAllString(request, "><") request = regexp.MustCompile(`\s+`).ReplaceAllString(request, " ") return request } func (soap SOAP) createUserToken() (string, error) { newUuid, err := uuid.NewV4() if err != nil { return "", err } nonce := newUuid.Bytes() nonce64 := base64.StdEncoding.EncodeToString(nonce) timestamp := time.Now().Add(soap.TokenAge).UTC().Format(time.RFC3339) token := string(nonce) + timestamp + soap.Password sha := sha1.New() sha.Write([]byte(token)) shaToken := sha.Sum(nil) shaDigest64 := base64.StdEncoding.EncodeToString(shaToken) return ` ` + soap.User + ` ` + shaDigest64 + ` ` + nonce64 + ` ` + timestamp + ` `, nil }