154 lines
4.3 KiB
Go
154 lines
4.3 KiB
Go
package soapcalls
|
|
|
|
import (
|
|
"context"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var (
|
|
ErrWrongDMR = errors.New("something broke somewhere - wrong DMR URL?")
|
|
)
|
|
|
|
type rootNode struct {
|
|
XMLName xml.Name `xml:"root"`
|
|
Device struct {
|
|
XMLName xml.Name `xml:"device"`
|
|
ServiceList struct {
|
|
XMLName xml.Name `xml:"serviceList"`
|
|
Services []struct {
|
|
XMLName xml.Name `xml:"service"`
|
|
Type string `xml:"serviceType"`
|
|
ID string `xml:"serviceId"`
|
|
ControlURL string `xml:"controlURL"`
|
|
EventSubURL string `xml:"eventSubURL"`
|
|
} `xml:"service"`
|
|
} `xml:"serviceList"`
|
|
} `xml:"device"`
|
|
}
|
|
|
|
type eventPropertySet struct {
|
|
XMLName xml.Name `xml:"propertyset"`
|
|
EventInstance struct {
|
|
XMLName xml.Name `xml:"InstanceID"`
|
|
Value string `xml:"val,attr"`
|
|
EventCurrentTransportActions struct {
|
|
Value string `xml:"val,attr"`
|
|
} `xml:"CurrentTransportActions"`
|
|
EventTransportState struct {
|
|
Value string `xml:"val,attr"`
|
|
} `xml:"TransportState"`
|
|
} `xml:"property>LastChange>Event>InstanceID"`
|
|
}
|
|
|
|
// DMRextracted stored the services urls
|
|
type DMRextracted struct {
|
|
AvtransportControlURL string
|
|
AvtransportEventSubURL string
|
|
RenderingControlURL string
|
|
ConnectionManagerURL string
|
|
}
|
|
|
|
// DMRextractor extracts the services URLs from the main DMR xml.
|
|
func DMRextractor(ctx context.Context, dmrurl string) (*DMRextracted, error) {
|
|
var root rootNode
|
|
ex := &DMRextracted{}
|
|
|
|
parsedURL, err := url.Parse(dmrurl)
|
|
if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" {
|
|
return nil, fmt.Errorf("DMRextractor parse error: %w", err)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
req, err := http.NewRequestWithContext(ctx, "GET", dmrurl, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor GET error: %w", err)
|
|
}
|
|
|
|
req.Header.Set("Connection", "close")
|
|
|
|
xmlresp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor Do GET error: %w", err)
|
|
}
|
|
defer xmlresp.Body.Close()
|
|
|
|
xmlbody, err := io.ReadAll(xmlresp.Body)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor read error: %w", err)
|
|
}
|
|
|
|
err = xml.Unmarshal(xmlbody, &root)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor unmarshal error: %w", err)
|
|
}
|
|
|
|
for i := 0; i < len(root.Device.ServiceList.Services); i++ {
|
|
service := root.Device.ServiceList.Services[i]
|
|
if !strings.HasPrefix(service.EventSubURL, "/") {
|
|
service.EventSubURL = "/" + service.EventSubURL
|
|
}
|
|
if !strings.HasPrefix(service.ControlURL, "/") {
|
|
service.ControlURL = "/" + service.ControlURL
|
|
}
|
|
|
|
if service.ID == "urn:upnp-org:serviceId:AVTransport" {
|
|
ex.AvtransportControlURL = parsedURL.Scheme + "://" + parsedURL.Host + service.ControlURL
|
|
ex.AvtransportEventSubURL = parsedURL.Scheme + "://" + parsedURL.Host + service.EventSubURL
|
|
|
|
_, err := url.ParseRequestURI(ex.AvtransportControlURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor invalid AvtransportControlURL: %w", err)
|
|
}
|
|
|
|
_, err = url.ParseRequestURI(ex.AvtransportEventSubURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor invalid AvtransportEventSubURL: %w", err)
|
|
}
|
|
}
|
|
|
|
if service.ID == "urn:upnp-org:serviceId:RenderingControl" {
|
|
ex.RenderingControlURL = parsedURL.Scheme + "://" + parsedURL.Host + service.ControlURL
|
|
|
|
_, err = url.ParseRequestURI(ex.RenderingControlURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor invalid RenderingControlURL: %w", err)
|
|
}
|
|
}
|
|
|
|
if service.ID == "urn:upnp-org:serviceId:ConnectionManager" {
|
|
ex.ConnectionManagerURL = parsedURL.Scheme + "://" + parsedURL.Host + service.ControlURL
|
|
|
|
_, err = url.ParseRequestURI(ex.ConnectionManagerURL)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("DMRextractor invalid ConnectionManagerURL: %w", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
if ex.AvtransportControlURL != "" {
|
|
return ex, nil
|
|
}
|
|
|
|
return nil, ErrWrongDMR
|
|
}
|
|
|
|
// EventNotifyParser parses the Notify messages from the DMR device.
|
|
func EventNotifyParser(xmlbody string) (string, string, error) {
|
|
var root eventPropertySet
|
|
err := xml.Unmarshal([]byte(xmlbody), &root)
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("EventNotifyParser unmarshal error: %w", err)
|
|
}
|
|
previousstate := root.EventInstance.EventCurrentTransportActions.Value
|
|
newstate := root.EventInstance.EventTransportState.Value
|
|
|
|
return previousstate, newstate, nil
|
|
}
|