From f351c2b047de844585f9bbcc88479bde52b37bfd Mon Sep 17 00:00:00 2001 From: Ramon Dantas Date: Mon, 15 May 2017 18:45:47 -0300 Subject: [PATCH] Store torrent FileList info as bencoded data. (#530) Use this instead of joining with forward-slashes because it's possible that a torrent uses "foo/bar" as a filename or part of the directory list. --- model/file.go | 30 +++++++++++++++---- model/torrent.go | 6 +++- router/upload.go | 8 ++--- router/uploadHandler.go | 10 ++++--- .../metainfoFetcher/metainfoFetcher.go | 19 ++++++++---- 5 files changed, 54 insertions(+), 19 deletions(-) diff --git a/model/file.go b/model/file.go index 67b64168..c4e236b3 100644 --- a/model/file.go +++ b/model/file.go @@ -1,14 +1,34 @@ package model +import ( + "github.com/zeebo/bencode" +) + type File struct { - ID uint `gorm:"column:file_id;primary_key"` - TorrentID uint `gorm:"column:torrent_id;unique_index:idx_tid_path"` - Path string `gorm:"column:path;unique_index:idx_tid_path"` - Filesize int64 `gorm:"column:filesize"` + ID uint `gorm:"column:file_id;primary_key"` + TorrentID uint `gorm:"column:torrent_id;unique_index:idx_tid_path"` + // this path is bencode'd, call Path() to obtain + BencodedPath string `gorm:"column:path;unique_index:idx_tid_path"` + Filesize int64 `gorm:"column:filesize"` } // Returns the total size of memory allocated for this struct func (f File) Size() int { - return (2 + len(f.Path) + 2) * 8; + return (2 + len(f.BencodedPath) + 1) * 8; +} + +func (f *File) Path() (out []string) { + bencode.DecodeString(f.BencodedPath, &out) + return +} + +func (f *File) SetPath(path []string) error { + encoded, err := bencode.EncodeString(path) + if err != nil { + return err + } + + f.BencodedPath = encoded + return nil } diff --git a/model/torrent.go b/model/torrent.go index de081167..5d6b0131 100644 --- a/model/torrent.go +++ b/model/torrent.go @@ -6,6 +6,7 @@ import ( "fmt" "html/template" + "path/filepath" "strconv" "strings" "time" @@ -131,7 +132,10 @@ func (t *Torrent) ToJSON() TorrentJSON { } fileListJSON := make([]FileJSON, 0, len(t.FileList)) for _, f := range t.FileList { - fileListJSON = append(fileListJSON, FileJSON{Path: f.Path, Filesize: util.FormatFilesize2(f.Filesize)}) + fileListJSON = append(fileListJSON, FileJSON{ + Path: filepath.Join(f.Path()...), + Filesize: util.FormatFilesize2(f.Filesize), + }) } uploader := "" if t.Uploader != nil { diff --git a/router/upload.go b/router/upload.go index 57a65759..2ec9f05f 100644 --- a/router/upload.go +++ b/router/upload.go @@ -27,7 +27,7 @@ import ( // Use this, because we seem to avoid using models, and we would need // the torrent ID to create the File in the DB type UploadedFile struct { - Path string + Path []string Filesize int64 } @@ -161,10 +161,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { // extract filelist fileInfos := torrent.Info.GetFiles() - for _, info := range fileInfos { + for _, fileInfo := range fileInfos { f.FileList = append(f.FileList, UploadedFile{ - Path: info.Path.FilePath(), - Filesize: int64(info.Length), + Path: fileInfo.Path, + Filesize: int64(fileInfo.Length), }) } } else { diff --git a/router/uploadHandler.go b/router/uploadHandler.go index 74170780..0d7049ea 100644 --- a/router/uploadHandler.go +++ b/router/uploadHandler.go @@ -64,10 +64,12 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) { // add filelist to files db, if we have one if len(uploadForm.FileList) > 0 { for _, uploadedFile := range uploadForm.FileList { - file := model.File{ - TorrentID: torrent.ID, - Path: uploadedFile.Path, - Filesize: uploadedFile.Filesize} + file := model.File{TorrentID: torrent.ID, Filesize: uploadedFile.Filesize} + err := file.SetPath(uploadedFile.Path) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } db.ORM.Create(&file) } } diff --git a/service/torrent/metainfoFetcher/metainfoFetcher.go b/service/torrent/metainfoFetcher/metainfoFetcher.go index 8da54513..cf87a953 100644 --- a/service/torrent/metainfoFetcher/metainfoFetcher.go +++ b/service/torrent/metainfoFetcher/metainfoFetcher.go @@ -120,23 +120,32 @@ func (fetcher *MetainfoFetcher) removeFromFailed(tID uint) { delete(fetcher.failedOperations, tID) } - func updateFileList(dbEntry model.Torrent, info *metainfo.Info) error { torrentFiles := info.UpvertedFiles() log.Infof("TID %d has %d files.", dbEntry.ID, len(torrentFiles)) + for _, file := range torrentFiles { - path := file.DisplayPath(info) + var path []string + if file.Path != nil { + path = file.Path + } else { + // If it's nil, use the torrent name (info.Name) as the path (single-file torrent) + path = append(path, info.Name) + } // Can't read FileList from the GetTorrents output, rely on the unique_index // to ensure no files are duplicated. - log.Infof("Adding file %s to filelist of TID %d", path, dbEntry.ID) + log.Infof("Adding file %s to filelist of TID %d", file.DisplayPath(info), dbEntry.ID) dbFile := model.File{ TorrentID: dbEntry.ID, - Path: path, Filesize: file.Length, } + err := dbFile.SetPath(path) + if err != nil { + return err + } - err := db.ORM.Create(&dbFile).Error + err = db.ORM.Create(&dbFile).Error if err != nil { return err }