aboutsummaryrefslogtreecommitdiff
path: root/models/torrent.go
blob: 6cae23c3d784962495a2ea181665037673c72f02 (plain)
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
package models

import (
	"bytes"
	"crypto/sha1"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/felix/dhtsearch/bencode"
	"github.com/felix/dhtsearch/krpc"
)

// Data for persistent storage
type Torrent struct {
	ID       int       `json:"-"`
	Infohash Infohash  `json:"infohash"`
	Name     string    `json:"name"`
	Files    []File    `json:"files" db:"-"`
	Size     int       `json:"size"`
	Updated  time.Time `json:"updated"`
	Created  time.Time `json:"created"`
	Tags     []string  `json:"tags" db:"-"`
}

type File struct {
	ID        int    `json:"-"`
	Path      string `json:"path"`
	Size      int    `json:"size"`
	TorrentID int    `json:"torrent_id" db:"torrent_id"`
}

func InfohashMatchesMetadata(ih Infohash, md []byte) bool {
	info := sha1.Sum(md)
	return bytes.Equal([]byte(ih), info[:])
}

func TorrentFromMetadata(ih Infohash, md []byte) (*Torrent, error) {
	if !InfohashMatchesMetadata(ih, md) {
		return nil, fmt.Errorf("infohash does not match metadata")
	}
	info, _, err := bencode.DecodeDict(md, 0)
	if err != nil {
		return nil, err
	}

	// Get the directory or advisory filename
	name, err := krpc.GetString(info, "name")
	if err != nil {
		return nil, err
	}

	bt := Torrent{
		Infohash: ih,
		Name:     name,
	}

	if files, err := krpc.GetList(info, "files"); err == nil {
		// Multiple file mode
		bt.Files = make([]File, len(files))

		// Files is a list of dicts
		for i, item := range files {
			file := item.(map[string]interface{})

			// Paths is a list of strings
			paths := file["path"].([]interface{})
			path := make([]string, len(paths))
			for j, p := range paths {
				path[j] = p.(string)
			}

			fSize, err := krpc.GetInt(file, "length")
			if err != nil {
				return nil, err
			}
			bt.Files[i] = File{
				// Assume Unix path sep?
				Path: strings.Join(path[:], string(os.PathSeparator)),
				Size: fSize,
			}
			// Ensure the torrent size totals all files'
			bt.Size = bt.Size + fSize
		}
	} else if length, err := krpc.GetInt(info, "length"); err == nil {
		// Single file mode
		bt.Size = length
	} else {
		return nil, fmt.Errorf("found neither length or files")
	}
	return &bt, nil
}