vendor: add github.com/anacrolix/dms
This commit is contained in:
parent
7ee7bc87ae
commit
5edfd31a6d
183 changed files with 12766 additions and 0 deletions
24
vendor/github.com/anacrolix/dms/LICENSE
generated
vendored
Normal file
24
vendor/github.com/anacrolix/dms/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
Copyright (c) 2012, Matt Joiner <anacrolix@gmail.com>.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the <organization> nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
102
vendor/github.com/anacrolix/dms/dlna/dlna.go
generated
vendored
Normal file
102
vendor/github.com/anacrolix/dms/dlna/dlna.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
package dlna
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TimeSeekRangeDomain = "TimeSeekRange.dlna.org"
|
||||
ContentFeaturesDomain = "contentFeatures.dlna.org"
|
||||
TransferModeDomain = "transferMode.dlna.org"
|
||||
)
|
||||
|
||||
type ContentFeatures struct {
|
||||
ProfileName string
|
||||
SupportTimeSeek bool
|
||||
SupportRange bool
|
||||
// Play speeds, DLNA.ORG_PS would go here if supported.
|
||||
Transcoded bool
|
||||
}
|
||||
|
||||
func BinaryInt(b bool) uint {
|
||||
if b {
|
||||
return 1
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// flags are in hex. trailing 24 zeroes, 26 are after the space
|
||||
// "DLNA.ORG_OP=" time-seek-range-supp bytes-range-header-supp
|
||||
func (cf ContentFeatures) String() (ret string) {
|
||||
//DLNA.ORG_PN=[a-zA-Z0-9_]*
|
||||
params := make([]string, 0, 2)
|
||||
if cf.ProfileName != "" {
|
||||
params = append(params, "DLNA.ORG_PN="+cf.ProfileName)
|
||||
}
|
||||
params = append(params, fmt.Sprintf(
|
||||
"DLNA.ORG_OP=%b%b;DLNA.ORG_CI=%b",
|
||||
BinaryInt(cf.SupportTimeSeek),
|
||||
BinaryInt(cf.SupportRange),
|
||||
BinaryInt(cf.Transcoded)))
|
||||
return strings.Join(params, ";")
|
||||
}
|
||||
|
||||
func ParseNPTTime(s string) (time.Duration, error) {
|
||||
var h, m, sec, ms time.Duration
|
||||
n, err := fmt.Sscanf(s, "%d:%2d:%2d.%3d", &h, &m, &sec, &ms)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if n < 3 {
|
||||
return -1, fmt.Errorf("invalid npt time: %s", s)
|
||||
}
|
||||
ret := time.Duration(h) * time.Hour
|
||||
ret += time.Duration(m) * time.Minute
|
||||
ret += sec * time.Second
|
||||
ret += ms * time.Millisecond
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func FormatNPTTime(npt time.Duration) string {
|
||||
npt /= time.Millisecond
|
||||
ms := npt % 1000
|
||||
npt /= 1000
|
||||
s := npt % 60
|
||||
npt /= 60
|
||||
m := npt % 60
|
||||
npt /= 60
|
||||
h := npt
|
||||
return fmt.Sprintf("%02d:%02d:%02d.%03d", h, m, s, ms)
|
||||
}
|
||||
|
||||
type NPTRange struct {
|
||||
Start, End time.Duration
|
||||
}
|
||||
|
||||
func ParseNPTRange(s string) (ret NPTRange, err error) {
|
||||
ss := strings.SplitN(s, "-", 2)
|
||||
if ss[0] != "" {
|
||||
ret.Start, err = ParseNPTTime(ss[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if ss[1] != "" {
|
||||
ret.End, err = ParseNPTTime(ss[1])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me NPTRange) String() (ret string) {
|
||||
ret = me.Start.String() + "-"
|
||||
if me.End >= 0 {
|
||||
ret += me.End.String()
|
||||
}
|
||||
return
|
||||
}
|
68
vendor/github.com/anacrolix/dms/soap/soap.go
generated
vendored
Normal file
68
vendor/github.com/anacrolix/dms/soap/soap.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
package soap
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
const (
|
||||
EncodingStyle = "http://schemas.xmlsoap.org/soap/encoding/"
|
||||
EnvelopeNS = "http://schemas.xmlsoap.org/soap/envelope/"
|
||||
)
|
||||
|
||||
type Arg struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
XMLName xml.Name
|
||||
Args []Arg
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
Action []byte `xml:",innerxml"`
|
||||
}
|
||||
|
||||
type UPnPError struct {
|
||||
XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"`
|
||||
Code uint `xml:"errorCode"`
|
||||
Desc string `xml:"errorDescription"`
|
||||
}
|
||||
|
||||
type FaultDetail struct {
|
||||
XMLName xml.Name `xml:"detail"`
|
||||
Data interface{}
|
||||
}
|
||||
|
||||
type Fault struct {
|
||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Fault"`
|
||||
FaultCode string `xml:"faultcode"`
|
||||
FaultString string `xml:"faultstring"`
|
||||
Detail FaultDetail `xml:"detail"`
|
||||
}
|
||||
|
||||
func NewFault(s string, detail interface{}) *Fault {
|
||||
return &Fault{
|
||||
FaultCode: EnvelopeNS + ":Client",
|
||||
FaultString: s,
|
||||
Detail: FaultDetail{
|
||||
Data: detail,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type Envelope struct {
|
||||
XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
|
||||
EncodingStyle string `xml:"encodingStyle,attr"`
|
||||
Body Body `xml:"http://schemas.xmlsoap.org/soap/envelope/ Body"`
|
||||
}
|
||||
|
||||
/* XML marshalling of nested namespaces is broken.
|
||||
|
||||
func NewEnvelope(action []byte) Envelope {
|
||||
return Envelope{
|
||||
EncodingStyle: EncodingStyle,
|
||||
Body: Body{action},
|
||||
}
|
||||
}
|
||||
*/
|
330
vendor/github.com/anacrolix/dms/ssdp/ssdp.go
generated
vendored
Normal file
330
vendor/github.com/anacrolix/dms/ssdp/ssdp.go
generated
vendored
Normal file
|
@ -0,0 +1,330 @@
|
|||
package ssdp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/ipv4"
|
||||
)
|
||||
|
||||
const (
|
||||
AddrString = "239.255.255.250:1900"
|
||||
rootDevice = "upnp:rootdevice"
|
||||
aliveNTS = "ssdp:alive"
|
||||
byebyeNTS = "ssdp:byebye"
|
||||
)
|
||||
|
||||
var (
|
||||
NetAddr *net.UDPAddr
|
||||
)
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
NetAddr, err = net.ResolveUDPAddr("udp4", AddrString)
|
||||
if err != nil {
|
||||
log.Panicf("Could not resolve %s: %s", AddrString, err)
|
||||
}
|
||||
}
|
||||
|
||||
type badStringError struct {
|
||||
what string
|
||||
str string
|
||||
}
|
||||
|
||||
func (e *badStringError) Error() string { return fmt.Sprintf("%s %q", e.what, e.str) }
|
||||
|
||||
func ReadRequest(b *bufio.Reader) (req *http.Request, err error) {
|
||||
tp := textproto.NewReader(b)
|
||||
var s string
|
||||
if s, err = tp.ReadLine(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err == io.EOF {
|
||||
err = io.ErrUnexpectedEOF
|
||||
}
|
||||
}()
|
||||
|
||||
var f []string
|
||||
// TODO a split that only allows N values?
|
||||
if f = strings.SplitN(s, " ", 3); len(f) < 3 {
|
||||
return nil, &badStringError{"malformed request line", s}
|
||||
}
|
||||
if f[1] != "*" {
|
||||
return nil, &badStringError{"bad URL request", f[1]}
|
||||
}
|
||||
req = &http.Request{
|
||||
Method: f[0],
|
||||
}
|
||||
var ok bool
|
||||
if req.ProtoMajor, req.ProtoMinor, ok = http.ParseHTTPVersion(strings.TrimSpace(f[2])); !ok {
|
||||
return nil, &badStringError{"malformed HTTP version", f[2]}
|
||||
}
|
||||
|
||||
mimeHeader, err := tp.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header = http.Header(mimeHeader)
|
||||
return
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
conn *net.UDPConn
|
||||
Interface net.Interface
|
||||
Server string
|
||||
Services []string
|
||||
Devices []string
|
||||
Location func(net.IP) string
|
||||
UUID string
|
||||
NotifyInterval time.Duration
|
||||
closed chan struct{}
|
||||
}
|
||||
|
||||
func makeConn(ifi net.Interface) (ret *net.UDPConn, err error) {
|
||||
ret, err = net.ListenMulticastUDP("udp", &ifi, NetAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
p := ipv4.NewPacketConn(ret)
|
||||
if err := p.SetMulticastTTL(2); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
if err := p.SetMulticastLoopback(true); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Server) serve() {
|
||||
for {
|
||||
b := make([]byte, me.Interface.MTU)
|
||||
n, addr, err := me.conn.ReadFromUDP(b)
|
||||
select {
|
||||
case <-me.closed:
|
||||
return
|
||||
default:
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("error reading from UDP socket: %s", err)
|
||||
break
|
||||
}
|
||||
go me.handle(b[:n], addr)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) Init() (err error) {
|
||||
me.closed = make(chan struct{})
|
||||
me.conn, err = makeConn(me.Interface)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Server) Close() {
|
||||
close(me.closed)
|
||||
me.sendByeBye()
|
||||
me.conn.Close()
|
||||
}
|
||||
|
||||
func (me *Server) Serve() (err error) {
|
||||
go me.serve()
|
||||
for {
|
||||
addrs, err := me.Interface.Addrs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
ip := func() net.IP {
|
||||
switch val := addr.(type) {
|
||||
case *net.IPNet:
|
||||
return val.IP
|
||||
case *net.IPAddr:
|
||||
return val.IP
|
||||
}
|
||||
panic(fmt.Sprint("unexpected addr type:", addr))
|
||||
}()
|
||||
extraHdrs := [][2]string{
|
||||
{"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)},
|
||||
{"LOCATION", me.Location(ip)},
|
||||
}
|
||||
me.notifyAll(aliveNTS, extraHdrs)
|
||||
}
|
||||
time.Sleep(me.NotifyInterval)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) usnFromTarget(target string) string {
|
||||
if target == me.UUID {
|
||||
return target
|
||||
}
|
||||
return me.UUID + "::" + target
|
||||
}
|
||||
|
||||
func (me *Server) makeNotifyMessage(target, nts string, extraHdrs [][2]string) []byte {
|
||||
lines := [...][2]string{
|
||||
{"HOST", AddrString},
|
||||
{"NT", target},
|
||||
{"NTS", nts},
|
||||
{"SERVER", me.Server},
|
||||
{"USN", me.usnFromTarget(target)},
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprint(buf, "NOTIFY * HTTP/1.1\r\n")
|
||||
writeHdr := func(keyValue [2]string) {
|
||||
fmt.Fprintf(buf, "%s: %s\r\n", keyValue[0], keyValue[1])
|
||||
}
|
||||
for _, pair := range lines {
|
||||
writeHdr(pair)
|
||||
}
|
||||
for _, pair := range extraHdrs {
|
||||
writeHdr(pair)
|
||||
}
|
||||
fmt.Fprint(buf, "\r\n")
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
func (me *Server) send(buf []byte, addr *net.UDPAddr) {
|
||||
if n, err := me.conn.WriteToUDP(buf, addr); err != nil {
|
||||
log.Printf("error writing to UDP socket: %s", err)
|
||||
} else if n != len(buf) {
|
||||
log.Printf("short write: %d/%d bytes", n, len(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) delayedSend(delay time.Duration, buf []byte, addr *net.UDPAddr) {
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(delay):
|
||||
me.send(buf, addr)
|
||||
case <-me.closed:
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (me *Server) log(args ...interface{}) {
|
||||
args = append([]interface{}{me.Interface.Name + ":"}, args...)
|
||||
log.Print(args...)
|
||||
}
|
||||
|
||||
func (me *Server) sendByeBye() {
|
||||
for _, type_ := range me.allTypes() {
|
||||
buf := me.makeNotifyMessage(type_, byebyeNTS, nil)
|
||||
me.send(buf, NetAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) notifyAll(nts string, extraHdrs [][2]string) {
|
||||
for _, type_ := range me.allTypes() {
|
||||
buf := me.makeNotifyMessage(type_, nts, extraHdrs)
|
||||
delay := time.Duration(rand.Int63n(int64(100 * time.Millisecond)))
|
||||
me.delayedSend(delay, buf, NetAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) allTypes() (ret []string) {
|
||||
for _, a := range [][]string{
|
||||
{rootDevice, me.UUID},
|
||||
me.Devices,
|
||||
me.Services,
|
||||
} {
|
||||
ret = append(ret, a...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Server) handle(buf []byte, sender *net.UDPAddr) {
|
||||
req, err := ReadRequest(bufio.NewReader(bytes.NewReader(buf)))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
if req.Method != "M-SEARCH" || req.Header.Get("man") != `"ssdp:discover"` {
|
||||
return
|
||||
}
|
||||
var mx uint
|
||||
if req.Header.Get("Host") == AddrString {
|
||||
mxHeader := req.Header.Get("mx")
|
||||
i, err := strconv.ParseUint(mxHeader, 0, 0)
|
||||
if err != nil {
|
||||
log.Printf("Invalid mx header %q: %s", mxHeader, err)
|
||||
return
|
||||
}
|
||||
mx = uint(i)
|
||||
} else {
|
||||
mx = 1
|
||||
}
|
||||
types := func(st string) []string {
|
||||
if st == "ssdp:all" {
|
||||
return me.allTypes()
|
||||
}
|
||||
for _, t := range me.allTypes() {
|
||||
if t == st {
|
||||
return []string{t}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}(req.Header.Get("st"))
|
||||
for _, ip := range func() (ret []net.IP) {
|
||||
addrs, err := me.Interface.Addrs()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, addr := range addrs {
|
||||
if ip, ok := func() (net.IP, bool) {
|
||||
switch data := addr.(type) {
|
||||
case *net.IPNet:
|
||||
if data.Contains(sender.IP) {
|
||||
return data.IP, true
|
||||
}
|
||||
return nil, false
|
||||
case *net.IPAddr:
|
||||
return data.IP, true
|
||||
}
|
||||
panic(addr)
|
||||
}(); ok {
|
||||
ret = append(ret, ip)
|
||||
}
|
||||
}
|
||||
return
|
||||
}() {
|
||||
for _, type_ := range types {
|
||||
resp := me.makeResponse(ip, type_, req)
|
||||
delay := time.Duration(rand.Int63n(int64(time.Second) * int64(mx)))
|
||||
me.delayedSend(delay, resp, sender)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (me *Server) makeResponse(ip net.IP, targ string, req *http.Request) (ret []byte) {
|
||||
resp := &http.Response{
|
||||
StatusCode: 200,
|
||||
ProtoMajor: 1,
|
||||
ProtoMinor: 1,
|
||||
Header: make(http.Header),
|
||||
Request: req,
|
||||
}
|
||||
for _, pair := range [...][2]string{
|
||||
{"CACHE-CONTROL", fmt.Sprintf("max-age=%d", 5*me.NotifyInterval/2/time.Second)},
|
||||
{"EXT", ""},
|
||||
{"LOCATION", me.Location(ip)},
|
||||
{"SERVER", me.Server},
|
||||
{"ST", targ},
|
||||
{"USN", me.usnFromTarget(targ)},
|
||||
} {
|
||||
resp.Header.Set(pair[0], pair[1])
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
if err := resp.Write(buf); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
91
vendor/github.com/anacrolix/dms/upnp/eventing.go
generated
vendored
Normal file
91
vendor/github.com/anacrolix/dms/upnp/eventing.go
generated
vendored
Normal file
|
@ -0,0 +1,91 @@
|
|||
package upnp
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TODO: Why use namespace prefixes in PropertySet et al? Because the spec
|
||||
// uses them, and I believe the Golang standard library XML spec implementers
|
||||
// incorrectly assume that you can get away with just xmlns="".
|
||||
|
||||
// propertyset is the root element sent in an event callback.
|
||||
type PropertySet struct {
|
||||
XMLName struct{} `xml:"e:propertyset"`
|
||||
Properties []Property
|
||||
// This should be set to `"urn:schemas-upnp-org:event-1-0"`.
|
||||
Space string `xml:"xmlns:e,attr"`
|
||||
}
|
||||
|
||||
// propertys provide namespacing to the contained variables.
|
||||
type Property struct {
|
||||
XMLName struct{} `xml:"e:property"`
|
||||
Variable Variable
|
||||
}
|
||||
|
||||
// Represents an evented state variable that has sendEvents="yes" in its
|
||||
// service spec.
|
||||
type Variable struct {
|
||||
XMLName xml.Name
|
||||
Value string `xml:",chardata"`
|
||||
}
|
||||
|
||||
type subscriber struct {
|
||||
sid string
|
||||
nextSeq uint32 // 0 for initial event, wraps from Uint32Max to 1.
|
||||
urls []*url.URL
|
||||
expiry time.Time
|
||||
}
|
||||
|
||||
// Intended to eventually be an embeddable implementation for managing
|
||||
// eventing for a service. Not complete.
|
||||
type Eventing struct {
|
||||
subscribers map[string]*subscriber
|
||||
}
|
||||
|
||||
func (me *Eventing) Subscribe(callback []*url.URL, timeoutSeconds int) (sid string, actualTimeout int, err error) {
|
||||
var uuid [16]byte
|
||||
io.ReadFull(rand.Reader, uuid[:])
|
||||
sid = FormatUUID(uuid[:])
|
||||
if _, ok := me.subscribers[sid]; ok {
|
||||
err = fmt.Errorf("already subscribed: %s", sid)
|
||||
return
|
||||
}
|
||||
ssr := &subscriber{
|
||||
sid: sid,
|
||||
urls: callback,
|
||||
expiry: time.Now().Add(time.Duration(timeoutSeconds) * time.Second),
|
||||
}
|
||||
if me.subscribers == nil {
|
||||
me.subscribers = make(map[string]*subscriber)
|
||||
}
|
||||
me.subscribers[sid] = ssr
|
||||
actualTimeout = int(ssr.expiry.Sub(time.Now()) / time.Second)
|
||||
return
|
||||
}
|
||||
|
||||
func (me *Eventing) Unsubscribe(sid string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var callbackURLRegexp = regexp.MustCompile("<(.*?)>")
|
||||
|
||||
// Parse the CALLBACK HTTP header in an event subscription request. See UPnP
|
||||
// Device Architecture 4.1.2.
|
||||
func ParseCallbackURLs(callback string) (ret []*url.URL) {
|
||||
for _, match := range callbackURLRegexp.FindAllStringSubmatch(callback, -1) {
|
||||
_url, err := url.Parse(match[1])
|
||||
if err != nil {
|
||||
log.Printf("bad callback url: %q", match[1])
|
||||
continue
|
||||
}
|
||||
ret = append(ret, _url)
|
||||
}
|
||||
return
|
||||
}
|
159
vendor/github.com/anacrolix/dms/upnp/upnp.go
generated
vendored
Normal file
159
vendor/github.com/anacrolix/dms/upnp/upnp.go
generated
vendored
Normal file
|
@ -0,0 +1,159 @@
|
|||
package upnp
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var serviceURNRegexp *regexp.Regexp = regexp.MustCompile(`^urn:schemas-upnp-org:service:(\w+):(\d+)$`)
|
||||
|
||||
type ServiceURN struct {
|
||||
Type string
|
||||
Version uint64
|
||||
}
|
||||
|
||||
func (me ServiceURN) String() string {
|
||||
return fmt.Sprintf("urn:schemas-upnp-org:service:%s:%d", me.Type, me.Version)
|
||||
}
|
||||
|
||||
func ParseServiceType(s string) (ret ServiceURN, err error) {
|
||||
matches := serviceURNRegexp.FindStringSubmatch(s)
|
||||
if matches == nil {
|
||||
err = errors.New(s)
|
||||
return
|
||||
}
|
||||
if len(matches) != 3 {
|
||||
log.Panicf("Invalid serviceURNRegexp ?")
|
||||
}
|
||||
ret.Type = matches[1]
|
||||
ret.Version, err = strconv.ParseUint(matches[2], 0, 0)
|
||||
return
|
||||
}
|
||||
|
||||
type SoapAction struct {
|
||||
ServiceURN
|
||||
Action string
|
||||
}
|
||||
|
||||
func ParseActionHTTPHeader(s string) (ret SoapAction, err error) {
|
||||
if s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
hashIndex := strings.LastIndex(s, "#")
|
||||
if hashIndex == -1 {
|
||||
return
|
||||
}
|
||||
ret.Action = s[hashIndex+1:]
|
||||
ret.ServiceURN, err = ParseServiceType(s[:hashIndex])
|
||||
return
|
||||
}
|
||||
|
||||
type SpecVersion struct {
|
||||
Major int `xml:"major"`
|
||||
Minor int `xml:"minor"`
|
||||
}
|
||||
|
||||
type Icon struct {
|
||||
Mimetype string `xml:"mimetype"`
|
||||
Width int `xml:"width"`
|
||||
Height int `xml:"height"`
|
||||
Depth int `xml:"depth"`
|
||||
URL string `xml:"url"`
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
XMLName xml.Name `xml:"service"`
|
||||
ServiceType string `xml:"serviceType"`
|
||||
ServiceId string `xml:"serviceId"`
|
||||
SCPDURL string
|
||||
ControlURL string `xml:"controlURL"`
|
||||
EventSubURL string `xml:"eventSubURL"`
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
DeviceType string `xml:"deviceType"`
|
||||
FriendlyName string `xml:"friendlyName"`
|
||||
Manufacturer string `xml:"manufacturer"`
|
||||
ModelName string `xml:"modelName"`
|
||||
UDN string
|
||||
IconList []Icon `xml:"iconList>icon"`
|
||||
ServiceList []Service `xml:"serviceList>service"`
|
||||
}
|
||||
|
||||
type DeviceDesc struct {
|
||||
XMLName xml.Name `xml:"urn:schemas-upnp-org:device-1-0 root"`
|
||||
SpecVersion SpecVersion `xml:"specVersion"`
|
||||
Device Device `xml:"device"`
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
XMLName xml.Name `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"`
|
||||
Code uint `xml:"errorCode"`
|
||||
Desc string `xml:"errorDescription"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("%d %s", e.Code, e.Desc)
|
||||
}
|
||||
|
||||
const (
|
||||
InvalidActionErrorCode = 401
|
||||
ActionFailedErrorCode = 501
|
||||
ArgumentValueInvalidErrorCode = 600
|
||||
)
|
||||
|
||||
var (
|
||||
InvalidActionError = Errorf(401, "Invalid Action")
|
||||
ArgumentValueInvalidError = Errorf(600, "The argument value is invalid")
|
||||
)
|
||||
|
||||
// Errorf creates an UPNP error from the given code and description
|
||||
func Errorf(code uint, tpl string, args ...interface{}) *Error {
|
||||
return &Error{Code: code, Desc: fmt.Sprintf(tpl, args...)}
|
||||
}
|
||||
|
||||
// ConvertError converts any error to an UPNP error
|
||||
func ConvertError(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e
|
||||
}
|
||||
return Errorf(ActionFailedErrorCode, err.Error())
|
||||
}
|
||||
|
||||
type Action struct {
|
||||
Name string
|
||||
Arguments []Argument
|
||||
}
|
||||
|
||||
type Argument struct {
|
||||
Name string
|
||||
Direction string
|
||||
RelatedStateVar string
|
||||
}
|
||||
|
||||
type SCPD struct {
|
||||
XMLName xml.Name `xml:"urn:schemas-upnp-org:service-1-0 scpd"`
|
||||
SpecVersion SpecVersion `xml:"specVersion"`
|
||||
ActionList []Action `xml:"actionList>action"`
|
||||
ServiceStateTable []StateVariable `xml:"serviceStateTable>stateVariable"`
|
||||
}
|
||||
|
||||
type StateVariable struct {
|
||||
SendEvents string `xml:"sendEvents,attr"`
|
||||
Name string `xml:"name"`
|
||||
DataType string `xml:"dataType"`
|
||||
AllowedValues *[]string `xml:"allowedValueList>allowedValue,omitempty"`
|
||||
}
|
||||
|
||||
func FormatUUID(buf []byte) string {
|
||||
return fmt.Sprintf("uuid:%x-%x-%x-%x-%x", buf[:4], buf[4:6], buf[6:8], buf[8:10], buf[10:16])
|
||||
}
|
45
vendor/github.com/anacrolix/dms/upnpav/upnpav.go
generated
vendored
Normal file
45
vendor/github.com/anacrolix/dms/upnpav/upnpav.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
package upnpav
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
)
|
||||
|
||||
const (
|
||||
NoSuchObjectErrorCode = 701
|
||||
)
|
||||
|
||||
type Resource struct {
|
||||
XMLName xml.Name `xml:"res"`
|
||||
ProtocolInfo string `xml:"protocolInfo,attr"`
|
||||
URL string `xml:",chardata"`
|
||||
Size uint64 `xml:"size,attr,omitempty"`
|
||||
Bitrate uint `xml:"bitrate,attr,omitempty"`
|
||||
Duration string `xml:"duration,attr,omitempty"`
|
||||
Resolution string `xml:"resolution,attr,omitempty"`
|
||||
}
|
||||
|
||||
type Container struct {
|
||||
Object
|
||||
XMLName xml.Name `xml:"container"`
|
||||
ChildCount int `xml:"childCount,attr"`
|
||||
}
|
||||
|
||||
type Item struct {
|
||||
Object
|
||||
XMLName xml.Name `xml:"item"`
|
||||
Res []Resource
|
||||
}
|
||||
|
||||
type Object struct {
|
||||
ID string `xml:"id,attr"`
|
||||
ParentID string `xml:"parentID,attr"`
|
||||
Restricted int `xml:"restricted,attr"` // indicates whether the object is modifiable
|
||||
Class string `xml:"upnp:class"`
|
||||
Icon string `xml:"upnp:icon,omitempty"`
|
||||
Title string `xml:"dc:title"`
|
||||
Artist string `xml:"upnp:artist,omitempty"`
|
||||
Album string `xml:"upnp:album,omitempty"`
|
||||
Genre string `xml:"upnp:genre,omitempty"`
|
||||
AlbumArtURI string `xml:"upnp:albumArtURI,omitempty"`
|
||||
Searchable int `xml:"searchable,attr"`
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue