This commit is contained in:
2018-11-04 15:58:15 +01:00
commit f956bcee28
1178 changed files with 584552 additions and 0 deletions

1
vendor/github.com/anacrolix/torrent/metainfo/README generated vendored Normal file
View File

@@ -0,0 +1 @@
A library for manipulating ".torrent" files.

View File

@@ -0,0 +1,27 @@
package metainfo
type AnnounceList [][]string
// Whether the AnnounceList should be preferred over a single URL announce.
func (al AnnounceList) OverridesAnnounce(announce string) bool {
for _, tier := range al {
for _, url := range tier {
if url != "" || announce == "" {
return true
}
}
}
return false
}
func (al AnnounceList) DistinctValues() (ret map[string]struct{}) {
for _, tier := range al {
for _, v := range tier {
if ret == nil {
ret = make(map[string]struct{})
}
ret[v] = struct{}{}
}
}
return
}

View File

@@ -0,0 +1,27 @@
package metainfo
import "strings"
// Information specific to a single file inside the MetaInfo structure.
type FileInfo struct {
Length int64 `bencode:"length"`
Path []string `bencode:"path"`
}
func (fi *FileInfo) DisplayPath(info *Info) string {
if info.IsDir() {
return strings.Join(fi.Path, "/")
} else {
return info.Name
}
}
func (me FileInfo) Offset(info *Info) (ret int64) {
for _, fi := range info.UpvertedFiles() {
if me.DisplayPath(info) == fi.DisplayPath(info) {
return
}
ret += fi.Length
}
panic("not found")
}

58
vendor/github.com/anacrolix/torrent/metainfo/hash.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package metainfo
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
const HashSize = 20
// 20-byte SHA1 hash used for info and pieces.
type Hash [HashSize]byte
func (h Hash) Bytes() []byte {
return h[:]
}
func (h Hash) AsString() string {
return string(h[:])
}
func (h Hash) String() string {
return h.HexString()
}
func (h Hash) HexString() string {
return fmt.Sprintf("%x", h[:])
}
func (h *Hash) FromHexString(s string) (err error) {
if len(s) != 2*HashSize {
err = fmt.Errorf("hash hex string has bad length: %d", len(s))
return
}
n, err := hex.Decode(h[:], []byte(s))
if err != nil {
return
}
if n != HashSize {
panic(n)
}
return
}
func NewHashFromHex(s string) (h Hash) {
err := h.FromHexString(s)
if err != nil {
panic(err)
}
return
}
func HashBytes(b []byte) (ret Hash) {
hasher := sha1.New()
hasher.Write(b)
copy(ret[:], hasher.Sum(nil))
return
}

156
vendor/github.com/anacrolix/torrent/metainfo/info.go generated vendored Normal file
View File

