1 package parser
2
3 import (
4 "fmt"
5 "io"
6 "io/ioutil"
7 "math"
8 "os"
9 "time"
10
11 "github.com/concurrency-8/args"
12 bencode "github.com/zeebo/bencode"
13 )
14
15
16 var BLOCK_LEN = uint32(math.Pow(2, 14))
17
18
19 func Parse(reader io.Reader) (TorrentFile, error) {
20 data, err := ioutil.ReadAll(reader)
21
22 if err != nil {
23 return TorrentFile{}, err
24 }
25
26 metadata := &MetaData{}
27 err = bencode.DecodeBytes(data, metadata)
28
29 if err != nil {
30 return TorrentFile{}, err
31 }
32
33 info := &InfoMetaData{}
34 err = bencode.DecodeBytes(metadata.Info, info)
35
36 if err != nil {
37 return TorrentFile{}, err
38 }
39
40 var Length uint64
41 files := make([]*File, 0)
42
43 os.Mkdir(info.Name, os.ModePerm)
44 if info.Length > 0 {
45 var filePointer *os.File
46 if !args.ARGS.Resume {
47 filePointer, err = os.Create(info.Name + "/" + info.Name)
48 } else {
49 filePointer, err = os.OpenFile(info.Name+"/"+info.Name, os.O_APPEND|os.O_WRONLY, 0600)
50 }
51
52 if err != nil {
53 fmt.Println(err)
54 panic(err)
55 }
56 files = append(files, &File{
57 Path: []string{info.Name},
58 Length: info.Length,
59 FilePointer: filePointer,
60 })
61 Length = info.Length
62 } else {
63
64 metadataFiles := make([]*FileMetaData, 0)
65 err = bencode.DecodeBytes(info.Files, &metadataFiles)
66 if err != nil {
67 return TorrentFile{}, err
68 }
69
70 for _, f := range metadataFiles {
71 var filePointer *os.File
72 if !args.ARGS.Resume {
73 filePointer, err = os.Create(info.Name + "/" + f.Path[0])
74 } else {
75 filePointer, err = os.OpenFile(info.Name+"/"+f.Path[0], os.O_APPEND|os.O_WRONLY, 0600)
76 }
77 if err != nil {
78 fmt.Println(err)
79 panic("Unable to create files ")
80 }
81 files = append(files, &File{
82 Path: []string{info.Name + "/" + f.Path[0]},
83 Length: f.Length,
84 FilePointer: filePointer,
85 })
86 Length += f.Length
87 }
88 }
89
90
91 announces := make([]string, 0)
92
93 if len(metadata.AnnounceList) > 0 {
94 for _, announceItem := range metadata.AnnounceList {
95 for _, announce := range announceItem {
96 announces = append(announces, announce)
97 }
98 }
99 } else {
100 announces = append(announces, metadata.Announce)
101 }
102
103
104 return TorrentFile{
105 Name: info.Name,
106 Announce: announces,
107 Comment: metadata.Comment,
108 CreatedBy: metadata.CreatedBy,
109 CreatedAt: time.Unix(metadata.CreatedAt, 0),
110 InfoHash: toSHA1(metadata.Info),
111 Length: Length,
112 Files: files,
113 PieceLength: info.PieceLength,
114 Piece: info.Piece,
115 }, nil
116 }
117
118
119 func ParseFromFile(path string) (TorrentFile, error) {
120 file, err := os.Open(path)
121 if err != nil {
122 return TorrentFile{}, err
123 }
124
125 defer file.Close()
126
127 return Parse(file)
128 }
129
130
131 func PieceLen(torrent TorrentFile, index uint32) (length uint32, err error) {
132 totalLength := torrent.Length
133 pieceLength := torrent.PieceLength
134 lastPieceLen := uint32(totalLength % uint64(pieceLength))
135 lastPieceIndex := uint32(len(torrent.Piece)/20 - 1)
136
137 if lastPieceLen == 0 {
138 lastPieceLen = pieceLength
139 }
140
141 if lastPieceIndex == index {
142 length = lastPieceLen
143 } else if lastPieceIndex > index {
144 length = pieceLength
145 } else {
146 err = fmt.Errorf("Piece Index out of range")
147 }
148 return
149 }
150
151
152 func BlocksPerPiece(torrent TorrentFile, index uint32) (blocks uint32, err error) {
153 pieceLength, err := PieceLen(torrent, index)
154
155 if err != nil {
156 return
157 }
158 blocks = uint32(math.Ceil(float64(pieceLength) / float64(BLOCK_LEN)))
159 return
160 }
161
162
163 func BlockLen(torrent TorrentFile, pieceIndex uint32, blockIndex uint32) (length uint32, err error) {
164 pieceLength, err := PieceLen(torrent, pieceIndex)
165
166 if err != nil {
167 return
168 }
169
170 lastBlockLength := pieceLength % BLOCK_LEN
171 lastBlockIndex := uint32(math.Ceil(float64(pieceLength)/float64(BLOCK_LEN))) - 1
172
173 if lastBlockLength == 0 {
174 lastBlockLength = BLOCK_LEN
175 }
176
177 if lastBlockIndex == blockIndex {
178 length = lastBlockLength
179 } else if lastBlockIndex > blockIndex {
180 length = BLOCK_LEN
181 } else {
182 err = fmt.Errorf("Block Index out of range")
183 }
184 return
185 }
186
View as plain text