Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0
Ce dépôt a été archivé le 2022-05-07. Vous pouvez voir ses fichiers ou le cloner, mais pas ouvrir de ticket ou de demandes d'ajout, ni soumettre de changements.
nyaa-pantsu/router/upload.go
akuma06 3775e9dfb5 Torrent Hidden Option (to test before merging)
* Added a check on username and userId when converting torrent to JSON
* Added a checkbox for hidden in modepanel, torrent user edit and upload
* Added a Hidden field bool in torrent model and upload form
2017-05-27 20:33:40 +02:00

306 lignes
8 Kio
Go

package router
import (
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"github.com/NyaaPantsu/nyaa/cache"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/service/upload"
"github.com/NyaaPantsu/nyaa/util"
"github.com/NyaaPantsu/nyaa/util/categories"
"github.com/NyaaPantsu/nyaa/util/metainfo"
"github.com/zeebo/bencode"
)
// 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
Filesize int64
}
// uploadForm serializing HTTP form for torrent upload
type uploadForm struct {
Name string
Magnet string
Category string
Remake bool
Description string
Status int
Hidden bool
CaptchaID string
WebsiteLink string
Infohash string
CategoryID int
SubCategoryID int
Filesize int64
Filepath string
FileList []uploadedFile
Trackers []string
}
// TODO: these should be in another package (?)
// form names
const uploadFormName = "name"
const uploadFormTorrent = "torrent"
const uploadFormMagnet = "magnet"
const uploadFormCategory = "c"
const uploadFormRemake = "remake"
const uploadFormDescription = "desc"
const uploadFormWebsiteLink = "website_link"
const uploadFormStatus = "status"
const uploadFormHidden = "hidden"
// error indicating that you can't send both a magnet link and torrent
var errTorrentPlusMagnet = errors.New("Upload either a torrent file or magnet link, not both")
// error indicating a torrent is private
var errPrivateTorrent = errors.New("Torrent is private")
// error indicating a problem with its trackers
var errTrackerProblem = errors.New("Torrent does not have any (working) trackers: https://" + config.WebAddress + "/faq#trackers")
// error indicating a torrent's name is invalid
var errInvalidTorrentName = errors.New("Torrent name is invalid")
// error indicating a torrent's description is invalid
var errInvalidTorrentDescription = errors.New("Torrent description is invalid")
// error indicating a torrent's description is invalid
var errInvalidWebsiteLink = errors.New("Website url or IRC link is invalid")
// error indicating a torrent's category is invalid
var errInvalidTorrentCategory = errors.New("Torrent category is invalid")
// var p = bluemonday.UGCPolicy()
/**
uploadForm.ExtractInfo takes an http request and computes all fields for this form
*/
func (f *uploadForm) ExtractInfo(r *http.Request) error {
f.Name = r.FormValue(uploadFormName)
f.Category = r.FormValue(uploadFormCategory)
f.Description = r.FormValue(uploadFormDescription)
f.WebsiteLink = r.FormValue(uploadFormWebsiteLink)
f.Status, _ = strconv.Atoi(r.FormValue(uploadFormStatus))
f.Magnet = r.FormValue(uploadFormMagnet)
f.Remake = r.FormValue(uploadFormRemake) == "on"
f.Hidden = r.FormValue(uploadFormHidden) == "on"
// trim whitespace
f.Name = util.TrimWhitespaces(f.Name)
f.Description = util.Sanitize(util.TrimWhitespaces(f.Description), "default")
f.WebsiteLink = util.TrimWhitespaces(f.WebsiteLink)
f.Magnet = util.TrimWhitespaces(f.Magnet)
cache.Impl.ClearAll()
defer r.Body.Close()
catsSplit := strings.Split(f.Category, "_")
// need this to prevent out of index panics
if len(catsSplit) == 2 {
CatID, err := strconv.Atoi(catsSplit[0])
if err != nil {
return errInvalidTorrentCategory
}
SubCatID, err := strconv.Atoi(catsSplit[1])
if err != nil {
return errInvalidTorrentCategory
}
if !categories.CategoryExists(f.Category) {
return errInvalidTorrentCategory
}
f.CategoryID = CatID
f.SubCategoryID = SubCatID
} else {
return errInvalidTorrentCategory
}
if f.WebsiteLink != "" {
// WebsiteLink
urlRegexp, _ := regexp.Compile(`^(https?:\/\/|ircs?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$`)
if !urlRegexp.MatchString(f.WebsiteLink) {
return errInvalidWebsiteLink
}
}
// first: parse torrent file (if any) to fill missing information
tfile, _, err := r.FormFile(uploadFormTorrent)
if err == nil {
var torrent metainfo.TorrentFile
// decode torrent
_, seekErr := tfile.Seek(0, io.SeekStart)
if seekErr != nil {
return seekErr
}
err = bencode.NewDecoder(tfile).Decode(&torrent)
if err != nil {
return metainfo.ErrInvalidTorrentFile
}
// check a few things
if torrent.IsPrivate() {
return errPrivateTorrent
}
trackers := torrent.GetAllAnnounceURLS()
f.Trackers = uploadService.CheckTrackers(trackers)
if len(f.Trackers) == 0 {
return errTrackerProblem
}
// Name
if len(f.Name) == 0 {
f.Name = torrent.TorrentName()
}
// Magnet link: if a file is provided it should be empty
if len(f.Magnet) != 0 {
return errTorrentPlusMagnet
}
binInfohash, err := torrent.Infohash()
if err != nil {
return err
}
f.Infohash = strings.ToUpper(hex.EncodeToString(binInfohash[:]))
f.Magnet = util.InfoHashToMagnet(f.Infohash, f.Name, trackers...)
// extract filesize
f.Filesize = int64(torrent.TotalSize())
// extract filelist
fileInfos := torrent.Info.GetFiles()
for _, fileInfo := range fileInfos {
f.FileList = append(f.FileList, uploadedFile{
Path: fileInfo.Path,
Filesize: int64(fileInfo.Length),
})
}
} else {
// No torrent file provided
magnetURL, err := url.Parse(string(f.Magnet)) //?
if err != nil {
return err
}
xt := magnetURL.Query().Get("xt")
if !strings.HasPrefix(xt, "urn:btih:") {
return errors.New("Incorrect magnet")
}
xt = strings.SplitAfter(xt, ":")[2]
f.Infohash = strings.ToUpper(strings.Split(xt, "&")[0])
isBase32, err := regexp.MatchString("^[2-7A-Z]{32}$", f.Infohash)
if err != nil {
return err
}
if !isBase32 {
isBase16, err := regexp.MatchString("^[0-9A-F]{40}$", f.Infohash)
if err != nil {
return err
}
if !isBase16 {
return errors.New("Incorrect hash")
}
}
// TODO: Get Trackers from magnet URL
f.Filesize = 0
f.Filepath = ""
return nil
}
// then actually check that we have everything we need
if len(f.Name) == 0 {
return errInvalidTorrentName
}
// after data has been checked & extracted, write it to disk
if len(config.TorrentFileStorage) > 0 {
err := writeTorrentToDisk(tfile, f.Infohash+".torrent", &f.Filepath)
if err != nil {
return err
}
} else {
f.Filepath = ""
}
return nil
}
func (f *uploadForm) ExtractEditInfo(r *http.Request) error {
f.Name = r.FormValue(uploadFormName)
f.Category = r.FormValue(uploadFormCategory)
f.WebsiteLink = r.FormValue(uploadFormWebsiteLink)
f.Description = r.FormValue(uploadFormDescription)
f.Hidden = r.FormValue(uploadFormHidden) == "on"
f.Status, _ = strconv.Atoi(r.FormValue(uploadFormStatus))
// trim whitespace
f.Name = util.TrimWhitespaces(f.Name)
f.Description = util.Sanitize(util.TrimWhitespaces(f.Description), "default")
defer r.Body.Close()
catsSplit := strings.Split(f.Category, "_")
// need this to prevent out of index panics
if len(catsSplit) == 2 {
CatID, err := strconv.Atoi(catsSplit[0])
if err != nil {
return errInvalidTorrentCategory
}
SubCatID, err := strconv.Atoi(catsSplit[1])
if err != nil {
return errInvalidTorrentCategory
}
if !categories.CategoryExists(f.Category) {
return errInvalidTorrentCategory
}
f.CategoryID = CatID
f.SubCategoryID = SubCatID
} else {
return errInvalidTorrentCategory
}
return nil
}
func writeTorrentToDisk(file multipart.File, name string, fullpath *string) error {
_, seekErr := file.Seek(0, io.SeekStart)
if seekErr != nil {
return seekErr
}
b, err := ioutil.ReadAll(file)
if err != nil {
return err
}
*fullpath = fmt.Sprintf("%s%c%s", config.TorrentFileStorage, os.PathSeparator, name)
return ioutil.WriteFile(*fullpath, b, 0644)
}
// newUploadForm creates a new upload form given parameters as list
func newUploadForm(params ...string) (uploadForm uploadForm) {
if len(params) > 1 {
uploadForm.Category = params[0]
} else {
uploadForm.Category = "3_12"
}
if len(params) > 2 {
uploadForm.Description = params[1]
} else {
uploadForm.Description = "Description"
}
return
}