]> Sergey Matveev's repositories - btrtrc.git/blob - metainfo/magnet.go
Merge pull request #99 from hustcat/magnet
[btrtrc.git] / metainfo / magnet.go
1 package metainfo
2
3 import (
4         "encoding/base32"
5         "encoding/hex"
6         "fmt"
7         "net/url"
8         "strings"
9 )
10
11 // Magnet link components.
12 type Magnet struct {
13         InfoHash    Hash
14         Trackers    []string
15         DisplayName string
16 }
17
18 const xtPrefix = "urn:btih:"
19
20 func (m Magnet) String() string {
21         // net.URL likes to assume //, and encodes ':' on us, so we do most of
22         // this manually.
23         ret := "magnet:?xt="
24         ret += xtPrefix + hex.EncodeToString(m.InfoHash[:])
25         if m.DisplayName != "" {
26                 ret += "&dn=" + url.QueryEscape(m.DisplayName)
27         }
28         for _, tr := range m.Trackers {
29                 ret += "&tr=" + url.QueryEscape(tr)
30         }
31         return ret
32 }
33
34 // ParseMagnetURI parses Magnet-formatted URIs into a Magnet instance
35 func ParseMagnetURI(uri string) (m Magnet, err error) {
36         u, err := url.Parse(uri)
37         if err != nil {
38                 err = fmt.Errorf("error parsing uri: %s", err)
39                 return
40         }
41         if u.Scheme != "magnet" {
42                 err = fmt.Errorf("unexpected scheme: %q", u.Scheme)
43                 return
44         }
45         xt := u.Query().Get("xt")
46         if !strings.HasPrefix(xt, xtPrefix) {
47                 err = fmt.Errorf("bad xt parameter")
48                 return
49         }
50         infoHash := xt[len(xtPrefix):]
51
52         // BTIH hash can be in HEX or BASE32 encoding
53         // will assign apropriate func judging from symbol length
54         var decode func(dst, src []byte) (int, error)
55         switch len(infoHash) {
56         case 40:
57                 decode = hex.Decode
58         case 32:
59                 decode = base32.StdEncoding.Decode
60         }
61
62         if decode == nil {
63                 err = fmt.Errorf("unhandled xt parameter encoding: encoded length %d", len(infoHash))
64                 return
65         }
66         n, err := decode(m.InfoHash[:], []byte(infoHash))
67         if err != nil {
68                 err = fmt.Errorf("error decoding xt: %s", err)
69                 return
70         }
71         if n != 20 {
72                 panic(n)
73         }
74         m.DisplayName = u.Query().Get("dn")
75         m.Trackers = u.Query()["tr"]
76         return
77 }