@@ -0,0 +1,156 @@
package metainfo
import (
"crypto/sha1"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"github.com/anacrolix/missinggo/slices"
)
// The info dictionary.
type Info struct {
PieceLength int64 `bencode:"piece length"`
Pieces []byte `bencode:"pieces"`
Name string `bencode:"name"`
Length int64 `bencode:"length,omitempty"`
Private *bool `bencode:"private,omitempty"`
// TODO: Document this field.
Source string `bencode:"source,omitempty"`
Files []FileInfo `bencode:"files,omitempty"`
}
// This is a helper that sets Files and Pieces from a root path and its
// children.
func (info *Info) BuildFromFilePath(root string) (err error) {
info.Name = filepath.Base(root)
info.Files = nil
err = filepath.Walk(root, func(path string, fi os.FileInfo, err error) error {
if err != nil {
return err
}
if fi.IsDir() {
// Directories are implicit in torrent files.
return nil
} else if path == root {
// The root is a file.
info.Length = fi.Size()
return nil
}
relPath, err := filepath.Rel(root, path)
if err != nil {
return fmt.Errorf("error getting relative path: %s", err)
}
info.Files = append(info.Files, FileInfo{
Path: strings.Split(relPath, string(filepath.Separator)),
Length: fi.Size(),
})
return nil
})
if err != nil {
return
}
slices.Sort(info.Files, func(l, r FileInfo) bool {
return strings.Join(l.Path, "/") < strings.Join(r.Path, "/")
})
err = info.GeneratePieces(func(fi FileInfo) (io.ReadCloser, error) {
return os.Open(filepath.Join(root, strings.Join(fi.Path, string(filepath.Separator))))
})
if err != nil {
err = fmt.Errorf("error generating pieces: %s", err)
}
return
}
// Concatenates all the files in the torrent into w. open is a function that
// gets at the contents of the given file.
func (info *Info) writeFiles(w io.Writer, open func(fi FileInfo) (io.ReadCloser, error)) error {
for _, fi := range info.UpvertedFiles() {
r, err := open(fi)
if err != nil {
return fmt.Errorf("error opening %v: %s", fi, err)
}
wn, err := io.CopyN(w, r, fi.Length)
r.Close()
if wn != fi.Length {
return fmt.Errorf("error copying %v: %s", fi, err)
}
}
return nil
}
// Sets Pieces (the block of piece hashes in the Info) by using the passed
// function to get at the torrent data.
func (info *Info) GeneratePieces(open func(fi FileInfo) (io.ReadCloser, error)) error {
if info.PieceLength == 0 {
return errors.New("piece length must be non-zero")
}
pr, pw := io.Pipe()
go func() {
err := info.writeFiles(pw, open)
pw.CloseWithError(err)
}()
defer pr.Close()
var pieces []byte
for {
hasher := sha1.New()
wn, err := io.CopyN(hasher, pr, info.PieceLength)
if err == io.EOF {
err = nil
}
if err != nil {
return err
}
if wn == 0 {
break
}
pieces = hasher.Sum(pieces)
if wn < info.PieceLength {
break
}
}
info.Pieces = pieces
return nil
}
func (info *Info) TotalLength() (ret int64) {
if info.IsDir() {
for _, fi := range info.Files {
ret += fi.Length
}
} else {
ret = info.Length
}
return
}
func (info *Info) NumPieces() int {
return len(info.Pieces) / 20
}
func (info *Info) IsDir() bool {
return len(info.Files) != 0
}
// The files field, converted up from the old single-file in the parent info
// dict if necessary. This is a helper to avoid having to conditionally handle
// single and multi-file torrent infos.
func (info *Info) UpvertedFiles() []FileInfo {
if len(info.Files) == 0 {
return []FileInfo{{
Length: info.Length,
// Callers should determine that Info.Name is the basename, and
// thus a regular file.
Path: nil,
}}
}
return info.Files
}
func (info *Info) Piece(index int) Piece {
return Piece{info, pieceIndex(index)}
}

77
vendor/github.com/anacrolix/torrent/metainfo/magnet.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
package metainfo
import (
"encoding/base32"
"encoding/hex"
"fmt"
"net/url"
"strings"
)
// Magnet link components.
type Magnet struct {
InfoHash Hash
Trackers []string
DisplayName string
}
const xtPrefix = "urn:btih:"
func (m Magnet) String() string {
// net.URL likes to assume //, and encodes ':' on us, so we do most of
// this manually.
ret := "magnet:?xt="
ret += xtPrefix + hex.EncodeToString(m.InfoHash[:])
if m.DisplayName != "" {
ret += "&dn=" + url.QueryEscape(m.DisplayName)
}
for _, tr := range m.Trackers {
ret += "&tr=" + url.QueryEscape(tr)
}
return ret
}
// ParseMagnetURI parses Magnet-formatted URIs into a Magnet instance
func ParseMagnetURI(uri string) (m Magnet, err error) {
u, err := url.Parse(uri)
if err != nil {
err = fmt.Errorf("error parsing uri: %s", err)
return
}
if u.Scheme != "magnet" {
err = fmt.Errorf("unexpected scheme: %q", u.Scheme)
return
}
xt := u.Query().Get("xt")
if !strings.HasPrefix(xt, xtPrefix) {
err = fmt.Errorf("bad xt parameter")
return
}
infoHash := xt[len(xtPrefix):]
// BTIH hash can be in HEX or BASE32 encoding
// will assign appropriate func judging from symbol length
var decode func(dst, src []byte) (int, error)
switch len(infoHash) {
case 40:
decode = hex.Decode
case 32:
decode = base32.StdEncoding.Decode
}
if decode == nil {
err = fmt.Errorf("unhandled xt parameter encoding: encoded length %d", len(infoHash))
return
}
n, err := decode(m.InfoHash[:], []byte(infoHash))
if err != nil {
err = fmt.Errorf("error decoding xt: %s", err)
return
}
if n != 20 {
panic(n)
}
m.DisplayName = u.Query().Get("dn")
m.Trackers = u.Query()["tr"]
return
}

View File

