vendor: add github.com/anacrolix/dms

This commit is contained in:
nicolov 2019-01-03 23:23:39 +01:00 committed by Nick Craig-Wood
parent 7ee7bc87ae
commit 5edfd31a6d
183 changed files with 12766 additions and 0 deletions

24
vendor/github.com/anacrolix/dms/LICENSE generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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"`
}