mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-21 14:11:56 +00:00

goupnp is an existing upnp client for go, which provides all the functionality we need, licensed under BSD-2-Clause, so we can copy it over and modify parts of it for our case. Specifically, we add contexts to all the methods so we can better handle timeouts, remove the dependency on large charsets, and (eventually) trim out extra components we don't need. Signed-off-by: julianknodt <julianknodt@gmail.com>
89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/zip"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
)
|
|
|
|
func acquireFile(specFilename string, xmlSpecURL string) error {
|
|
if f, err := os.Open(specFilename); err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return err
|
|
}
|
|
} else {
|
|
f.Close()
|
|
return nil
|
|
}
|
|
|
|
resp, err := http.Get(xmlSpecURL)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("could not download spec %q from %q: %s",
|
|
specFilename, xmlSpecURL, resp.Status)
|
|
}
|
|
|
|
tmpFilename := specFilename + ".download"
|
|
w, err := os.Create(tmpFilename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer w.Close()
|
|
|
|
_, err = io.Copy(w, resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.Rename(tmpFilename, specFilename)
|
|
}
|
|
|
|
func globFiles(pattern string, archive *zip.ReadCloser) []*zip.File {
|
|
var files []*zip.File
|
|
for _, f := range archive.File {
|
|
if matched, err := path.Match(pattern, f.Name); err != nil {
|
|
// This shouldn't happen - all patterns are hard-coded, errors in them
|
|
// are a programming error.
|
|
panic(err)
|
|
} else if matched {
|
|
files = append(files, f)
|
|
}
|
|
}
|
|
return files
|
|
}
|
|
|
|
func unmarshalXmlFile(file *zip.File, data interface{}) error {
|
|
r, err := file.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
decoder := xml.NewDecoder(r)
|
|
defer r.Close()
|
|
return decoder.Decode(data)
|
|
}
|
|
|
|
var scpdFilenameRe = regexp.MustCompile(
|
|
`.*/([a-zA-Z0-9]+)([0-9]+)\.xml`)
|
|
|
|
func urnPartsFromSCPDFilename(filename string) (*URNParts, error) {
|
|
parts := scpdFilenameRe.FindStringSubmatch(filename)
|
|
if len(parts) != 3 {
|
|
return nil, fmt.Errorf("SCPD filename %q does not have expected number of parts", filename)
|
|
}
|
|
name, version := parts[1], parts[2]
|
|
return &URNParts{
|
|
URN: serviceURNPrefix + name + ":" + version,
|
|
Name: name,
|
|
Version: version,
|
|
}, nil
|
|
}
|