]> Sergey Matveev's repositories - btrtrc.git/blob - metainfo/metainfo.go
Merge pull request #13 from milosgajdos83/metainfo-trackerless
[btrtrc.git] / metainfo / metainfo.go
1 package metainfo
2
3 import (
4         "crypto/sha1"
5         "io"
6         "os"
7
8         "github.com/anacrolix/torrent/bencode"
9 )
10
11 // Information specific to a single file inside the MetaInfo structure.
12 type FileInfo struct {
13         Length int64    `bencode:"length"`
14         Path   []string `bencode:"path"`
15 }
16
17 // Load a MetaInfo from an io.Reader. Returns a non-nil error in case of
18 // failure.
19 func Load(r io.Reader) (*MetaInfo, error) {
20         var mi MetaInfo
21         d := bencode.NewDecoder(r)
22         err := d.Decode(&mi)
23         if err != nil {
24                 return nil, err
25         }
26         return &mi, nil
27 }
28
29 // Convenience function for loading a MetaInfo from a file.
30 func LoadFromFile(filename string) (*MetaInfo, error) {
31         f, err := os.Open(filename)
32         if err != nil {
33                 return nil, err
34         }
35         defer f.Close()
36         return Load(f)
37 }
38
39 // The info dictionary.
40 type Info struct {
41         PieceLength int64      `bencode:"piece length"`
42         Pieces      []byte     `bencode:"pieces"`
43         Name        string     `bencode:"name"`
44         Length      int64      `bencode:"length,omitempty"`
45         Private     bool       `bencode:"private,omitempty"`
46         Files       []FileInfo `bencode:"files,omitempty"`
47 }
48
49 func (me *Info) TotalLength() (ret int64) {
50         if me.IsDir() {
51                 for _, fi := range me.Files {
52                         ret += fi.Length
53                 }
54         } else {
55                 ret = me.Length
56         }
57         return
58 }
59
60 func (me *Info) NumPieces() int {
61         return len(me.Pieces) / 20
62 }
63
64 type Piece interface {
65         Hash() []byte
66         Length() int64
67         Offset() int64
68 }
69
70 type piece struct {
71         Info *Info
72         i    int
73 }
74
75 func (me piece) Length() int64 {
76         if me.i == me.Info.NumPieces()-1 {
77                 return me.Info.TotalLength() - int64(me.i)*me.Info.PieceLength
78         }
79         return me.Info.PieceLength
80 }
81
82 func (me piece) Offset() int64 {
83         return int64(me.i) * me.Info.PieceLength
84 }
85
86 func (me piece) Hash() []byte {
87         return me.Info.Pieces[me.i*20 : (me.i+1)*20]
88 }
89
90 func (me *Info) Piece(i int) piece {
91         return piece{me, i}
92 }
93
94 func (i *Info) IsDir() bool {
95         return len(i.Files) != 0
96 }
97
98 // The files field, converted up from the old single-file in the parent info
99 // dict if necessary. This is a helper to avoid having to conditionally handle
100 // single and multi-file torrent infos.
101 func (i *Info) UpvertedFiles() []FileInfo {
102         if len(i.Files) == 0 {
103                 return []FileInfo{{
104                         Length: i.Length,
105                         // Callers should determine that Info.Name is the basename, and
106                         // thus a regular file.
107                         Path: nil,
108                 }}
109         }
110         return i.Files
111 }
112
113 // The info dictionary with its hash and raw bytes exposed, as these are
114 // important to Bittorrent.
115 type InfoEx struct {
116         Info
117         Hash  []byte
118         Bytes []byte
119 }
120
121 var (
122         _ bencode.Marshaler   = InfoEx{}
123         _ bencode.Unmarshaler = &InfoEx{}
124 )
125
126 func (this *InfoEx) UnmarshalBencode(data []byte) error {
127         this.Bytes = make([]byte, 0, len(data))
128         this.Bytes = append(this.Bytes, data...)
129         h := sha1.New()
130         _, err := h.Write(this.Bytes)
131         if err != nil {
132                 panic(err)
133         }
134         this.Hash = h.Sum(nil)
135         return bencode.Unmarshal(data, &this.Info)
136 }
137
138 func (this InfoEx) MarshalBencode() ([]byte, error) {
139         if this.Bytes != nil {
140                 return this.Bytes, nil
141         }
142         return bencode.Marshal(&this.Info)
143 }
144
145 type MetaInfo struct {
146         Info         InfoEx      `bencode:"info"`
147         Announce     string      `bencode:"announce,omitempty"`
148         AnnounceList [][]string  `bencode:"announce-list,omitempty"`
149         Nodes        [][]string  `bencode:"nodes,omitempty"`
150         CreationDate int64       `bencode:"creation date,omitempty"`
151         Comment      string      `bencode:"comment,omitempty"`
152         CreatedBy    string      `bencode:"created by,omitempty"`
153         Encoding     string      `bencode:"encoding,omitempty"`
154         URLList      interface{} `bencode:"url-list,omitempty"`
155 }