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
Chris MacLeod c9b72206a5 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-09 21:34:40 -05:00

252 lignes
6,2 Kio
Go

package router
import (
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"github.com/ewhal/nyaa/config"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/util"
"github.com/ewhal/nyaa/util/metainfo"
"github.com/microcosm-cc/bluemonday"
"github.com/zeebo/bencode"
)
// UploadForm serializing HTTP form for torrent upload
type UploadForm struct {
Name string
Magnet string
Category string
Description string
captcha.Captcha
Infohash string
CategoryID int
SubCategoryID int
Filesize int64
Filepath string
}
// TODO: these should be in another package (?)
// form value for torrent name
const UploadFormName = "name"
// form value for torrent file
const UploadFormTorrent = "torrent"
// form value for magnet uri (?)
const UploadFormMagnet = "magnet"
// form value for category
const UploadFormCategory = "c"
// form value for description
const UploadFormDescription = "desc"
// 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 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.Magnet = r.FormValue(UploadFormMagnet)
f.Captcha = captcha.Extract(r)
if !captcha.Authenticate(f.Captcha) {
// TODO: Prettier passing of mistyped Captcha errors
return errors.New(captcha.ErrInvalidCaptcha.Error())
}
// trim whitespace
f.Name = util.TrimWhitespaces(f.Name)
f.Description = p.Sanitize(util.TrimWhitespaces(f.Description))
f.Magnet = util.TrimWhitespaces(f.Magnet)
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
}
f.CategoryID = CatID
f.SubCategoryID = SubCatID
} else {
return ErrInvalidTorrentCategory
}
// 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()
if !CheckTrackers(trackers) {
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())
} else {
// No torrent file provided
magnetURL, parseErr := url.Parse(f.Magnet)
if parseErr != nil {
return metainfo.ErrInvalidTorrentFile
}
exactTopic := magnetURL.Query().Get("xt")
if !strings.HasPrefix(exactTopic, "urn:btih:") {
return metainfo.ErrInvalidTorrentFile
}
f.Infohash = strings.ToUpper(strings.TrimPrefix(exactTopic, "urn:btih:"))
matched, err := regexp.MatchString("^[0-9A-F]{40}$", f.Infohash)
if err != nil || !matched {
return metainfo.ErrInvalidTorrentFile
}
f.Filesize = 0
f.Filepath = ""
}
// 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 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)
}
func CheckTrackers(trackers []string) bool {
// TODO: move to runtime configuration
var deadTrackers = []string{ // substring matches!
"://open.nyaatorrents.info:6544",
"://tracker.openbittorrent.com:80",
"://tracker.publicbt.com:80",
"://stats.anisource.net:2710",
"://exodus.desync.com",
"://open.demonii.com:1337",
"://tracker.istole.it:80",
"://tracker.ccc.de:80",
"://bt2.careland.com.cn:6969",
"://announce.torrentsmd.com:8080"}
var numGood int
for _, t := range trackers {
good := true
for _, check := range deadTrackers {
if strings.Contains(t, check) {
good = false
}
}
if good {
numGood++
}
}
return numGood > 0
}
// 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
}