]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Add first bits of "torrent" library (.torrent file manipulation).
authornsf <no.smile.face@gmail.com>
Wed, 27 Jun 2012 20:26:56 +0000 (02:26 +0600)
committernsf <no.smile.face@gmail.com>
Wed, 27 Jun 2012 20:26:56 +0000 (02:26 +0600)
Add missing testdata for bencode also.

bencode/_testdata/continuum.torrent [new file with mode: 0644]
torrent/README [new file with mode: 0644]
torrent/_testdata/archlinux-2011.08.19-netinstall-i686.iso.torrent [new file with mode: 0644]
torrent/_testdata/continuum.torrent [new file with mode: 0644]
torrent/file.go [new file with mode: 0644]
torrent/file_test.go [new file with mode: 0644]

diff --git a/bencode/_testdata/continuum.torrent b/bencode/_testdata/continuum.torrent
new file mode 100644 (file)
index 0000000..ac15b75
Binary files /dev/null and b/bencode/_testdata/continuum.torrent differ
diff --git a/torrent/README b/torrent/README
new file mode 100644 (file)
index 0000000..6da37b8
--- /dev/null
@@ -0,0 +1 @@
+A library for manipulating ".torrent" files.
diff --git a/torrent/_testdata/archlinux-2011.08.19-netinstall-i686.iso.torrent b/torrent/_testdata/archlinux-2011.08.19-netinstall-i686.iso.torrent
new file mode 100644 (file)
index 0000000..9ce7748
Binary files /dev/null and b/torrent/_testdata/archlinux-2011.08.19-netinstall-i686.iso.torrent differ
diff --git a/torrent/_testdata/continuum.torrent b/torrent/_testdata/continuum.torrent
new file mode 100644 (file)
index 0000000..ac15b75
Binary files /dev/null and b/torrent/_testdata/continuum.torrent differ
diff --git a/torrent/file.go b/torrent/file.go
new file mode 100644 (file)
index 0000000..82b776a
--- /dev/null
@@ -0,0 +1,167 @@
+package torrent
+
+import (
+       "crypto/sha1"
+       "errors"
+       "github.com/nsf/libtorgo/bencode"
+       "io"
+       "os"
+       "time"
+)
+
+//----------------------------------------------------------------------------
+
+type SingleFile struct {
+       Name   string
+       Length int64
+}
+
+//----------------------------------------------------------------------------
+
+type MultiFile struct {
+       Name  string
+       Files []FileInfo
+}
+
+type FileInfo struct {
+       Length int64
+       Path   []string
+}
+
+//----------------------------------------------------------------------------
+
+type File struct {
+       // this will be returned as SingleFile or MultiFile, see Info method
+       name   string
+       length int64
+       files  []FileInfo
+
+       InfoHash    []byte
+       PieceLength int64
+       Pieces      []byte
+       Private     bool
+
+       AnnounceList [][]string
+       CreationDate time.Time
+       Comment      string
+       CreatedBy    string
+       Encoding     string
+       URLList      []string
+}
+
+// the real type of this return value is SingleFile or MultiFile and it must be
+// checked by an API user
+func (f *File) Info() interface{} {
+       if len(f.files) > 0 {
+               return MultiFile{Name: f.name, Files: f.files}
+       }
+       return SingleFile{Name: f.name, Length: f.length}
+}
+
+func Open(r io.Reader) (*File, error) {
+       var file File
+       var data torrent_data
+       d := bencode.NewDecoder(r)
+       err := d.Decode(&data)
+       if err != nil {
+               return nil, err
+       }
+
+       // post-parse processing
+       file.name = data.Info.Name
+       file.length = data.Info.Length
+       if len(data.Info.Files) > 0 {
+               file.files = make([]FileInfo, len(data.Info.Files))
+               for i, fi := range data.Info.Files {
+                       file.files[i] = FileInfo{
+                               Length: fi.Length,
+                               Path:   fi.Path,
+                       }
+               }
+       }
+       file.InfoHash = data.Info.Hash
+       file.PieceLength = data.Info.PieceLength
+       file.Pieces = data.Info.Pieces
+       file.Private = data.Info.Private
+
+       if len(data.AnnounceList) > 0 {
+               file.AnnounceList = data.AnnounceList
+       } else {
+               file.AnnounceList = [][]string{[]string{data.Announce}}
+       }
+       file.CreationDate = time.Unix(data.CreationDate, 0)
+       file.Comment = data.Comment
+       file.CreatedBy = data.CreatedBy
+       file.Encoding = data.Encoding
+       if data.URLList != nil {
+               switch v := data.URLList.(type) {
+               case string:
+                       file.URLList = []string{v}
+               case []interface{}:
+                       var ok bool
+                       ss := make([]string, len(v))
+                       for i, s := range v {
+                               ss[i], ok = s.(string)
+                               if !ok {
+                                       return nil, errors.New("bad url-list data type")
+                               }
+                       }
+                       file.URLList = ss
+               default:
+                       return nil, errors.New("bad url-list data type")
+               }
+       }
+       return &file, nil
+}
+
+func OpenFromFile(filename string) (*File, error) {
+       f, err := os.Open(filename)
+       if err != nil {
+               return nil, err
+       }
+       defer f.Close()
+       return Open(f)
+}
+
+//----------------------------------------------------------------------------
+// unmarshal structures
+//----------------------------------------------------------------------------
+
+type torrent_info_file struct {
+       Path   []string `bencode:"path"`
+       Length int64    `bencode:"length"`
+       MD5Sum []byte   `bencode:"md5sum,omitempty"`
+}
+
+type torrent_info struct {
+       PieceLength int64               `bencode:"piece length"`
+       Pieces      []byte              `bencode:"pieces"`
+       Name        string              `bencode:"name"`
+       Length      int64               `bencode:"length,omitempty"`
+       MD5Sum      []byte              `bencode:"md5sum,omitempty"`
+       Private     bool                `bencode:"private,omitempty"`
+       Files       []torrent_info_file `bencode:"files,omitempty"`
+}
+
+type torrent_info_ex struct {
+       torrent_info
+       Hash []byte
+}
+
+func (this *torrent_info_ex) UnmarshalBencode(data []byte) error {
+       h := sha1.New()
+       h.Write(data)
+       this.Hash = h.Sum(this.Hash)
+       return bencode.Unmarshal(data, &this.torrent_info)
+}
+
+type torrent_data struct {
+       Info         torrent_info_ex `bencode:"info"`
+       Announce     string          `bencode:"announce"`
+       AnnounceList [][]string      `bencode:"announce-list,omitempty"`
+       CreationDate int64           `bencode:"creation date,omitempty"`
+       Comment      string          `bencode:"comment,omitempty"`
+       CreatedBy    string          `bencode:"created by,omitempty"`
+       Encoding     string          `bencode:"encoding,omitempty"`
+       URLList      interface{}     `bencode:"url-list,omitempty"`
+}
diff --git a/torrent/file_test.go b/torrent/file_test.go
new file mode 100644 (file)
index 0000000..32a9edb
--- /dev/null
@@ -0,0 +1,36 @@
+package torrent
+
+import "testing"
+import "path"
+
+func test_file(t *testing.T, filename string) {
+       f, err := OpenFromFile(filename)
+       if err != nil {
+               t.Fatal(err)
+       }
+
+       switch info := f.Info().(type) {
+       case SingleFile:
+               t.Logf("Single file: %s (length: %d)\n", info.Name, info.Length)
+       case MultiFile:
+               t.Logf("Multiple files: %s\n", info.Name)
+               for _, f := range info.Files {
+                       t.Logf(" - %s (length: %d)\n", path.Join(f.Path...), f.Length)
+               }
+       }
+
+       for _, group := range f.AnnounceList {
+               for _, tracker := range group {
+                       t.Logf("Tracker: %s\n", tracker)
+               }
+       }
+       for _, url := range f.URLList {
+               t.Logf("URL: %s\n", url)
+       }
+
+}
+
+func TestFile(t *testing.T) {
+       test_file(t, "_testdata/archlinux-2011.08.19-netinstall-i686.iso.torrent")
+       test_file(t, "_testdata/continuum.torrent")
+}