Albirew/nyaa-pantsu
Archivé
1
0
Bifurcation 0

Merge branch 'master' into faq-fix

Cette révision appartient à :
Ramon Dantas 2017-05-07 10:08:00 -03:00 révisé par GitHub
révision 3b2073a984
14 fichiers modifiés avec 202 ajouts et 73 suppressions

Voir le fichier

@ -1,5 +1,6 @@
package config
// remember to update the FAQ when updating these
var Trackers = []string{
"udp://tracker.coppersurfer.tk:6969",
"udp://zer0day.to:1337/announce",

Voir le fichier

@ -82,7 +82,7 @@ func (t *Torrents) ToJson() TorrentsJson {
Status: t.Status,
Hash: t.Hash,
Date: time.Unix(t.Date, 0).Format(time.RFC3339),
Filesize: util.FormatFilesize(t.Filesize),
Filesize: util.FormatFilesize2(t.Filesize),
Description: template.HTML(t.Description),
Comments: b,
Sub_Category: strconv.Itoa(t.Sub_Category),

31
public/js/uploadPage.js Fichier normal
Voir le fichier

@ -0,0 +1,31 @@
(function() {
var torrent = $("input[name=torrent]"),
magnet = $("input[name=magnet]"),
name = $("input[name=name]");
torrent.on("change", function() {
if (torrent.val() == "") {
enableField(magnet);
name.attr("required", "");
} else {
disableField(magnet);
// .torrent file will allow autofilling name
name.removeAttr("required", "");
}
});
magnet.on("change", function() {
if (magnet.val() == "")
enableField(torrent);
else
disableField(torrent);
});
function enableField(e) {
e.attr("required", "")
.removeAttr("disabled");
}
function disableField(e) {
e.attr("disabled", "")
.removeAttr("required");
}
})();

Voir le fichier

@ -7,7 +7,7 @@ import (
var TemplateDir = "templates"
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate *template.Template
var homeTemplate, searchTemplate, faqTemplate, uploadTemplate, viewTemplate, viewRegisterTemplate, viewLoginTemplate, viewRegisterSuccessTemplate *template.Template
type templateLoader struct {
templ **template.Template
@ -43,6 +43,21 @@ func ReloadTemplates() {
name: "view",
file: "view.html",
},
templateLoader{
templ: &viewRegisterTemplate,
name: "user_register",
file: "user/register.html",
},
templateLoader{
templ: &viewRegisterSuccessTemplate,
name: "user_register_success",
file: "user/signup_success.html",
},
templateLoader{
templ: &viewLoginTemplate,
name: "user_login",
file: "user/login.html",
},
}
for _, templ := range templs {
t := template.Must(template.New(templ.name).Funcs(FuncMap).ParseFiles(filepath.Join(TemplateDir, "index.html"), filepath.Join(TemplateDir, templ.file)))

Voir le fichier

@ -7,6 +7,7 @@ import (
"net/url"
"strconv"
"strings"
"regexp"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/util"
@ -21,10 +22,12 @@ type UploadForm struct {
Magnet string
Infohash string
Category string
CategoryId int
SubCategoryId int
Description string
captcha.Captcha
CategoryId int
SubCategoryId int
Filesize int64
}
// TODO: these should be in another package (?)
@ -44,9 +47,17 @@ 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
// FIXME: hardcoded link
var ErrTrackerProblem = errors.New("torrent does not have any (working) trackers: https://nyaa.pantsu.cat/faq#trackers")
// error indicating a torrent's name is invalid
var ErrInvalidTorrentName = errors.New("torrent name is invalid")
@ -74,15 +85,6 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
f.Description = p.Sanitize(util.TrimWhitespaces(f.Description))
f.Magnet = util.TrimWhitespaces(f.Magnet)
if len(f.Name) == 0 {
return ErrInvalidTorrentName
}
if len(f.Description) == 0 {
return ErrInvalidTorrentDescription
}
catsSplit := strings.Split(f.Category, "_")
// need this to prevent out of index panics
if len(catsSplit) == 2 {
@ -101,13 +103,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
return ErrInvalidTorrentCategory
}
if len(f.Magnet) == 0 {
// try parsing torrent file if provided if no magnet is specified
tfile, _, err := r.FormFile(UploadFormTorrent)
if err != nil {
return err
}
// first: parse torrent file (if any) to fill missing information
tfile, _, err := r.FormFile(UploadFormTorrent)
if err == nil {
var torrent metainfo.TorrentFile
// decode torrent
err = bencode.NewDecoder(tfile).Decode(&torrent)
@ -115,17 +114,32 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
return metainfo.ErrInvalidTorrentFile
}
// check if torrent is private
// check a few things
if torrent.IsPrivate() {
return ErrPrivateTorrent
}
trackers := torrent.GetAllAnnounceURLS()
if !CheckTrackers(trackers) {
return ErrTrackerProblem
}
// generate magnet
// 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 := torrent.Infohash()
f.Infohash = hex.EncodeToString(binInfohash[:])
f.Infohash = strings.ToUpper(hex.EncodeToString(binInfohash[:]))
f.Magnet = util.InfoHashToMagnet(f.Infohash, f.Name)
f.Infohash = strings.ToUpper(f.Infohash)
// extract filesize
f.Filesize = int64(torrent.TotalSize())
} else {
// No torrent file provided
magnetUrl, parseErr := url.Parse(f.Magnet)
if parseErr != nil {
return metainfo.ErrInvalidTorrentFile
@ -133,14 +147,57 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error {
exactTopic := magnetUrl.Query().Get("xt")
if !strings.HasPrefix(exactTopic, "urn:btih:") {
return metainfo.ErrInvalidTorrentFile
} else {
f.Infohash = strings.ToUpper(strings.TrimPrefix(exactTopic, "urn:btih:"))
}
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
}
// then actually check that we have everything we need
if len(f.Name) == 0 {
return ErrInvalidTorrentName
}
//if len(f.Description) == 0 {
// return ErrInvalidTorrentDescription
//}
return nil
}
var dead_trackers = []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"}
func CheckTrackers(trackers []string) bool {
var numGood int
for _, t := range trackers {
var good bool = true
for _, check := range dead_trackers {
if strings.Contains(t, check) {
good = false
}
}
if good {
numGood += 1
}
}
return numGood > 0
}
// NewUploadForm creates a new upload form given parameters as list
func NewUploadForm(params ...string) (uploadForm UploadForm) {
if len(params) > 1 {

Voir le fichier

@ -17,6 +17,7 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
var uploadForm UploadForm
if r.Method == "POST" {
defer r.Body.Close()
// validation is done in ExtractInfo()
err = uploadForm.ExtractInfo(r)
if err == nil {
if !captcha.Authenticate(uploadForm.Captcha) {
@ -25,7 +26,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
return
}
//validate name + hash
//add to db and redirect depending on result
torrent := model.Torrents{
Name: uploadForm.Name,
@ -34,9 +34,10 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
Status: 1,
Hash: uploadForm.Infohash,
Date: time.Now().Unix(),
Filesize: uploadForm.Filesize, // FIXME: should set to NULL instead of 0
Description: uploadForm.Description,
Comments: []byte{}}
fmt.Printf("%+v\n", torrent)
//fmt.Printf("%+v\n", torrent)
db.ORM.Create(&torrent)
fmt.Printf("%+v\n", torrent)
url, err := Router.Get("view_torrent").URL("id", strconv.Itoa(torrent.Id))
@ -44,7 +45,6 @@ func UploadHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, url.String(), 302)
}
}
fmt.Printf("%+v\n", uploadForm)
} else if r.Method == "GET" {
uploadForm.CaptchaID = captcha.GetID(r.RemoteAddr)
htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}

Voir le fichier

@ -1,9 +1,9 @@
package router
import (
"html/template"
"net/http"
"github.com/ewhal/nyaa/service/captcha"
"github.com/ewhal/nyaa/service/user"
"github.com/ewhal/nyaa/service/user/form"
"github.com/ewhal/nyaa/util/languages"
@ -11,23 +11,14 @@ import (
"github.com/gorilla/mux"
)
var viewRegisterTemplate = template.Must(template.New("userRegister").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/register.html"))
var viewLoginTemplate = template.Must(template.New("userLogin").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/login.html"))
var viewRegisterSuccessTemplate = template.Must(template.New("userRegisterSuccess").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/user/signup_success.html"))
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html"))
func init() {
template.Must(viewRegisterTemplate.ParseGlob("templates/_*.html"))
template.Must(viewLoginTemplate.ParseGlob("templates/_*.html"))
template.Must(viewRegisterSuccessTemplate.ParseGlob("templates/_*.html"))
}
// Getting View User Registration
func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) {
b := form.RegistrationForm{}
modelHelper.BindValueForm(&b, r)
b.CaptchaID = captcha.GetID(r.RemoteAddr)
languages.SetTranslation("en-us", viewRegisterTemplate)
htv := UserRegisterTemplateVariables{b, NewSearchForm(), Navigation{}, r.URL, mux.CurrentRoute(r)}
err := viewRegisterTemplate.ExecuteTemplate(w, "index.html", htv)
@ -58,9 +49,14 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) {
}
// Post Registration controller
// Post Registration controller, we do some check on the form here, the rest on user service
func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) {
// Check same Password
if !captcha.Authenticate(captcha.Extract(r)) {
// TODO: Prettier passing of mistyoed captcha errors
http.Error(w, captcha.ErrInvalidCaptcha.Error(), 403)
return
}
if (r.PostFormValue("password") == r.PostFormValue("password_confirm")) && (r.PostFormValue("password") != "") {
if (form.EmailValidation(r.PostFormValue("email"))) && (form.ValidateUsername(r.PostFormValue("username"))) {
_, err := userService.CreateUser(w, r)

Voir le fichier

@ -41,52 +41,52 @@ func ValidateUsername(username string) bool {
// RegistrationForm is used when creating a user.
type RegistrationForm struct {
Username string `form:"registrationUsername" binding:"required"`
Email string `form:"registrationEmail" binding:"required"`
Password string `form:"registrationPassword" binding:"required"`
CaptchaID string `form:"captchaID" binding:"required"`
Username string `form:"registrationUsername"`
Email string `form:"registrationEmail"`
Password string `form:"registrationPassword"`
CaptchaID string `form:"captchaID" inmodel:"false"`
}
// RegistrationForm is used when creating a user authentication.
type LoginForm struct {
Email string `form:"email" binding:"required"`
Password string `form:"password" binding:"required"`
Email string `form:"email"`
Password string `form:"password"`
}
// UserForm is used when updating a user.
type UserForm struct {
Email string `form:"email" binding:"required"`
Email string `form:"email"`
}
// PasswordForm is used when updating a user password.
type PasswordForm struct {
CurrentPassword string `form:"currentPassword" binding:"required"`
Password string `form:"newPassword" binding:"required"`
CurrentPassword string `form:"currentPassword"`
Password string `form:"newPassword"`
}
// SendPasswordResetForm is used when sending a password reset token.
type SendPasswordResetForm struct {
Email string `form:"email" binding:"required"`
Email string `form:"email"`
}
// PasswordResetForm is used when reseting a password.
type PasswordResetForm struct {
PasswordResetToken string `form:"token" binding:"required"`
Password string `form:"newPassword" binding:"required"`
PasswordResetToken string `form:"token"`
Password string `form:"newPassword"`
}
// VerifyEmailForm is used when verifying an email.
type VerifyEmailForm struct {
ActivationToken string `form:"token" binding:"required"`
ActivationToken string `form:"token"`
}
// ActivateForm is used when activating user.
type ActivateForm struct {
Activation bool `form:"activation" binding:"required"`
Activation bool `form:"activation"`
}
// UserRoleForm is used when adding or removing a role from a user.
type UserRoleForm struct {
UserId int `form:"userId" binding:"required"`
RoleId int `form:"roleId" binding:"required"`
UserId int `form:"userId"`
RoleId int `form:"roleId"`
}

Voir le fichier

@ -43,6 +43,18 @@
<p>The magnet link should look like this: <span style="font-family:monospace">
magnet:?xt=urn:btih:[hash]&amp;dn=[name]&amp;tr=[tracker]&amp;tr=[...]</span></p>
<h2 id="trackers">Which trackers do you recommend using?</h2>
<p>If your torrent upload is denied because of trackers you'll need to add some of these:</p>
<pre>udp://tracker.coppersurfer.tk:6969
udp://zer0day.to:1337/announce
udp://tracker.leechers-paradise.org:6969
udp://explodie.org:6969
udp://tracker.opentrackr.org:1337
udp://tracker.internetwarriors.net:1337/announce
udp://eddie4.nl:6969/announce
http://mgtracker.org:6969/announce
http://tracker.baka-sub.cf/announce</pre>
<h2>How can I help?</h2>
<p>If you have website development expertise, you can join the #nyaapantsu IRC channel on irc.rizon.net.
If you have any current databases, especially for sukebei, <b>UPLOAD THEM</b>.</p>
@ -54,7 +66,8 @@
<p>It's the author's favorite programming language.</p>
<br />
<img style="max-width: 100%" src="https://my.mixtape.moe/omrskw.png">
<img style="max-width: 100%" src="https://my.mixtape.moe/omrskw.png" alt="funny meme">
<br />
<h2>nyaa.pantsu.cat and sukebei.pantsu.cat do not host any files.</h2>

Voir le fichier

@ -1,6 +1,7 @@
{{define "captcha"}}
<div class="form-group captcha-container">
<input type="text" name="capthcaID" value="{{.CaptchaID}}" hidden>
<label for="solution">Captcha</label>
<input type="text" name="captchaID" value="{{.CaptchaID}}" hidden>
<img src="/captcha/{{.CaptchaID}}.png">
<input type="number" name="solution" class="form-control" placeholder="Captcha" required>
</div>

Voir le fichier

@ -6,16 +6,16 @@
<form enctype="multipart/form-data" role="upload" method="POST">
<div class="form-group">
<label for="torrent">Torrent file upload</label>
<label for="name">Name</label>
<input type="text" name="name" class="form-control" placeholder="File Name" value="{{.Name}}" required>
</div>
<div class="form-group">
<label for="torrent">Torrent file</label>
<input type="file" name="torrent" id="torrent" accept=".torrent">
<p class="help-block">Upload a torrent file to pre-fill information</p>
<p class="help-block">Uploading a torrent file allows pre-filling some fields, this is recommended.</p>
</div>
<div class="form-group">
<label for="name">FileName</label>
<input type="text" name="name" class="form-control"placeholder="File Name" value="{{.Name}}" required>
</div>
<div class="form-group">
<label for="Magnet">Magnet Link</label>
<label for="magnet">Magnet Link</label>
<input type="text" name="magnet" class="form-control"
style="width:60rem" placeholder="Magnet Link" value="{{.Magnet}}">
</div>
@ -45,6 +45,7 @@
<div class="form-group">
<label for="desc">Torrent Description</label>
<p class="help-block">A limited set of HTML is allowed in the description, make sure to use <span style="font-family:monospace">&lt;br/&gt;</span>.</p>
<textarea name="desc" class="form-control" rows="10">{{.Description}}</textarea>
</div>
@ -56,4 +57,7 @@
</form>
{{end}}
</div>
{{end}}
{{end}}
{{define "js_footer"}}
<script type="text/javascript" charset="utf-8" src="{{.URL.Parse "/js/uploadPage.js"}}"></script>
{{end}}

Voir le fichier

@ -25,3 +25,11 @@ func FormatFilesize(bytes int64) string {
}
return fmt.Sprintf("%.1f %s", value, unit)
}
func FormatFilesize2(bytes int64) string {
if bytes == 0 { // this is what gorm returns for NULL
return "Unknown"
} else {
return FormatFilesize(bytes)
}
}

Voir le fichier

@ -110,7 +110,7 @@ func (tf *TorrentFile) TorrentName() string {
// return true if this torrent is private otherwise return false
func (tf *TorrentFile) IsPrivate() bool {
return tf.Info.Private == nil || *tf.Info.Private == 0
return tf.Info.Private != nil && *tf.Info.Private == 1
}
// calculate infohash

Voir le fichier

@ -17,12 +17,15 @@ func AssignValue(model interface{}, form interface{}) {
formElem := reflect.ValueOf(form).Elem()
typeOfTForm := formElem.Type()
for i := 0; i < formElem.NumField(); i++ {
modelField := modelIndirect.FieldByName(typeOfTForm.Field(i).Name)
if modelField.IsValid() {
formField := formElem.Field(i)
modelField.Set(formField)
} else {
log.Warnf("modelField : %s - %s", typeOfTForm.Field(i).Name, modelField)
tag := typeOfTForm.Field(i).Tag
if tag.Get("omit") != "false" {
modelField := modelIndirect.FieldByName(typeOfTForm.Field(i).Name)
if modelField.IsValid() {
formField := formElem.Field(i)
modelField.Set(formField)
} else {
log.Warnf("modelField : %s - %s", typeOfTForm.Field(i).Name, modelField)
}
}
}
}