]> Sergey Matveev's repositories - btrtrc.git/blob - metainfo/metainfo.go
Fixes for single/multi-file torrent Infos
[btrtrc.git] / metainfo / metainfo.go
1 package metainfo
2
3 import (
4         "crypto/sha1"
5         "io"
6         "os"
7
8         "github.com/anacrolix/libtorgo/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 (i *Info) IsDir() bool {
50         return len(i.Files) != 0
51 }
52
53 // The files field, converted up from the old single-file in the parent info
54 // dict if necessary. This is a helper to avoid having to conditionally handle
55 // single and multi-file torrent infos.
56 func (i *Info) UpvertedFiles() []FileInfo {
57         if len(i.Files) == 0 {
58                 return []FileInfo{{
59                         Length: i.Length,
60                         // Callers should determine that Info.Name is the basename, and
61                         // thus a regular file.
62                         Path: nil,
63                 }}
64         }
65         return i.Files
66 }
67
68 // The info dictionary with its hash and raw bytes exposed, as these are
69 // important to Bittorrent.
70 type InfoEx struct {
71         Info
72         Hash  []byte
73         Bytes []byte
74 }
75
76 var (
77         _ bencode.Marshaler   = InfoEx{}
78         _ bencode.Unmarshaler = &InfoEx{}
79 )
80
81 func (this *InfoEx) UnmarshalBencode(data []byte) error {
82         this.Bytes = make([]byte, 0, len(data))
83         this.Bytes = append(this.Bytes, data...)
84         h := sha1.New()
85         _, err := h.Write(this.Bytes)
86         if err != nil {
87                 panic(err)
88         }
89         this.Hash = h.Sum(nil)
90         return bencode.Unmarshal(data, &this.Info)
91 }
92
93 func (this InfoEx) MarshalBencode() ([]byte, error) {
94         if this.Bytes != nil {
95                 return this.Bytes, nil
96         }
97         return bencode.Marshal(&this.Info)
98 }
99
100 type MetaInfo struct {
101         Info         InfoEx      `bencode:"info"`
102         Announce     string      `bencode:"announce"`
103         AnnounceList [][]string  `bencode:"announce-list,omitempty"`
104         CreationDate int64       `bencode:"creation date,omitempty"`
105         Comment      string      `bencode:"comment,omitempty"`
106         CreatedBy    string      `bencode:"created by,omitempty"`
107         Encoding     string      `bencode:"encoding,omitempty"`
108         URLList      interface{} `bencode:"url-list,omitempty"`
109 }