/
fileDecoder.go
134 lines (120 loc) · 3.01 KB
/
fileDecoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package goTorrent
//Taipei torrent
import (
"bytes"
"crypto/sha1"
"errors"
bencode "github.com/jackpal/bencode-go"
"os"
"strings"
"io"
)
//FileDict is the struct that contains all the information for a file in the torrent
type FileDict struct {
//m.Info.Files[ind].Path
Length int64
Path []string
Md5sum string
FirstPieceInd int64
FirstOffset int64
LastPieceInd int64
LastByteLast int64
File os.File
}
//InfoDict contains information about the torrent like pieces hashes and length
type InfoDict struct {
PieceLength int64 `bencode:"piece length"`
Pieces string //hashes of all pieces
Private int64
Name string
// Single File Mode
Length int64
Md5sum string
// Multiple File mode
Files []FileDict
}
//MetaInfo contains information for the torrent and the tracker
type MetaInfo struct {
Info InfoDict
InfoHash string
Announce string
AnnounceList [][]string `bencode:"announce-list"`
CreationDate string `bencode:"creation date"`
Comment string
CreatedBy string `bencode:"created by"`
Encoding string
}
func getString(m map[string]interface{}, k string) string {
if v, ok := m[k]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
func getSliceSliceString(m map[string]interface{}, k string) (aas [][]string) {
if a, ok := m[k]; ok {
if b, ok := a.([]interface{}); ok {
for _, c := range b {
if d, ok := c.([]interface{}); ok {
var sliceOfStrings []string
for _, e := range d {
if f, ok := e.(string); ok {
sliceOfStrings = append(sliceOfStrings, f)
}
}
if len(sliceOfStrings) > 0 {
aas = append(aas, sliceOfStrings)
}
}
}
}
}
return
}
func getMetaInfo(torrent string) (metaInfo *MetaInfo, err error) {
var input io.ReadCloser
if input, err = os.Open(torrent); err != nil {
return
}
// We need to calcuate the sha1 of the Info map, including every value in the
// map. The easiest way to do this is to read the data using the Decode
// API, and then pick through it manually.
var m interface{}
m, err = bencode.Decode(input)
input.Close()
if err != nil {
err = errors.New("Couldn't parse torrent file phase 1: " + err.Error())
return
}
topMap, ok := m.(map[string]interface{})
if !ok {
err = errors.New("Couldn't parse torrent file phase 2.")
return
}
infoMap, ok := topMap["info"]
if !ok {
err = errors.New("Couldn't parse torrent file. info")
return
}
var b bytes.Buffer
if err = bencode.Marshal(&b, infoMap); err != nil {
return
}
hash := sha1.New()
hash.Write(b.Bytes())
var m2 MetaInfo
err = bencode.Unmarshal(&b, &m2.Info)
if err != nil {
return
}
m2.InfoHash = string(hash.Sum(nil))
m2.Announce = getString(topMap, "announce")
m2.AnnounceList = getSliceSliceString(topMap, "announce-list")
m2.CreationDate = getString(topMap, "creation date")
m2.Comment = getString(topMap, "comment")
m2.CreatedBy = getString(topMap, "created by")
m2.Encoding = strings.ToUpper(getString(topMap, "encoding"))
metaInfo = &m2
return
}