Albirew/nyaa-pantsu
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/utils/validator/torrent/functions.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
}