2017-07-01 23:09:35 +02:00
package torrentValidator
import (
"encoding/base32"
"encoding/hex"
2017-07-06 21:53:13 +02:00
"io"
"mime/multipart"
"net/url"
"regexp"
"strconv"
"strings"
2017-07-01 23:09:35 +02:00
"github.com/NyaaPantsu/nyaa/config"
2017-07-02 16:54:55 +02:00
"github.com/NyaaPantsu/nyaa/utils/categories"
2017-07-16 17:20:35 +02:00
"github.com/NyaaPantsu/nyaa/utils/cookies"
2017-07-02 16:54:55 +02:00
"github.com/NyaaPantsu/nyaa/utils/format"
2017-07-16 17:20:35 +02:00
msg "github.com/NyaaPantsu/nyaa/utils/messages"
2017-07-02 16:54:55 +02:00
"github.com/NyaaPantsu/nyaa/utils/metainfo"
"github.com/NyaaPantsu/nyaa/utils/torrentLanguages"
2017-07-01 23:09:35 +02:00
"github.com/gin-gonic/gin"
"github.com/zeebo/bencode"
)
func ( r * TorrentRequest ) ValidateName ( ) error {
// then actually check that we have everything we need
if len ( r . Name ) == 0 {
2017-07-13 00:20:43 +02:00
return errTorrentNameInvalid
2017-07-01 23:09:35 +02:00
}
return nil
}
func ( r * TorrentRequest ) ValidateDescription ( ) error {
2017-07-10 14:11:05 +02:00
if len ( r . Description ) > config . Get ( ) . DescriptionLength {
2017-07-13 00:20:43 +02:00
return errTorrentDescInvalid
2017-07-01 23:09:35 +02:00
}
return nil
}
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:" ) {
2017-07-13 00:20:43 +02:00
return errTorrentMagnetInvalid
2017-07-01 23:09:35 +02:00
}
xt = strings . SplitAfter ( xt , ":" ) [ 2 ]
r . Infohash = strings . TrimSpace ( strings . ToUpper ( strings . Split ( xt , "&" ) [ 0 ] ) )
return nil
}
func ( r * TorrentRequest ) ValidateWebsiteLink ( ) error {
if r . WebsiteLink != "" {
// WebsiteLink
2017-07-13 00:20:43 +02:00
urlRegexp , _ := regexp . Compile ( ` ^(https?:\/\/|ircs?:\/\/)?([\da-z\.-]+)\.([a-z\.] { 2,6})([\/\w \.-]*)*(\/.*)?$ ` )
2017-07-01 23:09:35 +02:00
if ! urlRegexp . MatchString ( r . WebsiteLink ) {
2017-07-13 00:20:43 +02:00
return errTorrentURIInvalid
2017-07-01 23:09:35 +02:00
}
}
return nil
}
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 {
2017-07-13 00:20:43 +02:00
return errTorrentHashInvalid
2017-07-01 23:09:35 +02:00
}
} 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 {
2017-07-13 00:20:43 +02:00
return errTorrentCatInvalid
2017-07-01 23:09:35 +02:00
}
CatID , err := strconv . Atoi ( catsSplit [ 0 ] )
if err != nil {
2017-07-13 00:20:43 +02:00
return errTorrentCatInvalid
2017-07-01 23:09:35 +02:00
}
SubCatID , err := strconv . Atoi ( catsSplit [ 1 ] )
if err != nil {
2017-07-13 00:20:43 +02:00
return errTorrentCatInvalid
2017-07-01 23:09:35 +02:00
}
2017-07-06 21:53:13 +02:00
if ! categories . Exists ( r . Category ) {
2017-07-13 00:20:43 +02:00
return errTorrentCatInvalid
2017-07-01 23:09:35 +02:00
}
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
2017-07-10 14:11:05 +02:00
for _ , cat := range config . Get ( ) . Torrents . EnglishOnlyCategories {
2017-07-01 23:09:35 +02:00
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
}
2017-07-17 21:32:55 +02:00
r . Languages = append ( r . Languages , "en" )
2017-07-01 23:09:35 +02:00
return nil
}
englishSelected := false
for _ , language := range r . Languages {
2017-07-17 21:32:55 +02:00
if language == "en" {
2017-07-01 23:09:35 +02:00
englishSelected = true
}
if language != "" && ! torrentLanguages . LanguageExists ( language ) {
2017-07-13 00:20:43 +02:00
return errTorrentLangInvalid
2017-07-01 23:09:35 +02:00
}
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 {
2017-07-17 21:32:55 +02:00
r . Languages = append ( r . Languages , "en" )
2017-07-01 23:09:35 +02:00
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 {
2017-07-10 14:11:05 +02:00
for key , cat := range config . Get ( ) . Torrents . NonEnglishOnlyCategories {
2017-07-01 23:09:35 +02:00
if cat == r . Category {
2017-07-10 14:11:05 +02:00
r . Category = config . Get ( ) . Torrents . EnglishOnlyCategories [ key ]
2017-07-01 23:09:35 +02:00
isEnglishCategory = true
break
}
}
}
return nil
}
// ValidateMultipartUpload : Check if multipart upload is valid
func ( r * TorrentRequest ) ValidateMultipartUpload ( c * gin . Context , uploadFormTorrent string ) ( multipart . File , error ) {
// first: parse torrent file (if any) to fill missing information
tfile , _ , err := c . Request . FormFile ( uploadFormTorrent )
if err == nil {
var torrent metainfo . TorrentFile
// decode torrent
_ , seekErr := tfile . Seek ( 0 , io . SeekStart )
if seekErr != nil {
return tfile , seekErr
}
err = bencode . NewDecoder ( tfile ) . Decode ( & torrent )
if err != nil {
return tfile , metainfo . ErrInvalidTorrentFile
}
// check a few things
if torrent . IsPrivate ( ) {
2017-07-13 00:20:43 +02:00
return tfile , errTorrentPrivate
2017-07-01 23:09:35 +02:00
}
trackers := torrent . GetAllAnnounceURLS ( )
r . Trackers = CheckTrackers ( trackers )
if len ( r . Trackers ) == 0 {
2017-07-13 00:20:43 +02:00
return tfile , errTorrentNoTrackers
2017-07-01 23:09:35 +02:00
}
// Name
if len ( r . Name ) == 0 {
r . Name = torrent . TorrentName ( )
}
// Magnet link: if a file is provided it should be empty
if len ( r . Magnet ) != 0 {
2017-07-13 00:20:43 +02:00
return tfile , errTorrentAndMagnet
2017-07-01 23:09:35 +02:00
}
_ , seekErr = tfile . Seek ( 0 , io . SeekStart )
if seekErr != nil {
return tfile , seekErr
}
infohash , err := metainfo . DecodeInfohash ( tfile )
if err != nil {
return tfile , metainfo . ErrInvalidTorrentFile
}
r . Infohash = infohash
r . Magnet = format . InfoHashToMagnet ( infohash , r . Name , trackers ... )
// extract filesize
r . Filesize = int64 ( torrent . TotalSize ( ) )
// extract filelist
fileInfos := torrent . Info . GetFiles ( )
for _ , fileInfo := range fileInfos {
r . FileList = append ( r . FileList , uploadedFile {
Path : fileInfo . Path ,
Filesize : int64 ( fileInfo . Length ) ,
} )
}
} else {
err = r . ValidateMagnet ( )
if err != nil {
return tfile , err
}
err = r . ValidateHash ( )
if err != nil {
return tfile , err
}
// TODO: Get Trackers from magnet URL
r . Filesize = 0
r . Filepath = ""
return tfile , nil
}
return tfile , err
}
2017-07-16 17:20:35 +02:00
// 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
}