2017-05-09 17:09:45 +02:00
package apiService
2017-05-09 18:37:40 +02:00
import (
2017-05-11 23:59:00 +02:00
"encoding/base32"
2017-05-11 05:04:11 +02:00
"encoding/hex"
"errors"
2017-05-11 06:12:54 +02:00
"fmt"
2017-05-11 05:04:11 +02:00
"io"
2017-05-09 19:37:39 +02:00
"net/http"
"net/url"
2017-05-09 18:37:40 +02:00
"reflect"
2017-05-09 19:37:39 +02:00
"regexp"
"strings"
2017-05-09 18:37:40 +02:00
2017-05-17 07:58:40 +02:00
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service"
"github.com/NyaaPantsu/nyaa/service/upload"
"github.com/NyaaPantsu/nyaa/util/metainfo"
2017-05-11 05:04:11 +02:00
"github.com/zeebo/bencode"
2017-05-09 18:37:40 +02:00
)
type torrentsQuery struct {
Category int ` json:"category" `
SubCategory int ` json:"sub_category" `
Status int ` json:"status" `
Uploader int ` json:"uploader" `
Downloads int ` json:"downloads" `
}
2017-05-26 12:12:52 +02:00
// TorrentsRequest struct
2017-05-09 18:37:40 +02:00
type TorrentsRequest struct {
Query torrentsQuery ` json:"search" `
Page int ` json:"page" `
MaxPerPage int ` json:"limit" `
}
2017-05-26 12:12:52 +02:00
// TorrentRequest struct
2017-05-09 19:37:39 +02:00
//accept torrent files?
2017-05-09 20:54:50 +02:00
type TorrentRequest struct {
2017-05-09 19:37:39 +02:00
Name string ` json:"name" `
Category int ` json:"category" `
SubCategory int ` json:"sub_category" `
2017-05-09 22:24:32 +02:00
Magnet string ` json:"magnet" `
Hash string ` json:"hash" `
2017-05-09 19:37:39 +02:00
Description string ` json:"description" `
2017-05-28 06:22:39 +02:00
Remake bool ` json:"remake" `
WebsiteLink string ` json:"website_link" `
2017-05-09 19:37:39 +02:00
}
2017-05-26 12:12:52 +02:00
// UpdateRequest struct
2017-05-09 20:54:50 +02:00
type UpdateRequest struct {
Consistency, formatting, error checking, cleanup, and a couple bug fixes (#245)
* Checkpoint: it builds
The config, db, model, network, os, and public packages have had some
fixes to glaringly obvious flaws, dead code removed, and stylistic
changes.
* Style changes and old code removal in router
Router needs a lot of work done to its (lack of) error handling.
* Dead code removal and style changes
Now up to util/email/email.go. After I'm finished with the initial sweep
I'll go back and fix error handling and security issues. Then I'll fix
the broken API. Then I'll go through to add documentation and fix code
visibility.
* Finish dead code removal and style changes
Vendored libraries not touched. Everything still needs security fixes
and documentation. There's also one case of broken functionality.
* Fix accidental find-and-replace
* Style, error checking, saftey, bug fix changes
* Redo error checking erased during merge
* Re-add merge-erased fix. Make Safe safe.
2017-05-10 04:34:40 +02:00
ID int ` json:"id" `
2017-05-09 20:54:50 +02:00
Update TorrentRequest ` json:"update" `
}
2017-05-26 12:12:52 +02:00
// ToParams : Convert a torrentsrequest to searchparams
2017-05-10 20:42:20 +02:00
func ( r * TorrentsRequest ) ToParams ( ) serviceBase . WhereParams {
res := serviceBase . WhereParams { }
2017-05-09 18:37:40 +02:00
conditions := ""
v := reflect . ValueOf ( r . Query )
for i := 0 ; i < v . NumField ( ) ; i ++ {
field := v . Field ( i )
if field . Interface ( ) != reflect . Zero ( field . Type ( ) ) . Interface ( ) {
if i != 0 {
conditions += " AND "
}
conditions += v . Type ( ) . Field ( i ) . Tag . Get ( "json" ) + " = ?"
res . Params = append ( res . Params , field . Interface ( ) )
}
}
res . Conditions = conditions
return res
}
2017-05-09 19:37:39 +02:00
2017-05-09 22:24:32 +02:00
func validateName ( r * TorrentRequest ) ( error , int ) {
2017-05-11 05:04:11 +02:00
/ * if len ( r . Name ) < 100 { //isn't this too much?
2017-05-09 19:37:39 +02:00
return ErrShortName , http . StatusNotAcceptable
2017-05-11 05:04:11 +02:00
} * /
2017-05-09 22:24:32 +02:00
return nil , http . StatusOK
}
2017-05-19 04:55:59 +02:00
// TODO Check category is within accepted range
2017-05-09 22:24:32 +02:00
func validateCategory ( r * TorrentRequest ) ( error , int ) {
2017-05-09 19:37:39 +02:00
if r . Category == 0 {
return ErrCategory , http . StatusNotAcceptable
}
2017-05-09 22:24:32 +02:00
return nil , http . StatusOK
}
2017-05-19 04:55:59 +02:00
// TODO Check subCategory is within accepted range
2017-05-09 22:24:32 +02:00
func validateSubCategory ( r * TorrentRequest ) ( error , int ) {
2017-05-09 19:37:39 +02:00
if r . SubCategory == 0 {
return ErrSubCategory , http . StatusNotAcceptable
}
2017-05-09 22:24:32 +02:00
return nil , http . StatusOK
}
2017-05-09 19:37:39 +02:00
2017-05-28 06:22:39 +02:00
func validateWebsiteLink ( r * TorrentRequest ) ( error , int ) {
if r . WebsiteLink != "" {
// WebsiteLink
urlRegexp , _ := regexp . Compile ( ` ^(https?:\/\/|ircs?:\/\/)?([\da-z\.-]+)\.([a-z\.] { 2,6})([\/\w \.-]*)*\/?$ ` )
if ! urlRegexp . MatchString ( r . WebsiteLink ) {
return ErrWebsiteLink , http . StatusNotAcceptable
}
}
return nil , http . StatusOK
}
2017-05-09 22:24:32 +02:00
func validateMagnet ( r * TorrentRequest ) ( error , int ) {
2017-05-26 12:12:52 +02:00
magnetURL , err := url . Parse ( string ( r . Magnet ) ) //?
2017-05-09 22:24:32 +02:00
if err != nil {
return err , http . StatusInternalServerError
}
2017-05-26 12:12:52 +02:00
xt := magnetURL . Query ( ) . Get ( "xt" )
2017-05-11 23:59:00 +02:00
if ! strings . HasPrefix ( xt , "urn:btih:" ) {
2017-05-09 22:24:32 +02:00
return ErrMagnet , http . StatusNotAcceptable
2017-05-09 19:37:39 +02:00
}
2017-05-11 23:59:00 +02:00
xt = strings . SplitAfter ( xt , ":" ) [ 2 ]
r . Hash = strings . ToUpper ( strings . Split ( xt , "&" ) [ 0 ] )
2017-05-11 06:23:02 +02:00
fmt . Println ( r . Hash )
2017-05-09 22:24:32 +02:00
return nil , http . StatusOK
}
2017-05-09 19:37:39 +02:00
2017-05-09 22:24:32 +02:00
func validateHash ( r * TorrentRequest ) ( error , int ) {
r . Hash = strings . ToUpper ( r . Hash )
2017-05-11 23:59:00 +02:00
isBase32 , err := regexp . MatchString ( "^[2-7A-Z]{32}$" , r . Hash )
2017-05-09 19:37:39 +02:00
if err != nil {
return err , http . StatusInternalServerError
}
2017-05-11 23:59:00 +02:00
if ! isBase32 {
isBase16 , err := regexp . MatchString ( "^[0-9A-F]{40}$" , r . Hash )
if err != nil {
return err , http . StatusInternalServerError
}
if ! isBase16 {
return ErrHash , http . StatusNotAcceptable
}
} else {
//convert to base16
data , err := base32 . StdEncoding . DecodeString ( r . Hash )
if err != nil {
return err , http . StatusInternalServerError
}
hash16 := make ( [ ] byte , hex . EncodedLen ( len ( data ) ) )
hex . Encode ( hash16 , data )
2017-05-12 03:28:00 +02:00
r . Hash = strings . ToUpper ( string ( hash16 ) )
2017-05-09 19:37:39 +02:00
}
return nil , http . StatusOK
}
2017-05-09 20:54:50 +02:00
2017-05-26 12:12:52 +02:00
// ValidateUpload : Check if an upload is valid
2017-05-09 22:24:32 +02:00
func ( r * TorrentRequest ) ValidateUpload ( ) ( err error , code int ) {
validators := [ ] func ( r * TorrentRequest ) ( error , int ) {
validateName ,
validateCategory ,
validateSubCategory ,
validateMagnet ,
validateHash ,
2017-05-28 06:22:39 +02:00
validateWebsiteLink ,
2017-05-09 20:54:50 +02:00
}
2017-05-09 22:24:32 +02:00
for i , validator := range validators {
if r . Hash != "" && i == 3 {
continue
2017-05-09 20:54:50 +02:00
}
2017-05-09 22:24:32 +02:00
err , code = validator ( r )
2017-05-09 20:54:50 +02:00
if err != nil {
2017-05-09 22:24:32 +02:00
break
}
}
return err , code
}
2017-05-26 12:12:52 +02:00
// ValidateMultipartUpload : Check if multipart upload is valid
2017-05-11 05:04:11 +02:00
func ( r * TorrentRequest ) ValidateMultipartUpload ( req * http . Request ) ( int64 , error , int ) {
tfile , _ , err := req . FormFile ( "torrent" )
if err == nil {
var torrent metainfo . TorrentFile
// decode torrent
if _ , err = tfile . Seek ( 0 , io . SeekStart ) ; err != nil {
return 0 , err , http . StatusInternalServerError
}
if err = bencode . NewDecoder ( tfile ) . Decode ( & torrent ) ; err != nil {
return 0 , err , http . StatusInternalServerError
}
// check a few things
if torrent . IsPrivate ( ) {
2017-05-11 06:12:54 +02:00
return 0 , errors . New ( "private torrents not allowed" ) , http . StatusNotAcceptable
2017-05-11 05:04:11 +02:00
}
trackers := torrent . GetAllAnnounceURLS ( )
2017-05-27 00:45:18 +02:00
trackers = uploadService . CheckTrackers ( trackers )
if len ( trackers ) == 0 {
2017-05-11 06:12:54 +02:00
return 0 , errors . New ( "tracker(s) not allowed" ) , http . StatusNotAcceptable
2017-05-11 05:04:11 +02:00
}
if r . Name == "" {
r . Name = torrent . TorrentName ( )
}
2017-05-28 23:47:47 +02:00
_ , err = tfile . Seek ( 0 , io . SeekStart )
2017-05-11 05:04:11 +02:00
if err != nil {
return 0 , err , http . StatusInternalServerError
}
2017-05-28 23:47:47 +02:00
infohash , err := metainfo . DecodeInfohash ( tfile )
if err != nil {
return 0 , err , http . StatusInternalServerError
}
r . Hash = infohash
2017-05-11 05:04:11 +02:00
// extract filesize
filesize := int64 ( torrent . TotalSize ( ) )
err , code := r . ValidateUpload ( )
return filesize , err , code
}
return 0 , err , http . StatusInternalServerError
}
2017-05-26 12:12:52 +02:00
// ValidateUpdate : Check if an update is valid
2017-05-09 22:24:32 +02:00
func ( r * TorrentRequest ) ValidateUpdate ( ) ( err error , code int ) {
validators := [ ] func ( r * TorrentRequest ) ( error , int ) {
validateName ,
validateCategory ,
validateSubCategory ,
validateMagnet ,
validateHash ,
2017-05-28 06:22:39 +02:00
validateWebsiteLink ,
2017-05-09 22:24:32 +02:00
}
//don't update not requested values
//rewrite with reflect?
for i , validator := range validators {
if ( r . Name == "" && i == 0 ) || ( r . Category == 0 && i == 1 ) ||
( r . SubCategory == 0 && i == 2 ) ||
( r . Hash != "" || r . Magnet == "" && i == 3 ) || ( r . Hash == "" && i == 4 ) {
continue
2017-05-09 20:54:50 +02:00
}
2017-05-09 22:24:32 +02:00
err , code = validator ( r )
if err != nil {
break
2017-05-09 20:54:50 +02:00
}
}
2017-05-09 22:24:32 +02:00
return err , code
2017-05-09 20:54:50 +02:00
}
2017-05-26 12:12:52 +02:00
// UpdateTorrent : Update torrent model
2017-05-09 20:54:50 +02:00
//rewrite with reflect ?
Consistency, formatting, error checking, cleanup, and a couple bug fixes (#245)
* Checkpoint: it builds
The config, db, model, network, os, and public packages have had some
fixes to glaringly obvious flaws, dead code removed, and stylistic
changes.
* Style changes and old code removal in router
Router needs a lot of work done to its (lack of) error handling.
* Dead code removal and style changes
Now up to util/email/email.go. After I'm finished with the initial sweep
I'll go back and fix error handling and security issues. Then I'll fix
the broken API. Then I'll go through to add documentation and fix code
visibility.
* Finish dead code removal and style changes
Vendored libraries not touched. Everything still needs security fixes
and documentation. There's also one case of broken functionality.
* Fix accidental find-and-replace
* Style, error checking, saftey, bug fix changes
* Redo error checking erased during merge
* Re-add merge-erased fix. Make Safe safe.
2017-05-10 04:34:40 +02:00
func ( r * UpdateRequest ) UpdateTorrent ( t * model . Torrent ) {
2017-05-09 20:54:50 +02:00
if r . Update . Name != "" {
t . Name = r . Update . Name
}
if r . Update . Hash != "" {
t . Hash = r . Update . Hash
}
if r . Update . Category != 0 {
t . Category = r . Update . Category
}
if r . Update . SubCategory != 0 {
Consistency, formatting, error checking, cleanup, and a couple bug fixes (#245)
* Checkpoint: it builds
The config, db, model, network, os, and public packages have had some
fixes to glaringly obvious flaws, dead code removed, and stylistic
changes.
* Style changes and old code removal in router
Router needs a lot of work done to its (lack of) error handling.
* Dead code removal and style changes
Now up to util/email/email.go. After I'm finished with the initial sweep
I'll go back and fix error handling and security issues. Then I'll fix
the broken API. Then I'll go through to add documentation and fix code
visibility.
* Finish dead code removal and style changes
Vendored libraries not touched. Everything still needs security fixes
and documentation. There's also one case of broken functionality.
* Fix accidental find-and-replace
* Style, error checking, saftey, bug fix changes
* Redo error checking erased during merge
* Re-add merge-erased fix. Make Safe safe.
2017-05-10 04:34:40 +02:00
t . SubCategory = r . Update . SubCategory
2017-05-09 20:54:50 +02:00
}
if r . Update . Description != "" {
t . Description = r . Update . Description
}
2017-05-28 06:22:39 +02:00
if r . Update . WebsiteLink != "" {
t . WebsiteLink = r . Update . WebsiteLink
}
2017-05-09 20:54:50 +02:00
}