]> Sergey Matveev's repositories - btrtrc.git/blob - metainfo/metainfo.go
Rename torrent package to metainfo; expose MetaInfo["info"] so it can be used on...
[btrtrc.git] / metainfo / metainfo.go
1 package metainfo
2
3 import (
4         "crypto/sha1"
5         "errors"
6         "github.com/nsf/libtorgo/bencode"
7         "io"
8         "os"
9         "time"
10 )
11
12 // Information specific to a single file inside the MetaInfo structure..
13 type FileInfo struct {
14         Length int64    `bencode:"length"`
15         Path   []string `bencode:"path"`
16 }
17
18 // MetaInfo is the type you should use when reading torrent files. See Load and
19 // LoadFromFile functions. All the fields are intended to be read-only. If
20 // 'len(Files) == 1', then the FileInfo.Path is nil in that entry.
21 type MetaInfo struct {
22         Info
23         InfoHash     []byte
24         AnnounceList [][]string
25         CreationDate time.Time
26         Comment      string
27         CreatedBy    string
28         Encoding     string
29         WebSeedURLs  []string
30         InfoBytes    []byte
31 }
32
33 // Load a MetaInfo from an io.Reader. Returns a non-nil error in case of
34 // failure.
35 func Load(r io.Reader) (*MetaInfo, error) {
36         var mi MetaInfo
37         var data torrent_data
38         d := bencode.NewDecoder(r)
39         err := d.Decode(&data)
40         if err != nil {
41                 return nil, err
42         }
43
44         mi.Info = data.Info.Info
45         mi.InfoBytes = data.Info.Bytes
46         mi.InfoHash = data.Info.Hash
47         if len(data.AnnounceList) > 0 {
48                 mi.AnnounceList = data.AnnounceList
49         } else {
50                 mi.AnnounceList = [][]string{[]string{data.Announce}}
51         }
52         mi.CreationDate = time.Unix(data.CreationDate, 0)
53         mi.Comment = data.Comment
54         mi.CreatedBy = data.CreatedBy
55         mi.Encoding = data.Encoding
56         if data.URLList != nil {
57                 switch v := data.URLList.(type) {
58                 case string:
59                         mi.WebSeedURLs = []string{v}
60                 case []interface{}:
61                         var ok bool
62                         ss := make([]string, len(v))
63                         for i, s := range v {
64                                 ss[i], ok = s.(string)
65                                 if !ok {
66                                         return nil, errors.New("bad url-list data type")
67                                 }
68                         }
69                         mi.WebSeedURLs = ss
70                 default:
71                         return nil, errors.New("bad url-list data type")
72                 }
73         }
74         return &mi, nil
75 }
76
77 // Convenience function for loading a MetaInfo from a file.
78 func LoadFromFile(filename string) (*MetaInfo, error) {
79         f, err := os.Open(filename)
80         if err != nil {
81                 return nil, err
82         }
83         defer f.Close()
84         return Load(f)
85 }
86
87 type Info struct {
88         PieceLength int64      `bencode:"piece length"`
89         Pieces      []byte     `bencode:"pieces"`
90         Name        string     `bencode:"name"`
91         Length      int64      `bencode:"length,omitempty"`
92         Private     bool       `bencode:"private,omitempty"`
93         Files       []FileInfo `bencode:"files,omitempty"`
94 }
95
96 //----------------------------------------------------------------------------
97 // unmarshal structures
98 //----------------------------------------------------------------------------
99
100 type torrent_info_ex struct {
101         Info
102         Hash  []byte
103         Bytes []byte
104 }
105
106 func (this *torrent_info_ex) UnmarshalBencode(data []byte) error {
107         this.Bytes = make([]byte, 0, len(data))
108         this.Bytes = append(this.Bytes, data...)
109         h := sha1.New()
110         h.Write(this.Bytes)
111         this.Hash = h.Sum(this.Hash)
112         return bencode.Unmarshal(data, &this.Info)
113 }
114
115 func (this *torrent_info_ex) MarshalBencode() ([]byte, error) {
116         return bencode.Marshal(&this.Info)
117 }
118
119 type torrent_data struct {
120         Info         torrent_info_ex `bencode:"info"`
121         Announce     string          `bencode:"announce"`
122         AnnounceList [][]string      `bencode:"announce-list,omitempty"`
123         CreationDate int64           `bencode:"creation date,omitempty"`
124         Comment      string          `bencode:"comment,omitempty"`
125         CreatedBy    string          `bencode:"created by,omitempty"`
126         Encoding     string          `bencode:"encoding,omitempty"`
127         URLList      interface{}     `bencode:"url-list,omitempty"`
128 }