362 lignes
9.7 KiB
Go
362 lignes
9.7 KiB
Go
package torrentValidator
|
|
|
|
import (
|
|
"encoding/base32"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"net/url"
|
|
"os"
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/NyaaPantsu/nyaa/config"
|
|
"github.com/NyaaPantsu/nyaa/utils/categories"
|
|
"github.com/NyaaPantsu/nyaa/utils/cookies"
|
|
msg "github.com/NyaaPantsu/nyaa/utils/messages"
|
|
"github.com/NyaaPantsu/nyaa/utils/torrentLanguages"
|
|
"github.com/NyaaPantsu/nyaa/utils/validator/tag"
|
|
"github.com/anacrolix/torrent/metainfo"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ValidateName is a function validating the torrent name
|
|
func (r *TorrentRequest) ValidateName() error {
|
|
// then actually check that we have everything we need
|
|
if len(r.Name) == 0 {
|
|
return errTorrentNameInvalid
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateTags is a function validating tags by removing duplicate entries
|
|
func (r *TorrentRequest) ValidateTags() {
|
|
// and filter out multiple tags with the same type (only keep the first one)
|
|
var index config.ArrayString
|
|
filteredTags := []tagsValidator.CreateForm{}
|
|
for _, tag := range r.Tags {
|
|
if index.Contains(tag.Type) || tag.Type == "" {
|
|
continue
|
|
}
|
|
filteredTags = append(filteredTags, tag)
|
|
index = append(index, tag.Type)
|
|
}
|
|
|
|
r.Tags = filteredTags
|
|
}
|
|
|
|
// ValidateDescription is a function validating description length
|
|
func (r *TorrentRequest) ValidateDescription() error {
|
|
if len(r.Description) > config.Get().DescriptionLength {
|
|
return errTorrentDescInvalid
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateMagnet is a function validating a magnet uri format
|
|
func (r *TorrentRequest) ValidateMagnet() error {
|
|
magnetURL, err := url.Parse(string(r.Magnet)) //?
|
|
if err != nil {
|
|
return err
|
|
}
|
|
xt := magnetURL.Query().Get("xt")
|
|
if !strings.HasPrefix(xt, "urn:btih:") {
|
|
return errTorrentMagnetInvalid
|
|
}
|
|
xt = strings.SplitAfter(xt, ":")[2]
|
|
r.Infohash = strings.TrimSpace(strings.ToUpper(strings.Split(xt, "&")[0]))
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateWebsiteLink is a function validating a website link
|
|
func (r *TorrentRequest) ValidateWebsiteLink() error {
|
|
if r.WebsiteLink != "" {
|
|
// WebsiteLink
|
|
urlRegexp, _ := regexp.Compile(`^(https?:\/\/|ircs?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*(\/.*)?$`)
|
|
if !urlRegexp.MatchString(r.WebsiteLink) {
|
|
return errTorrentURIInvalid
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateHash is a function validating a torrent hash
|
|
func (r *TorrentRequest) ValidateHash() error {
|
|
isBase32, err := regexp.MatchString("^[2-7A-Z]{32}$", r.Infohash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isBase32 {
|
|
isBase16, err := regexp.MatchString("^[0-9A-F]{40}$", r.Infohash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !isBase16 {
|
|
return errTorrentHashInvalid
|
|
}
|
|
} else {
|
|
//convert to base16
|
|
data, err := base32.StdEncoding.DecodeString(r.Infohash)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
hash16 := make([]byte, hex.EncodedLen(len(data)))
|
|
hex.Encode(hash16, data)
|
|
r.Infohash = strings.ToUpper(string(hash16))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ExtractCategory : takes an http request and computes category field for this form
|
|
func (r *TorrentRequest) ExtractCategory() error {
|
|
catsSplit := strings.Split(r.Category, "_")
|
|
// need this to prevent out of index panics
|
|
if len(catsSplit) != 2 {
|
|
return errTorrentCatInvalid
|
|
}
|
|
CatID, err := strconv.Atoi(catsSplit[0])
|
|
if err != nil {
|
|
return errTorrentCatInvalid
|
|
}
|
|
SubCatID, err := strconv.Atoi(catsSplit[1])
|
|
if err != nil {
|
|
return errTorrentCatInvalid
|
|
}
|
|
|
|
if !categories.Exists(r.Category) {
|
|
return errTorrentCatInvalid
|
|
}
|
|
|
|
r.CategoryID = CatID
|
|
r.SubCategoryID = SubCatID
|
|
return nil
|
|
}
|
|
|
|
// ExtractLanguage : takes a http request, computes the torrent language from the form.
|
|
func (r *TorrentRequest) ExtractLanguage() error {
|
|
isEnglishCategory := false
|
|
for _, cat := range config.Get().Torrents.EnglishOnlyCategories {
|
|
if cat == r.Category {
|
|
isEnglishCategory = true
|
|
break
|
|
}
|
|
}
|
|
if len(r.Languages) == 0 {
|
|
// If no language, but in an English category, set to en-us, else just stop the check.
|
|
if !isEnglishCategory {
|
|
return nil
|
|
}
|
|
r.Languages = append(r.Languages, "en")
|
|
return nil
|
|
}
|
|
englishSelected := false
|
|
for _, language := range r.Languages {
|
|
if language == "en" {
|
|
englishSelected = true
|
|
}
|
|
|
|
if language != "" && !torrentLanguages.LanguageExists(language) {
|
|
return errTorrentLangInvalid
|
|
}
|
|
|
|
if strings.HasPrefix(language, "en") && isEnglishCategory {
|
|
englishSelected = true
|
|
}
|
|
}
|
|
|
|
// We shouldn't return an error for languages, just adding the right language is enough
|
|
if !englishSelected && isEnglishCategory {
|
|
r.Languages = append(r.Languages, "en")
|
|
return nil
|
|
}
|
|
|
|
// We shouldn't return an error if someone has selected only english for languages and missed the right category. Just move the torrent in the right one
|
|
// Multiple if conditions so we only do this for loop when needed
|
|
if len(r.Languages) == 1 && strings.HasPrefix(r.Languages[0], "en") && !isEnglishCategory && r.CategoryID > 0 {
|
|
for key, cat := range config.Get().Torrents.NonEnglishOnlyCategories {
|
|
if cat == r.Category {
|
|
r.Category = config.Get().Torrents.EnglishOnlyCategories[key]
|
|
isEnglishCategory = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ValidateMultipartUpload : Check if multipart upload is valid
|
|
func (r *TorrentRequest) ValidateMultipartUpload(c *gin.Context, uploadFormTorrent string) error {
|
|
// first: parse torrent file (if any) to fill missing information
|
|
tfile, _, err := c.Request.FormFile(uploadFormTorrent)
|
|
if err == nil {
|
|
torrent, err := metainfo.Load(tfile)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
torrentInfos, err := torrent.UnmarshalInfo()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check a few things
|
|
if torrentInfos.Private != nil && *torrentInfos.Private {
|
|
return errTorrentPrivate
|
|
}
|
|
// We check that the trackers are valid,
|
|
// we filter the dead ones
|
|
// and we add the needed ones from our config in the request and the torrent file
|
|
r.Trackers = CheckTrackers(torrent)
|
|
if len(r.Trackers) == 0 {
|
|
return errTorrentNoTrackers
|
|
}
|
|
|
|
// For the name, if none is provided in request, we set the name from the torrent file
|
|
if len(r.Name) == 0 {
|
|
r.Name = torrentInfos.Name
|
|
}
|
|
|
|
// Magnet link: if a file is provided it should be empty
|
|
if len(r.Magnet) != 0 {
|
|
return errTorrentAndMagnet
|
|
}
|
|
|
|
// We are using here default functions from anacrolix
|
|
infohash := torrent.HashInfoBytes()
|
|
|
|
if infohash.String() == "" {
|
|
return errInvalidTorrentFile
|
|
}
|
|
r.Infohash = infohash.String()
|
|
r.Magnet = torrent.Magnet(r.Name, infohash).String()
|
|
// extract filesize
|
|
r.Filesize = torrentInfos.TotalLength()
|
|
|
|
// extract filelist
|
|
fileInfos := torrentInfos.Files
|
|
for _, fileInfo := range fileInfos {
|
|
r.FileList = append(r.FileList, uploadedFile{
|
|
Path: fileInfo.Path,
|
|
Filesize: fileInfo.Length,
|
|
})
|
|
}
|
|
// Since we can change with anacrolix the trackers of a torrent,
|
|
// We will use this to generate directly the torrent file here
|
|
file := fmt.Sprintf("%s%c%s.torrent", config.Get().Torrents.FileStorage, os.PathSeparator, infohash.String())
|
|
f, err := os.Create(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
err = torrent.Write(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
} else {
|
|
err = r.ValidateMagnet()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = r.ValidateHash()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// TODO: Get Trackers from magnet URL
|
|
r.Filesize = 0
|
|
r.Filepath = ""
|
|
|
|
return nil
|
|
}
|
|
return err
|
|
}
|
|
|
|
// ExtractInfo : Function to assign values from request to ReassignForm
|
|
func (f *ReassignForm) ExtractInfo(c *gin.Context) bool {
|
|
f.By = c.PostForm("by")
|
|
messages := msg.GetMessages(c)
|
|
if f.By != "olduser" && f.By != "torrentid" {
|
|
messages.AddErrorTf("errors", "no_action_exist", f.By)
|
|
return false
|
|
}
|
|
|
|
f.Data = strings.Trim(c.PostForm("data"), " \r\n")
|
|
if f.By == "olduser" {
|
|
if f.Data == "" {
|
|
messages.AddErrorT("errors", "user_not_found")
|
|
return false
|
|
} else if strings.Contains(f.Data, "\n") {
|
|
messages.AddErrorT("errors", "multiple_username_error")
|
|
return false
|
|
}
|
|
} else if f.By == "torrentid" {
|
|
if f.Data == "" {
|
|
messages.AddErrorT("errors", "no_id_given")
|
|
return false
|
|
}
|
|
splitData := strings.Split(f.Data, "\n")
|
|
for i, tmp := range splitData {
|
|
tmp = strings.Trim(tmp, " \r")
|
|
torrentID, err := strconv.ParseUint(tmp, 10, 0)
|
|
if err != nil {
|
|
messages.AddErrorTf("errors", "parse_error_line", i+1)
|
|
return false // TODO: Shouldn't it continue to parse the rest and display the errored lines?
|
|
}
|
|
f.Torrents = append(f.Torrents, uint(torrentID))
|
|
}
|
|
}
|
|
|
|
tmpID := c.PostForm("to")
|
|
parsed, err := strconv.ParseUint(tmpID, 10, 32)
|
|
if err != nil {
|
|
messages.Error(err)
|
|
return false
|
|
}
|
|
f.AssignTo = uint(parsed)
|
|
_, _, _, _, err = cookies.RetrieveUserFromRequest(c, uint(parsed))
|
|
if err != nil {
|
|
messages.AddErrorTf("errors", "no_user_found_id", int(parsed))
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Get check if the tag map has the same tag in it (tag value + tag type)
|
|
func (ts TagsRequest) Get(tagType string) tagsValidator.CreateForm {
|
|
for _, ta := range ts {
|
|
if ta.Type == tagType {
|
|
return ta
|
|
}
|
|
}
|
|
return tagsValidator.CreateForm{}
|
|
}
|
|
|
|
type torrentInt interface {
|
|
GetDescriptiveTags() string
|
|
}
|
|
|
|
// Bind a torrent model with its tags to the tags request
|
|
func (ts *TagsRequest) Bind(torrent torrentInt) error {
|
|
for _, tagConf := range config.Get().Torrents.Tags.Types {
|
|
if tagConf.Field == "" {
|
|
return errMissingFieldConfig
|
|
}
|
|
tagField := reflect.ValueOf(torrent).Elem().FieldByName(tagConf.Field)
|
|
if !tagField.IsValid() {
|
|
return errWrongFieldConfig
|
|
}
|
|
|
|
if fmt.Sprint(tagField.Interface()) != "" && fmt.Sprint(tagField.Interface()) != "0" {
|
|
*ts = append(*ts, tagsValidator.CreateForm{Type: tagConf.Name, Tag: fmt.Sprint(tagField.Interface())})
|
|
}
|
|
}
|
|
if torrent.GetDescriptiveTags() != "" {
|
|
*ts = append(*ts, tagsValidator.CreateForm{Type: config.Get().Torrents.Tags.Default, Tag: torrent.GetDescriptiveTags()})
|
|
}
|
|
return nil
|
|
}
|