@@ -0,0 +1,87 @@
package metainfo
import (
"io"
"os"
"time"
"github.com/anacrolix/torrent/bencode"
)
type MetaInfo struct {
InfoBytes bencode.Bytes `bencode:"info,omitempty"`
Announce string `bencode:"announce,omitempty"`
AnnounceList AnnounceList `bencode:"announce-list,omitempty"`
Nodes []Node `bencode:"nodes,omitempty"`
CreationDate int64 `bencode:"creation date,omitempty,ignore_unmarshal_type_error"`
Comment string `bencode:"comment,omitempty"`
CreatedBy string `bencode:"created by,omitempty"`
Encoding string `bencode:"encoding,omitempty"`
UrlList UrlList `bencode:"url-list,omitempty"`
}
// Load a MetaInfo from an io.Reader. Returns a non-nil error in case of
// failure.
func Load(r io.Reader) (*MetaInfo, error) {
var mi MetaInfo
d := bencode.NewDecoder(r)
err := d.Decode(&mi)
if err != nil {
return nil, err
}
return &mi, nil
}
// Convenience function for loading a MetaInfo from a file.
func LoadFromFile(filename string) (*MetaInfo, error) {
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
return Load(f)
}
func (mi MetaInfo) UnmarshalInfo() (info Info, err error) {
err = bencode.Unmarshal(mi.InfoBytes, &info)
return
}
func (mi MetaInfo) HashInfoBytes() (infoHash Hash) {
return HashBytes(mi.InfoBytes)
}
// Encode to bencoded form.
func (mi MetaInfo) Write(w io.Writer) error {
return bencode.NewEncoder(w).Encode(mi)
}
// Set good default values in preparation for creating a new MetaInfo file.
func (mi *MetaInfo) SetDefaults() {
mi.Comment = "yoloham"
mi.CreatedBy = "github.com/anacrolix/torrent"
mi.CreationDate = time.Now().Unix()
// mi.Info.PieceLength = 256 * 1024
}
// Creates a Magnet from a MetaInfo.
func (mi *MetaInfo) Magnet(displayName string, infoHash Hash) (m Magnet) {
for t := range mi.UpvertedAnnounceList().DistinctValues() {
m.Trackers = append(m.Trackers, t)
}
m.DisplayName = displayName
m.InfoHash = infoHash
return
}
// Returns the announce list converted from the old single announce field if
// necessary.
func (mi *MetaInfo) UpvertedAnnounceList() AnnounceList {
if mi.AnnounceList.OverridesAnnounce(mi.Announce) {
return mi.AnnounceList
}
if mi.Announce != "" {
return [][]string{[]string{mi.Announce}}
}
return nil
}

40
vendor/github.com/anacrolix/torrent/metainfo/nodes.go generated vendored Normal file
View File

@@ -0,0 +1,40 @@
package metainfo
import (
"fmt"
"net"
"strconv"
"github.com/anacrolix/torrent/bencode"
)
type Node string
var (
_ bencode.Unmarshaler = new(Node)
)
func (n *Node) UnmarshalBencode(b []byte) (err error) {
var iface interface{}
err = bencode.Unmarshal(b, &iface)
if err != nil {
return
}
switch v := iface.(type) {
case string:
*n = Node(v)
case []interface{}:
func() {
defer func() {
r := recover()
if r != nil {
err = r.(error)
}
}()
*n = Node(net.JoinHostPort(v[0].(string), strconv.FormatInt(v[1].(int64), 10)))
}()
default:
err = fmt.Errorf("unsupported type: %T", iface)
}
return
}

32
vendor/github.com/anacrolix/torrent/metainfo/piece.go generated vendored Normal file
View File

@@ -0,0 +1,32 @@
package metainfo
import (
"github.com/anacrolix/missinggo"
)
type Piece struct {
Info *Info
i pieceIndex
}
type pieceIndex = int
func (p Piece) Length() int64 {
if int(p.i) == p.Info.NumPieces()-1 {
return p.Info.TotalLength() - int64(p.i)*p.Info.PieceLength
}
return p.Info.PieceLength
}
func (p Piece) Offset() int64 {
return int64(p.i) * p.Info.PieceLength
}
func (p Piece) Hash() (ret Hash) {
missinggo.CopyExact(&ret, p.Info.Pieces[p.i*HashSize:(p.i+1)*HashSize])
return
}
func (p Piece) Index() pieceIndex {
return p.i
}

View File

@@ -0,0 +1,7 @@
package metainfo
// Uniquely identifies a piece.
type PieceKey struct {
InfoHash Hash
Index pieceIndex
}

View File

@@ -0,0 +1,27 @@
package metainfo
import (
"github.com/anacrolix/torrent/bencode"
)
type UrlList []string
var (
_ bencode.Unmarshaler = (*UrlList)(nil)
)
func (me *UrlList) UnmarshalBencode(b []byte) error {
if len(b) == 0 {
return nil
}
if b[0] == 'l' {
var l []string
err := bencode.Unmarshal(b, &l)
*me = l
return err
}
var s string
err := bencode.Unmarshal(b, &s)
*me = []string{s}
return err
}