Albirew/nyaa-pantsu
Albirew
/
nyaa-pantsu
Archivé
1
0
Bifurcation 0

Finally services are nearly empty 👍

Moved api services. All validations are now in validator util and all upload functions are in upload util
Cette révision appartient à :
akuma06 2017-07-01 23:09:35 +02:00
Parent 1316062c9a
révision 205daea027
37 fichiers modifiés avec 729 ajouts et 711 suppressions

Voir le fichier

@ -192,7 +192,7 @@ func (p *TorrentParam) ToFilterQuery() string {
* We decided to fetch only the ids from ES and then query these ids to the
* database
*/
func (p *TorrentParam) Find(client *elastic.Client) (int64, []model.Torrent, error) {
func (p *TorrentParam) Find(client *elastic.Client) (int64, []models.Torrent, error) {
// TODO Why is it needed, what does it do ?
ctx := context.Background()
@ -231,13 +231,13 @@ func (p *TorrentParam) Find(client *elastic.Client) (int64, []model.Torrent, err
log.Infof("Query '%s' took %d milliseconds.", p.NameLike, result.TookInMillis)
log.Infof("Amount of results %d.", result.TotalHits())
torrents := make([]model.Torrent, len(result.Hits.Hits))
torrents := make([]models.Torrent, len(result.Hits.Hits))
if len(result.Hits.Hits) <= 0 {
return 0, nil, nil
}
for i, hit := range result.Hits.Hits {
// Deserialize hit.Source into a Tweet (could also be just a map[string]interface{}).
var tJson model.TorrentJSON
var tJson models.TorrentJSON
err := json.Unmarshal(*hit.Source, &tJson)
if err != nil {
log.Errorf("Cannot unmarshal elasticsearch torrent: %s", err)

Voir le fichier

@ -75,8 +75,8 @@ func APIHandler(c *gin.Context) {
return
}
b := model.APIResultJSON{
Torrents: model.APITorrentsToJSON(torrents),
b := models.APIResultJSON{
Torrents: models.APITorrentsToJSON(torrents),
}
b.QueryRecordCount = req.MaxPerPage
b.TotalRecordCount = nbTorrents
@ -157,18 +157,18 @@ func APIUploadHandler(c *gin.Context) {
}
if !messages.HasErrors() {
status := model.TorrentStatusNormal
status := models.TorrentStatusNormal
if upload.Remake { // overrides trusted
status = model.TorrentStatusRemake
status = models.TorrentStatusRemake
} else if user.IsTrusted() {
status = model.TorrentStatusTrusted
status = models.TorrentStatusTrusted
}
err = torrentService.ExistOrDelete(upload.Infohash, user)
if err != nil {
messages.Error(err)
}
if !messages.HasErrors() {
torrent := model.Torrent{
torrent := models.Torrent{
Name: upload.Name,
Category: upload.CategoryID,
SubCategory: upload.SubCategoryID,
@ -198,7 +198,7 @@ func APIUploadHandler(c *gin.Context) {
// add filelist to files db, if we have one
if len(upload.FileList) > 0 {
for _, uploadedFile := range upload.FileList {
file := model.File{TorrentID: torrent.ID, Filesize: upload.Filesize}
file := models.File{TorrentID: torrent.ID, Filesize: upload.Filesize}
err := file.SetPath(uploadedFile.Path)
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
@ -247,7 +247,7 @@ func APIUpdateHandler(c *gin.Context) {
messages.Error(err)
}
if !messages.HasErrors() {
torrent := model.Torrent{}
torrent := models.Torrent{}
db.ORM.Where("torrent_id = ?", c.PostForm("id")).First(&torrent)
if torrent.ID == 0 {
messages.Error(apiService.ErrTorrentID)
@ -286,7 +286,7 @@ func APISearchHandler(c *gin.Context) {
return
}
b := model.APITorrentsToJSON(torrents)
b := models.APITorrentsToJSON(torrents)
c.JSON(http.StatusOK, b)
}

Voir le fichier

@ -25,7 +25,7 @@ func DatabaseDumpHandler(c *gin.Context) {
// TODO Use config from cli
files, _ := filepath.Glob(filepath.Join(DatabaseDumpPath, "*.torrent"))
var dumpsJSON []model.DatabaseDumpJSON
var dumpsJSON []models.DatabaseDumpJSON
// TODO Filter *.torrent files
for _, f := range files {
// TODO Use config from cli
@ -40,7 +40,7 @@ func DatabaseDumpHandler(c *gin.Context) {
fmt.Println(err)
continue
}
dump := model.DatabaseDump{
dump := models.DatabaseDump{
Date: time.Now(),
Filesize: int64(tf.TotalSize()),
Name: tf.TorrentName(),

Voir le fichier

@ -46,7 +46,7 @@ func newSearchForm(c *gin.Context) searchForm {
ToDate: c.Query("toDate"), // We need to overwrite the value here, since date are formatted
}
}
func getUser(c *gin.Context) *model.User {
func getUser(c *gin.Context) *models.User {
user, _, _ := userService.RetrieveCurrentUser(c)
return &user
}

Voir le fichier

@ -114,7 +114,7 @@ func IndexModPanel(c *gin.Context) {
comments, _ := commentService.GetAllComments(offset, 0, "", "")
torrentReports, _, _ := reportService.GetAllTorrentReports(offset, 0)
panelAdminTemplate(c, torrents, model.TorrentReportsToJSON(torrentReports), users, comments)
panelAdminTemplate(c, torrents, models.TorrentReportsToJSON(torrentReports), users, comments)
}
// TorrentsListPanel : Controller for listing torrents, can accept common search arguments
@ -180,7 +180,7 @@ func TorrentReportListPanel(c *gin.Context) {
torrentReports, nbReports, _ := reportService.GetAllTorrentReports(offset, (pagenum-1)*offset)
reportJSON := model.TorrentReportsToJSON(torrentReports)
reportJSON := models.TorrentReportsToJSON(torrentReports)
nav := navigation{nbReports, offset, pagenum, "mod_trlist_page"}
modelList(c, "admin/torrent_report.jet.html", reportJSON, nav, newSearchForm(c))
}
@ -275,7 +275,7 @@ func TorrentPostEditModPanel(c *gin.Context) {
messages.AddInfoT("infos", "torrent_updated")
if err == nil { // We only log edit torrent for admins
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
activity.Log(&model.User{}, torrent.Identifier(), "edit", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), "edit", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
}
}
}
@ -288,7 +288,7 @@ func CommentDeleteModPanel(c *gin.Context) {
comment, _, err := commentService.DeleteComment(id)
if err == nil {
activity.Log(&model.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), comment.User.Username, getUser(c).Username)
activity.Log(&models.User{}, comment.Identifier(), "delete", "comment_deleted_by", strconv.Itoa(int(comment.ID)), comment.User.Username, getUser(c).Username)
}
c.Redirect(http.StatusSeeOther, "/mod/comments?deleted")
@ -301,7 +301,7 @@ func TorrentDeleteModPanel(c *gin.Context) {
var returnRoute string
var err error
var torrent *model.Torrent
var torrent *models.Torrent
if definitely != nil {
torrent, _, err = torrentService.DefinitelyDeleteTorrent(id)
@ -325,7 +325,7 @@ func TorrentDeleteModPanel(c *gin.Context) {
}
if err == nil {
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
activity.Log(&model.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
}
c.Redirect(http.StatusSeeOther, returnRoute+"?deleted")
@ -340,7 +340,7 @@ func TorrentReportDeleteModPanel(c *gin.Context) {
_, _, _ = reportService.DeleteTorrentReport(uint(idNum))
/* If we need to log report delete activity
if err == nil {
activity.Log(&model.User{}, torrent.Identifier(), "delete", "torrent_report_deleted_by", strconv.Itoa(int(report.ID)), getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_report_deleted_by", strconv.Itoa(int(report.ID)), getUser(c).Username)
}
*/
c.Redirect(http.StatusSeeOther, "/mod/reports?deleted")
@ -469,7 +469,7 @@ func TorrentBlockModPanel(c *gin.Context) {
}
if err == nil {
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
activity.Log(&model.User{}, torrent.Identifier(), action, "torrent_"+action+"_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), action, "torrent_"+action+"_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
}
c.Redirect(http.StatusSeeOther, returnRoute+"?"+action)
@ -513,8 +513,8 @@ func torrentManyAction(c *gin.Context) {
}
if !userPermission.HasAdmin(currentUser) {
if c.PostForm("status") != "" { // Condition to check if a user try to change torrent status without having the right permission
if (status == model.TorrentStatusTrusted && !currentUser.IsTrusted()) || status == model.TorrentStatusAPlus || status == 0 {
status = model.TorrentStatusNormal
if (status == models.TorrentStatusTrusted && !currentUser.IsTrusted()) || status == models.TorrentStatusAPlus || status == 0 {
status = models.TorrentStatusNormal
}
}
if c.PostForm("owner") != "" { // Only admins can change owner of torrents
@ -573,10 +573,10 @@ func torrentManyAction(c *gin.Context) {
_, err := torrentService.UpdateUnscopeTorrent(&torrent)
if err == nil {
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
activity.Log(&model.User{}, torrent.Identifier(), "edited", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), "edited", "torrent_edited_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
}
} else if action == "delete" {
if status == model.TorrentStatusBlocked { // Then we should lock torrents before deleting them
if status == models.TorrentStatusBlocked { // Then we should lock torrents before deleting them
torrent.Status = status
messages.AddInfoTf("infos", "torrent_moved", torrent.Name)
torrentService.UpdateUnscopeTorrent(&torrent)
@ -587,7 +587,7 @@ func torrentManyAction(c *gin.Context) {
} else {
messages.AddInfoTf("infos", "torrent_deleted", torrent.Name)
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
activity.Log(&model.User{}, torrent.Identifier(), "deleted", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
activity.Log(&models.User{}, torrent.Identifier(), "deleted", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, getUser(c).Username)
}
} else {
messages.AddErrorTf("errors", "no_action_exist", action)

Voir le fichier

@ -350,7 +350,7 @@ func RSSTorznabHandler(c *gin.Context) {
}
}
func getTorrentList(c *gin.Context) (torrents []model.Torrent, createdAsTime time.Time, title string, err error) {
func getTorrentList(c *gin.Context) (torrents []models.Torrent, createdAsTime time.Time, title string, err error) {
page := c.Param("page")
userID := c.Param("id")
cat := c.Query("cat")

Voir le fichier

@ -49,5 +49,5 @@ func SearchHandler(c *gin.Context) {
searchForm := newSearchForm(c)
searchForm.SearchParam, searchForm.Category = searchParam, category
modelList(c, "site/torrents/listing.jet.html", model.TorrentsToJSON(torrents), nav, searchForm)
modelList(c, "site/torrents/listing.jet.html", models.TorrentsToJSON(torrents), nav, searchForm)
}

Voir le fichier

@ -128,7 +128,7 @@ func formTemplate(c *gin.Context, templateName string, form interface{}) {
renderTemplate(c, templateName, vars)
}
func torrentTemplate(c *gin.Context, torrent model.TorrentJSON, rootFolder *filelist.FileListFolder, captchaID string) {
func torrentTemplate(c *gin.Context, torrent models.TorrentJSON, rootFolder *filelist.FileListFolder, captchaID string) {
vars := commonVars(c)
vars.Set("Torrent", torrent)
vars.Set("RootFolder", rootFolder)
@ -136,7 +136,7 @@ func torrentTemplate(c *gin.Context, torrent model.TorrentJSON, rootFolder *file
renderTemplate(c, path.Join(SiteDir, "torrents/view.jet.html"), vars)
}
func userProfileEditTemplate(c *gin.Context, userProfile *model.User, userForm userForms.UserForm, languages map[string]string) {
func userProfileEditTemplate(c *gin.Context, userProfile *models.User, userForm userForms.UserForm, languages map[string]string) {
vars := commonVars(c)
vars.Set("UserProfile", userProfile)
vars.Set("UserForm", userForm)
@ -144,18 +144,18 @@ func userProfileEditTemplate(c *gin.Context, userProfile *model.User, userForm u
renderTemplate(c, path.Join(SiteDir, "user/edit.jet.html"), vars)
}
func userProfileTemplate(c *gin.Context, userProfile *model.User) {
func userProfileTemplate(c *gin.Context, userProfile *models.User) {
vars := commonVars(c)
vars.Set("UserProfile", userProfile)
renderTemplate(c, path.Join(SiteDir, "user/torrents.jet.html"), vars)
}
func userProfileNotificationsTemplate(c *gin.Context, userProfile *model.User) {
func userProfileNotificationsTemplate(c *gin.Context, userProfile *models.User) {
vars := commonVars(c)
vars.Set("UserProfile", userProfile)
renderTemplate(c, path.Join(SiteDir, "user/notifications.jet.html"), vars)
}
func databaseDumpTemplate(c *gin.Context, listDumps []model.DatabaseDumpJSON, GPGLink string) {
func databaseDumpTemplate(c *gin.Context, listDumps []models.DatabaseDumpJSON, GPGLink string) {
vars := commonVars(c)
vars.Set("ListDumps", listDumps)
vars.Set("GPGLink", GPGLink)
@ -168,7 +168,7 @@ func changeLanguageTemplate(c *gin.Context, language string, languages map[strin
renderTemplate(c, path.Join(SiteDir, "user/public/settings.jet.html"), vars)
}
func panelAdminTemplate(c *gin.Context, torrent []model.Torrent, reports []model.TorrentReportJSON, users []model.User, comments []model.Comment) {
func panelAdminTemplate(c *gin.Context, torrent []models.Torrent, reports []models.TorrentReportJSON, users []models.User, comments []models.Comment) {
vars := newPanelCommonVariables(c)
vars.Set("Torrents", torrent)
vars.Set("TorrentReports", reports)

Voir le fichier

@ -151,7 +151,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap {
vars.Set("NeedsCaptcha", userPermission.NeedsCaptcha)
vars.Set("GetRole", userPermission.GetRole)
vars.Set("IsFollower", userPermission.IsFollower)
vars.Set("DisplayTorrent", func(t model.Torrent, u *model.User) bool {
vars.Set("DisplayTorrent", func(t models.Torrent, u *models.User) bool {
return ((!t.Hidden && t.Status != 0) || userPermission.CurrentOrAdmin(u, t.UploaderID))
})
vars.Set("NoEncode", func(str string) template.HTML {
@ -242,7 +242,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap {
IdentifierChain string
}{f, nestLevel, T, identifierChain}
})
vars.Set("lastID", func(currentUrl *url.URL, torrents []model.TorrentJSON) int {
vars.Set("lastID", func(currentUrl *url.URL, torrents []models.TorrentJSON) int {
values := currentUrl.Query()
order := false
@ -285,7 +285,7 @@ func templateFunctions(vars jet.VarMap) jet.VarMap {
return template.HTML("<a href=\"" + url + "\">" + username + "</a>")
})
vars.Set("genActivityContent", func(a model.Activity, T publicSettings.TemplateTfunc) template.HTML {
vars.Set("genActivityContent", func(a models.Activity, T publicSettings.TemplateTfunc) template.HTML {
return activity.ToLocale(&a, T)
})
return vars

Voir le fichier

@ -43,15 +43,15 @@ func walkDirTest(dir string, t *testing.T) {
contextVars := ContextTest{
"dumps.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("GPGLink", "")
vars.Set("ListDumps", []model.DatabaseDumpJSON{})
vars.Set("ListDumps", []models.DatabaseDumpJSON{})
return vars
},
"activities.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.Activity{})
vars.Set("Models", []models.Activity{})
return vars
},
"listing.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.TorrentJSON{})
vars.Set("Models", []models.TorrentJSON{})
return vars
},
"edit.jet.html": func(vars jet.VarMap) jet.VarMap {
@ -64,7 +64,7 @@ func walkDirTest(dir string, t *testing.T) {
return vars
},
"view.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Torrent", &model.TorrentJSON{})
vars.Set("Torrent", &models.TorrentJSON{})
vars.Set("CaptchaID", "xxxxxx")
return vars
},
@ -78,14 +78,14 @@ func walkDirTest(dir string, t *testing.T) {
return vars
},
"index.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Torrents", []model.Torrent{})
vars.Set("Users", []model.User{})
vars.Set("Comments", []model.Comment{})
vars.Set("TorrentReports", []model.TorrentReportJSON{})
vars.Set("Torrents", []models.Torrent{})
vars.Set("Users", []models.User{})
vars.Set("Comments", []models.Comment{})
vars.Set("TorrentReports", []models.TorrentReportJSON{})
return vars
},
"paneltorrentedit.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Form", model.Torrent{})
vars.Set("Form", models.Torrent{})
return vars
},
"reassign.jet.html": func(vars jet.VarMap) jet.VarMap {
@ -93,19 +93,19 @@ func walkDirTest(dir string, t *testing.T) {
return vars
},
"torrentlist.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.Torrent{})
vars.Set("Models", []models.Torrent{})
return vars
},
"userlist.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.User{})
vars.Set("Models", []models.User{})
return vars
},
"commentlist.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.Comment{})
vars.Set("Models", []models.Comment{})
return vars
},
"torrent_report.jet.html": func(vars jet.VarMap) jet.VarMap {
vars.Set("Models", []model.TorrentReportJSON{})
vars.Set("Models", []models.TorrentReportJSON{})
return vars
},
}
@ -175,13 +175,13 @@ func mockupCommonVars(t *testing.T) jet.VarMap {
vars.Set("Theme", "")
vars.Set("Mascot", "")
vars.Set("MascotURL", "")
vars.Set("User", &model.User{})
vars.Set("UserProfile", &model.User{})
vars.Set("User", &models.User{})
vars.Set("UserProfile", &models.User{})
vars.Set("URL", &url.URL{})
vars.Set("CsrfToken", "xxxxxx")
vars.Set("Config", config.Conf)
vars.Set("Infos", make(map[string][]string))
vars.Set("Errors", make(map[string][]string))
vars.Set("UserProfile", &model.User{})
vars.Set("UserProfile", &models.User{})
return vars
}

Voir le fichier

@ -52,11 +52,11 @@ func UploadPostHandler(c *gin.Context) {
if err != nil {
messages.AddError("errors", err.Error())
}
status := model.TorrentStatusNormal
status := models.TorrentStatusNormal
if uploadForm.Remake { // overrides trusted
status = model.TorrentStatusRemake
status = models.TorrentStatusRemake
} else if user.IsTrusted() {
status = model.TorrentStatusTrusted
status = models.TorrentStatusTrusted
}
err = torrentService.ExistOrDelete(uploadForm.Infohash, user)
@ -66,7 +66,7 @@ func UploadPostHandler(c *gin.Context) {
if !messages.HasErrors() {
// add to db and redirect
torrent := model.Torrent{
torrent := models.Torrent{
Name: uploadForm.Name,
Category: uploadForm.CategoryID,
SubCategory: uploadForm.SubCategoryID,
@ -96,7 +96,7 @@ func UploadPostHandler(c *gin.Context) {
// add filelist to files db, if we have one
if len(uploadForm.FileList) > 0 {
for _, uploadedFile := range uploadForm.FileList {
file := model.File{TorrentID: torrent.ID, Filesize: uploadedFile.Filesize}
file := models.File{TorrentID: torrent.ID, Filesize: uploadedFile.Filesize}
err := file.SetPath(uploadedFile.Path)
if err != nil {
messages.AddError("errors", err.Error())

Voir le fichier

@ -73,7 +73,7 @@ func UserProfileHandler(c *gin.Context) {
query.Set("userID", id)
query.Set("max", "16")
c.Request.URL.RawQuery = query.Encode()
var torrents []model.Torrent
var torrents []models.Torrent
var err error
if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) {
_, torrents, _, err = search.SearchByQuery(c, 1)
@ -257,7 +257,7 @@ func UserNotificationsHandler(c *gin.Context) {
if c.Request.URL.Query()["clear"] != nil {
notifierService.DeleteAllNotifications(currentUser.ID)
messages.AddInfoT("infos", "notifications_cleared")
currentUser.Notifications = []model.Notification{}
currentUser.Notifications = []models.Notification{}
}
userProfileNotificationsTemplate(c, currentUser)
} else {

Voir le fichier

@ -103,7 +103,7 @@ func PostCommentHandler(c *gin.Context) {
if !messages.HasErrors() {
userID := currentUser.ID
comment := model.Comment{TorrentID: torrent.ID, UserID: userID, Content: content, CreatedAt: time.Now()}
comment := models.Comment{TorrentID: torrent.ID, UserID: userID, Content: content, CreatedAt: time.Now()}
err := db.ORM.Create(&comment).Error
if err != nil {
messages.Error(err)
@ -139,7 +139,7 @@ func ReportTorrentHandler(c *gin.Context) {
idNum, _ := strconv.Atoi(id)
userID := currentUser.ID
report := model.TorrentReport{
report := models.TorrentReport{
Description: c.PostForm("report_type"),
TorrentID: uint(idNum),
UserID: userID,
@ -164,7 +164,7 @@ func TorrentEditUserPanel(c *gin.Context) {
uploadForm := apiService.NewTorrentRequest()
uploadForm.Name = torrent.Name
uploadForm.Category = strconv.Itoa(torrent.Category) + "_" + strconv.Itoa(torrent.SubCategory)
uploadForm.Remake = torrent.Status == model.TorrentStatusRemake
uploadForm.Remake = torrent.Status == models.TorrentStatusRemake
uploadForm.WebsiteLink = string(torrent.WebsiteLink)
uploadForm.Description = string(torrent.Description)
uploadForm.Hidden = torrent.Hidden
@ -188,11 +188,11 @@ func TorrentPostEditUserPanel(c *gin.Context) {
messages.AddErrorT("errors", "fail_torrent_update")
}
if !messages.HasErrors() {
status := model.TorrentStatusNormal
status := models.TorrentStatusNormal
if uploadForm.Remake { // overrides trusted
status = model.TorrentStatusRemake
status = models.TorrentStatusRemake
} else if currentUser.IsTrusted() {
status = model.TorrentStatusTrusted
status = models.TorrentStatusTrusted
}
// update some (but not all!) values
torrent.Name = uploadForm.Name
@ -222,9 +222,9 @@ func TorrentDeleteUserPanel(c *gin.Context) {
if err == nil {
_, username := torrentService.HideTorrentUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
if userPermission.HasAdmin(currentUser) { // We hide username on log activity if user is not admin and torrent is hidden
activity.Log(&model.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username)
activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username)
} else {
activity.Log(&model.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username)
activity.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username)
}
//delete reports of torrent
whereParams := serviceBase.CreateWhereParams("torrent_id = ?", id)

Voir le fichier

@ -9,7 +9,7 @@ import (
// TorrentReport model
// User can be null (anonymous reports)
// FIXME can't preload field Torrents for model.TorrentReport
// FIXME can't preload field Torrents for models.TorrentReport
type TorrentReport struct {
ID uint `gorm:"column:torrent_report_id;primary_key"`
Description string `gorm:"column:type"`

Voir le fichier

@ -248,7 +248,7 @@ func (t *TorrentJSON) ToTorrent() Torrent {
return torrent
}
// ToJSON converts a model.Torrent to its equivalent JSON structure
// ToJSON converts a models.Torrent to its equivalent JSON structure
func (t *Torrent) ToJSON() TorrentJSON {
var trackers []string
if t.Trackers == "" {

Voir le fichier

@ -1,8 +1,8 @@
package users
// DeleteUser deletes a user.
func DeleteUser(currentUser *model.User, id string) (int, error) {
var user model.User
func DeleteUser(currentUser *models.User, id string) (int, error) {
var user models.User
if db.ORM.First(&user, id).RecordNotFound() {
return http.StatusNotFound, errors.New("user_not_found")

Voir le fichier

@ -1,7 +1,7 @@
package users
// UpdateUserCore updates a user. (Applying the modifed data of user).
func Update(user *model.User) (int, error) {
func Update(user *models.User) (int, error) {
if user.Email == "" {
user.MD5 = ""
} else {
@ -22,7 +22,7 @@ func Update(user *model.User) (int, error) {
}
// UpdateRawUser : Function to update a user without updating his associations model
func UpdateRawUser(user *model.User) (int, error) {
func UpdateRawUser(user *models.User) (int, error) {
user.UpdatedAt = time.Now()
err := db.ORM.Model(&user).UpdateColumn(&user).Error
if err != nil {

Voir le fichier

@ -1,469 +0,0 @@
package apiService
import (
"encoding/base32"
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/service"
"github.com/NyaaPantsu/nyaa/util"
"github.com/NyaaPantsu/nyaa/util/categories"
"github.com/NyaaPantsu/nyaa/util/metainfo"
"github.com/NyaaPantsu/nyaa/util/torrentLanguages"
"github.com/NyaaPantsu/nyaa/util/upload"
"github.com/gin-gonic/gin"
"github.com/zeebo/bencode"
)
// form names
const uploadFormName = "name"
const uploadFormTorrent = "torrent"
const uploadFormMagnet = "magnet"
const uploadFormCategory = "c"
const uploadFormRemake = "remake"
const uploadFormDescription = "desc"
const uploadFormWebsiteLink = "website_link"
const uploadFormStatus = "status"
const uploadFormHidden = "hidden"
const uploadFormLanguage = "language"
// 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: " + config.Conf.WebAddress.Nyaa + "/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 website link is invalid
var errInvalidWebsiteLink = errors.New("Website url or IRC link is invalid")
// error indicating a torrent's category is invalid
var errInvalidTorrentCategory = errors.New("Torrent category is invalid")
// error indicating a torrent's language is invalid
var errInvalidTorrentLanguage = errors.New("Torrent language is invalid")
// error indicating that a non-english torrent was uploaded to a english category
var errNonEnglishLanguageInEnglishCategory = errors.New("Torrent's category is for English translations, but torrent language isn't English.")
// error indicating that a english torrent was uploaded to a non-english category
var errEnglishLanguageInNonEnglishCategory = errors.New("Torrent's category is for non-English translations, but torrent language is English.")
type torrentsQuery struct {
Category int `json:"category"`
SubCategory int `json:"sub_category"`
Status int `json:"status"`
Uploader int `json:"uploader"`
Downloads int `json:"downloads"`
}
// TorrentsRequest struct
type TorrentsRequest struct {
Query torrentsQuery `json:"search"`
Page int `json:"page"`
MaxPerPage int `json:"limit"`
}
// APIResultJSON for torrents in json for api
type APIResultJSON struct {
Torrents []TorrentJSON `json:"torrents"`
QueryRecordCount int `json:"queryRecordCount"`
TotalRecordCount int `json:"totalRecordCount"`
}
// ToParams : Convert a torrentsrequest to searchparams
func (r *TorrentsRequest) ToParams() serviceBase.WhereParams {
res := serviceBase.WhereParams{}
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
}
func (r *TorrentRequest) validateName() error {
// then actually check that we have everything we need
if len(r.Name) == 0 {
return errInvalidTorrentName
}
return nil
}
func (r *TorrentRequest) validateDescription() error {
if len(r.Description) > config.Conf.DescriptionLength {
return errInvalidTorrentDescription
}
return nil
}
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 errInvalidWebsiteLink
}
}
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:") {
return ErrMagnet
}
xt = strings.SplitAfter(xt, ":")[2]
r.Infohash = strings.TrimSpace(strings.ToUpper(strings.Split(xt, "&")[0]))
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 {
return ErrHash
}
} 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
}
// ExtractEditInfo : takes an http request and computes all fields for this form
func (r *TorrentRequest) ExtractEditInfo(c *gin.Context) error {
err := r.ExtractBasicValue(c)
if err != nil {
return err
}
err = r.validateName()
if err != nil {
return err
}
err = r.ExtractCategory()
if err != nil {
return err
}
err = r.ExtractLanguage()
return err
}
// 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 errInvalidTorrentCategory
}
CatID, err := strconv.Atoi(catsSplit[0])
if err != nil {
return errInvalidTorrentCategory
}
SubCatID, err := strconv.Atoi(catsSplit[1])
if err != nil {
return errInvalidTorrentCategory
}
if !categories.CategoryExists(r.Category) {
return errInvalidTorrentCategory
}
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.Conf.Torrents.EnglishOnlyCategories {
if cat == r.Category {
isEnglishCategory = true
break
}
}
if r.Language == "" {
// If no language, but in an English category, set to en-us, else just stop the check.
if !isEnglishCategory {
return nil
}
// FIXME Maybe this shouldn't be hard-coded?
r.Language = "en-us"
}
if r.Language == "other" || r.Language == "multiple" {
// In this case, only check if it's on a English-only category.
if isEnglishCategory {
return errNonEnglishLanguageInEnglishCategory
}
return nil
}
if r.Language != "" && !torrentLanguages.LanguageExists(r.Language) {
return errInvalidTorrentLanguage
}
if !strings.HasPrefix(r.Language, "en") {
if isEnglishCategory {
return errNonEnglishLanguageInEnglishCategory
}
} else {
for _, cat := range config.Conf.Torrents.NonEnglishOnlyCategories {
if cat == r.Category {
return errEnglishLanguageInNonEnglishCategory
}
}
}
return nil
}
// ExtractBasicValue : takes an http request and computes all basic fields for this form
func (r *TorrentRequest) ExtractBasicValue(c *gin.Context) error {
c.Bind(r)
// trim whitespace
r.Name = strings.TrimSpace(r.Name)
r.Description = util.Sanitize(strings.TrimSpace(r.Description), "default")
r.WebsiteLink = strings.TrimSpace(r.WebsiteLink)
r.Magnet = strings.TrimSpace(r.Magnet)
// then actually check that we have everything we need
err := r.validateDescription()
if err != nil {
return err
}
err = r.validateWebsiteLink()
return err
}
// ExtractInfo : takes an http request and computes all fields for this form
func (r *TorrentRequest) ExtractInfo(c *gin.Context) error {
err := r.ExtractBasicValue(c)
if err != nil {
return err
}
err = r.ExtractCategory()
if err != nil {
return err
}
err = r.ExtractLanguage()
if err != nil {
return err
}
tfile, err := r.ValidateMultipartUpload(c)
if err != nil {
return err
}
// We check name only here, reason: we can try to retrieve them from the torrent file
err = r.validateName()
if err != nil {
return err
}
// after data has been checked & extracted, write it to disk
if len(config.Conf.Torrents.FileStorage) > 0 {
err := writeTorrentToDisk(tfile, r.Infohash+".torrent", &r.Filepath)
if err != nil {
return err
}
} else {
r.Filepath = ""
}
return nil
}
// ValidateMultipartUpload : Check if multipart upload is valid
func (r *TorrentRequest) ValidateMultipartUpload(c *gin.Context) (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() {
return tfile, errPrivateTorrent
}
trackers := torrent.GetAllAnnounceURLS()
r.Trackers = uploadService.CheckTrackers(trackers)
if len(r.Trackers) == 0 {
return tfile, errTrackerProblem
}
// 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 {
return tfile, errTorrentPlusMagnet
}
_, 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 = util.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
}
// UpdateTorrent : Update torrent model
//rewrite with reflect ?
func (r *UpdateRequest) UpdateTorrent(t *model.Torrent, currentUser *model.User) {
if r.Update.Name != "" {
t.Name = r.Update.Name
}
if r.Update.Infohash != "" {
t.Hash = r.Update.Infohash
}
if r.Update.CategoryID != 0 {
t.Category = r.Update.CategoryID
}
if r.Update.SubCategoryID != 0 {
t.SubCategory = r.Update.SubCategoryID
}
if r.Update.Description != "" {
t.Description = r.Update.Description
}
if r.Update.WebsiteLink != "" {
t.WebsiteLink = r.Update.WebsiteLink
}
status := model.TorrentStatusNormal
if r.Update.Remake { // overrides trusted
status = model.TorrentStatusRemake
} else if currentUser.IsTrusted() {
status = model.TorrentStatusTrusted
}
t.Status = status
}
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.Conf.Torrents.FileStorage, os.PathSeparator, name)
return ioutil.WriteFile(*fullpath, b, 0644)
}
// NewTorrentRequest : creates a new torrent request struc with some default value
func NewTorrentRequest(params ...string) (torrentRequest TorrentRequest) {
if len(params) > 1 {
torrentRequest.Category = params[0]
} else {
torrentRequest.Category = "3_12"
}
if len(params) > 2 {
torrentRequest.Description = params[1]
} else {
torrentRequest.Description = "Description"
}
return
}

Voir le fichier

@ -1,9 +0,0 @@
package api
// CreateUserAuthentication creates user authentication.
func CreateUserAuthentication(form *formStruct.LoginForm) (models.User, int, error) {
username := form.Username
pass := form.Password
user, status, err := user.Exists(username, pass)
return user, status, err
}

Voir le fichier

@ -1,30 +0,0 @@
package apiService
import "errors"
// ErrShortName : Error for invalid file name used by api
var ErrShortName = errors.New("file name should be at least 100 characters long")
// ErrCategory : Error for not found category used by api
var ErrCategory = errors.New("this category doesn't exist")
// ErrSubCategory : Error for not found sub category used by api
var ErrSubCategory = errors.New("this sub category doesn't exist")
// ErrWebsiteLink : error indicating a torrent's website link is invalid
var ErrWebsiteLink = errors.New("website url or IRC link is invalid")
// ErrMagnet : Error for incorrect magnet used by api
var ErrMagnet = errors.New("incorrect magnet")
// ErrHash : Error for incorrect hash used by api
var ErrHash = errors.New("incorrect hash")
// ErrAPIKey : Error for incorrect api key used by api
var ErrAPIKey = errors.New("incorrect api key")
// ErrTorrentID : Error for torrent id used by api
var ErrTorrentID = errors.New("torrent with requested id doesn't exist")
// ErrRights : Error for rights used by api
var ErrRights = errors.New("not enough rights for this request")

Voir le fichier

@ -1686,5 +1686,57 @@
{
"id": "error_content_type_post",
"translation": "Please provide either of Content-Type: application/json header or multipart/form-data"
},
{
"id": "torrent_name_invalid",
"translation": "Torrent name is invalid"
},
{
"id": "torrent_private",
"translation": "Torrent is private"
},
{
"id": "torrent_no_working_trackers",
"translation": "Torrent does not have any (working) trackers: <a href=\"/faq#trackers\">Trackers List</a>"
},
{
"id": "torrent_desc_invalid",
"translation": "Torrent description is invalid"
},
{
"id": "torrent_cat_invalid",
"translation": "Torrent category is invalid"
},
{
"id": "torrent_lang_invalid",
"translation": "Language sent is not yet supported! You can help supporting it by contributing in our github page"
},
{
"id": "torrent_cat_is_english",
"translation": "Torrent's category is for English translations, but language wasn't English. We changed it to english"
},
{
"id": "torrent_cat_not_english",
"translation": "Torrent's category is for non-English translations, but language selected is only English"
},
{
"id": "torrent_magnet_invalid",
"translation": "Magnet couldn't be parsed, please check it"
},
{
"id": "torrent_hash_invalid",
"translation": "Torrent hash is incorrect"
},
{
"id": "torrent_plus_magnet",
"translation": "Upload either a torrent file or magnet link, not both"
},
{
"id": "torrent_file_invalid",
"translation": "Torrent File is invalid"
},
{
"id": "torrent_uri_invalid",
"translation": "Website url or IRC link is invalid"
}
]

Voir le fichier

@ -1,6 +1,15 @@
package cookies
import "github.com/gin-gonic/gin"
import (
"github.com/gin-gonic/gin"
"os/user"
"github.com/gorilla/securecookie"
"fmt"
"strconv"
"time"
"github.com/NyaaPantsu/nyaa/models"
"github.com/NyaaPantsu/nyaa/models/users"
)
const (
// CookieName : Name of cookie
@ -11,7 +20,9 @@ const (
)
// CreateUserAuthentication creates user authentication.
func CreateUserAuthentication(c *gin.Context, form *formStruct.LoginForm) (int, error) {
user, status, err := api.CreateUserAuthentication(form)
username := form.Username
pass := form.Password
user, status, err := users.Exists(username, pass)
if err != nil {
return status, err
}

Voir le fichier

@ -15,6 +15,9 @@ import (
"github.com/NyaaPantsu/nyaa/util/timeHelper"
"github.com/gin-gonic/gin"
"github.com/gorilla/securecookie"
"github.com/NyaaPantsu/nyaa/models/users"
"os/user"
"github.com/NyaaPantsu/nyaa/models"
)
var verificationHandler = securecookie.New(config.EmailTokenHashKey, nil)
@ -31,7 +34,7 @@ func SendEmailVerification(to string, token string) error {
}
// SendVerificationToUser sends an email verification token to user.
func SendVerificationToUser(user model.User, newEmail string) (int, error) {
func SendVerificationToUser(user models.User, newEmail string) (int, error) {
validUntil := timeHelper.TwentyFourHoursLater() // TODO: longer duration?
value := map[string]string{
"t": strconv.FormatInt(validUntil.Unix(), 10),

Voir le fichier

@ -10,25 +10,25 @@ import (
// FileListFolder struct
type FileListFolder struct {
Folders []*FileListFolder
Files []model.File
Files []models.File
FolderName string
}
// FileListToFolder convert a filelist to filelistfolder
func FileListToFolder(fileList []model.File, folderName string) (out *FileListFolder) {
func FileListToFolder(fileList []models.File, folderName string) (out *FileListFolder) {
out = &FileListFolder{
Folders: make([]*FileListFolder, 0),
Files: make([]model.File, 0),
Files: make([]models.File, 0),
FolderName: folderName,
}
pathsToFolders := make(map[string][]model.File)
pathsToFolders := make(map[string][]models.File)
for _, file := range fileList {
pathArray := file.Path()
if len(pathArray) > 1 {
pathStrippedFile := model.File{
pathStrippedFile := models.File{
ID: file.ID,
TorrentID: file.TorrentID,
BencodedPath: "",

Voir le fichier

@ -1,12 +1,12 @@
package util
package format
import (
"fmt"
"net/url"
)
// FormatFilesize : format file size
func FormatFilesize(bytes int64) string {
// FileSize : format file size
func FileSize(bytes int64) string {
var unit string
var value float64
if bytes >= 1024*1024*1024*1024 {

Voir le fichier

@ -1,4 +1,4 @@
package util
package format
import (
"path"
@ -17,16 +17,16 @@ var _ = func() (_ struct{}) {
return
}()
func TestFormatFilesize(t *testing.T) {
format := FormatFilesize(0)
func TestFileSize(t *testing.T) {
format := FileSize(0)
if format != "0.0 B" {
t.Fatalf("Format of 0 bytes gives %s, expected '0.0 B'", format)
}
format = FormatFilesize(int64(math.Exp2(0)))
format = FileSize(int64(math.Exp2(0)))
if format != "1.0 B" {
t.Fatalf("Format of 1byte gives %s, expected '1.0 B'", format)
}
format = FormatFilesize(int64(math.Exp2(10)))
format = FileSize(int64(math.Exp2(10)))
if format != "1.0 KiB" {
t.Fatalf("Format of 1024 bytes gives %s, expected '1.0 KiB'", format)
}
@ -55,32 +55,3 @@ func TestInfoHashToMagnet(t *testing.T) {
t.Fatalf("Magnet URL parsed doesn't give the expected result, have this '%s', want this '%s'", magnet, magnetExpected)
}
}
func TestSafe(t *testing.T) {
safeString := map[string]string{
"'": "&#39;",
"&": "&amp;",
"http://exemple.com": "http://exemple.com",
}
for key, val := range safeString {
safe := Safe(key)
if string(safe) != val {
t.Errorf("Safe doesn't escape the right values, expected result %s, got %s", key, val)
}
}
}
func TestSafeText(t *testing.T) {
safeString := map[string]string{
"'": "&#39;",
"&": "&amp;",
"http://exemple.com": "http://exemple.com",
"<em>test</em><script>lol();</script>": "&lt;em&gt;test&lt;/em&gt;&lt;script&gt;lol();&lt;/script&gt;",
}
for key, val := range safeString {
safe := Safe(key)
if string(safe) != val {
t.Errorf("Safe doesn't escape the right values, expected result %s, got %s", key, val)
}
}
}

Voir le fichier

@ -1,4 +1,4 @@
package util
package format
import (
"fmt"

Voir le fichier

@ -5,4 +5,4 @@ import (
)
// ErrInvalidTorrentFile : error for indicating we have an invalid torrent file
var ErrInvalidTorrentFile = errors.New("invalid bittorrent file")
var ErrInvalidTorrentFile = errors.New("torrent_file_invalid")

Voir le fichier

@ -1,4 +1,4 @@
package util
package sanitize
import (
"bytes"

Voir le fichier

@ -1,4 +1,4 @@
package util
package sanitize
import (
"html"

32
util/sanitize/safe_test.go Fichier normal
Voir le fichier

@ -0,0 +1,32 @@
package sanitize
import "testing"
func TestSafe(t *testing.T) {
safeString := map[string]string{
"'": "&#39;",
"&": "&amp;",
"http://exemple.com": "http://exemple.com",
}
for key, val := range safeString {
safe := Safe(key)
if string(safe) != val {
t.Errorf("Safe doesn't escape the right values, expected result %s, got %s", key, val)
}
}
}
func TestSafeText(t *testing.T) {
safeString := map[string]string{
"'": "&#39;",
"&": "&amp;",
"http://exemple.com": "http://exemple.com",
"<em>test</em><script>lol();</script>": "&lt;em&gt;test&lt;/em&gt;&lt;script&gt;lol();&lt;/script&gt;",
}
for key, val := range safeString {
safe := Safe(key)
if string(safe) != val {
t.Errorf("Safe doesn't escape the right values, expected result %s, got %s", key, val)
}
}
}

Voir le fichier

@ -44,31 +44,31 @@ func stringIsASCII(input string) bool {
}
// SearchByQuery : search torrents according to request without user
func SearchByQuery(c *gin.Context, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
func SearchByQuery(c *gin.Context, pagenum int) (search common.SearchParam, tor []models.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(c, pagenum, true, false, false, false)
return
}
// SearchByQueryWithUser : search torrents according to request with user
func SearchByQueryWithUser(c *gin.Context, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
func SearchByQueryWithUser(c *gin.Context, pagenum int) (search common.SearchParam, tor []models.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(c, pagenum, true, true, false, false)
return
}
// SearchByQueryNoCount : search torrents according to request without user and count
func SearchByQueryNoCount(c *gin.Context, pagenum int) (search common.SearchParam, tor []model.Torrent, err error) {
func SearchByQueryNoCount(c *gin.Context, pagenum int) (search common.SearchParam, tor []models.Torrent, err error) {
search, tor, _, err = searchByQuery(c, pagenum, false, false, false, false)
return
}
// SearchByQueryDeleted : search deleted torrents according to request with user and count
func SearchByQueryDeleted(c *gin.Context, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
func SearchByQueryDeleted(c *gin.Context, pagenum int) (search common.SearchParam, tor []models.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(c, pagenum, true, true, true, false)
return
}
// SearchByQueryNoHidden : search torrents and filter those hidden
func SearchByQueryNoHidden(c *gin.Context, pagenum int) (search common.SearchParam, tor []model.Torrent, count int, err error) {
func SearchByQueryNoHidden(c *gin.Context, pagenum int) (search common.SearchParam, tor []models.Torrent, count int, err error) {
search, tor, count, err = searchByQuery(c, pagenum, true, false, false, true)
return
}
@ -79,7 +79,7 @@ func SearchByQueryNoHidden(c *gin.Context, pagenum int) (search common.SearchPar
// elasticsearch always provide a count to how many hits
// deleted is unused because es doesn't index deleted torrents
func searchByQuery(c *gin.Context, pagenum int, countAll bool, withUser bool, deleted bool, hidden bool) (
search common.SearchParam, tor []model.Torrent, count int, err error,
search common.SearchParam, tor []models.Torrent, count int, err error,
) {
if db.ElasticSearchClient != nil {
var torrentParam common.TorrentParam
@ -115,7 +115,7 @@ func searchByQuery(c *gin.Context, pagenum int, countAll bool, withUser bool, de
}
func searchByQueryPostgres(c *gin.Context, pagenum int, countAll bool, withUser bool, deleted bool, hidden bool) (
search common.SearchParam, tor []model.Torrent, count int, err error,
search common.SearchParam, tor []models.Torrent, count int, err error,
) {
max, err := strconv.ParseUint(c.DefaultQuery("limit", "50"), 10, 32)
if err != nil {

Voir le fichier

@ -1,62 +1,216 @@
package uploadService
package upload
import (
"encoding/base32"
"encoding/hex"
"errors"
"fmt"
"io"
"io/ioutil"
"mime/multipart"
"net/url"
"os"
"reflect"
"regexp"
"strconv"
"strings"
"net/url"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/model"
"github.com/NyaaPantsu/nyaa/models"
"github.com/NyaaPantsu/nyaa/service"
"github.com/NyaaPantsu/nyaa/util"
"github.com/NyaaPantsu/nyaa/util/categories"
"github.com/NyaaPantsu/nyaa/util/metainfo"
"github.com/NyaaPantsu/nyaa/util/sanitize"
"github.com/NyaaPantsu/nyaa/util/torrentLanguages"
"github.com/NyaaPantsu/nyaa/util/upload"
"github.com/NyaaPantsu/nyaa/util/validator/torrent"
"github.com/gin-gonic/gin"
"github.com/zeebo/bencode"
)
// CheckTrackers : Check if there is good trackers in torrent
func CheckTrackers(trackers []string) []string {
// 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",
"://open.demonii.com:1337",
"://tracker.btcake.com",
"://tracker.prq.to",
"://bt.rghost.net"}
// form names
const uploadFormTorrent = "torrent"
var trackerRet []string
for _, t := range trackers {
urlTracker, err := url.Parse(t)
if err == nil {
good := true
for _, check := range deadTrackers {
if strings.Contains(t, check) {
good = false
break // No need to continue the for loop
}
}
if good {
trackerRet = append(trackerRet, urlTracker.String())
}
}
}
return trackerRet
type torrentsQuery struct {
Category int `json:"category"`
SubCategory int `json:"sub_category"`
Status int `json:"status"`
Uploader int `json:"uploader"`
Downloads int `json:"downloads"`
}
// IsUploadEnabled : Check if upload is enabled in config
func IsUploadEnabled(u *model.User) bool {
if config.Conf.Torrents.UploadsDisabled {
if config.Conf.Torrents.AdminsAreStillAllowedTo && u.IsModerator() {
return true
}
if config.Conf.Torrents.TrustedUsersAreStillAllowedTo && u.IsTrusted() {
return true
}
return false
}
return true
// TorrentsRequest struct
type TorrentsRequest struct {
Query torrentsQuery `json:"search"`
Page int `json:"page"`
MaxPerPage int `json:"limit"`
}
// APIResultJSON for torrents in json for api
type APIResultJSON struct {
Torrents []models.TorrentJSON `json:"torrents"`
QueryRecordCount int `json:"queryRecordCount"`
TotalRecordCount int `json:"totalRecordCount"`
}
// ToParams : Convert a torrentsrequest to searchparams
func (r *TorrentsRequest) ToParams() serviceBase.WhereParams {
res := serviceBase.WhereParams{}
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
}
// ExtractEditInfo : takes an http request and computes all fields for this form
func ExtractEditInfo(c *gin.Context, r *torrentValidator.TorrentRequest) error {
err := ExtractBasicValue(c, r)
if err != nil {
return err
}
err = r.ValidateName()
if err != nil {
return err
}
err = r.ExtractCategory()
if err != nil {
return err
}
err = r.ExtractLanguage()
return err
}
// ExtractBasicValue : takes an http request and computes all basic fields for this form
func ExtractBasicValue(c *gin.Context, r *torrentValidator.TorrentRequest) error {
c.Bind(r)
// trim whitespace
r.Name = strings.TrimSpace(r.Name)
r.Description = sanitize.Sanitize(strings.TrimSpace(r.Description), "default")
r.WebsiteLink = strings.TrimSpace(r.WebsiteLink)
r.Magnet = strings.TrimSpace(r.Magnet)
// then actually check that we have everything we need
err := r.ValidateDescription()
if err != nil {
return err
}
err = r.ValidateWebsiteLink()
return err
}
// ExtractInfo : takes an http request and computes all fields for this form
func ExtractInfo(c *gin.Context, r *torrentValidator.TorrentRequest) error {
err := ExtractBasicValue(c, r)
if err != nil {
return err
}
err = r.ExtractCategory()
if err != nil {
return err
}
err = r.ExtractLanguage()
if err != nil {
return err
}
tfile, err := r.ValidateMultipartUpload(c, uploadFormTorrent)
if err != nil {
return err
}
// We check name only here, reason: we can try to retrieve them from the torrent file
err = r.ValidateName()
if err != nil {
return err
}
// after data has been checked & extracted, write it to disk
if len(config.Conf.Torrents.FileStorage) > 0 {
err := writeTorrentToDisk(tfile, r.Infohash+".torrent", &r.Filepath)
if err != nil {
return err
}
} else {
r.Filepath = ""
}
return nil
}
// UpdateTorrent : Update torrent model
//rewrite with reflect ?
func UpdateTorrent(r *torrentValidator.UpdateRequest, t *models.Torrent, currentUser *models.User) {
if r.Update.Name != "" {
t.Name = r.Update.Name
}
if r.Update.Infohash != "" {
t.Hash = r.Update.Infohash
}
if r.Update.CategoryID != 0 {
t.Category = r.Update.CategoryID
}
if r.Update.SubCategoryID != 0 {
t.SubCategory = r.Update.SubCategoryID
}
if r.Update.Description != "" {
t.Description = r.Update.Description
}
if r.Update.WebsiteLink != "" {
t.WebsiteLink = r.Update.WebsiteLink
}
status := models.TorrentStatusNormal
if r.Update.Remake { // overrides trusted
status = models.TorrentStatusRemake
} else if currentUser.IsTrusted() {
status = models.TorrentStatusTrusted
}
t.Status = status
}
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.Conf.Torrents.FileStorage, os.PathSeparator, name)
return ioutil.WriteFile(*fullpath, b, 0644)
}
// NewTorrentRequest : creates a new torrent request struc with some default value
func NewTorrentRequest(params ...string) (torrentRequest torrentValidator.TorrentRequest) {
if len(params) > 1 {
torrentRequest.Category = params[0]
} else {
torrentRequest.Category = "3_12"
}
if len(params) > 2 {
torrentRequest.Description = params[1]
} else {
torrentRequest.Description = "Description"
}
return
}

Voir le fichier

@ -3,17 +3,17 @@ package torrentValidator
// TorrentRequest struct
// Same json name as the constant!
type TorrentRequest struct {
Name string `json:"name,omitempty"`
Magnet string `json:"magnet,omitempty"`
Category string `json:"c"`
Remake bool `json:"remake,omitempty"`
Description string `json:"desc,omitempty"`
Status int `json:"status,omitempty"`
Hidden bool `json:"hidden,omitempty"`
CaptchaID string `json:"-"`
WebsiteLink string `json:"website_link,omitempty"`
SubCategory int `json:"sub_category,omitempty"`
Language string `json:"language,omitempty"`
Name string `validate:"required" json:"name,omitempty"`
Magnet string `json:"magnet,omitempty"`
Category string `validate:"required" json:"c"`
Remake bool `json:"remake,omitempty"`
Description string `json:"desc,omitempty"`
Status int `json:"status,omitempty"`
Hidden bool `json:"hidden,omitempty"`
CaptchaID string `json:"-"`
WebsiteLink string `validate:"uri" json:"website_link,omitempty"`
SubCategory int `json:"sub_category,omitempty"`
Languages []string `json:"languages,omitempty"`
Infohash string `json:"hash,omitempty"`
CategoryID int `json:"-"`

Voir le fichier

@ -0,0 +1,243 @@
package torrentValidator
import (
"encoding/base32"
"encoding/hex"
"errors"
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/util/categories"
"github.com/NyaaPantsu/nyaa/util/format"
"github.com/NyaaPantsu/nyaa/util/metainfo"
"github.com/NyaaPantsu/nyaa/util/torrentLanguages"
"github.com/gin-gonic/gin"
"github.com/zeebo/bencode"
"io"
"mime/multipart"
"net/url"
"regexp"
"strconv"
"strings"
)
func (r *TorrentRequest) ValidateName() error {
// then actually check that we have everything we need
if len(r.Name) == 0 {
return errors.New("torrent_name_invalid")
}
return nil
}
func (r *TorrentRequest) ValidateDescription() error {
if len(r.Description) > config.Conf.DescriptionLength {
return errors.New("torrent_desc_invalid")
}
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:") {
return errors.New("torrent_magnet_invalid")
}
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
urlRegexp, _ := regexp.Compile(`^(https?:\/\/|ircs?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$`)
if !urlRegexp.MatchString(r.WebsiteLink) {
return errors.New("torrent_uri_invalid")
}
}
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 {
return errors.New("torrent_hash_invalid")
}
} 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 errors.New("torrent_cat_invalid")
}
CatID, err := strconv.Atoi(catsSplit[0])
if err != nil {
return errors.New("torrent_cat_invalid")
}
SubCatID, err := strconv.Atoi(catsSplit[1])
if err != nil {
return errors.New("torrent_cat_invalid")
}
if !categories.CategoryExists(r.Category) {
return errors.New("torrent_cat_invalid")
}
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.Conf.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-us")
return nil
}
englishSelected := false
for _, language := range r.Languages {
if language == "en-us" {
englishSelected = true
}
if language != "" && !torrentLanguages.LanguageExists(language) {
return errors.New("torrent_lang_invalid")
}
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-us")
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.Conf.Torrents.NonEnglishOnlyCategories {
if cat == r.Category {
r.Category = config.Conf.Torrents.EnglishOnlyCategories[key]
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() {
return tfile, errors.New("torrent_private")
}
trackers := torrent.GetAllAnnounceURLS()
r.Trackers = CheckTrackers(trackers)
if len(r.Trackers) == 0 {
return tfile, errors.New("torrent_no_working_trackers")
}
// 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 {
return tfile, errors.New("torrent_plus_magnet")
}
_, 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
}

Voir le fichier

@ -0,0 +1,60 @@
package torrentValidator
import (
"github.com/NyaaPantsu/nyaa/config"
"github.com/NyaaPantsu/nyaa/models"
"net/url"
"strings"
)
// CheckTrackers : Check if there is good trackers in torrent
func CheckTrackers(trackers []string) []string {
// 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",
"://open.demonii.com:1337",
"://tracker.btcake.com",
"://tracker.prq.to",
"://bt.rghost.net"}
var trackerRet []string
for _, t := range trackers {
urlTracker, err := url.Parse(t)
if err == nil {
good := true
for _, check := range deadTrackers {
if strings.Contains(t, check) {
good = false
break // No need to continue the for loop
}
}
if good {
trackerRet = append(trackerRet, urlTracker.String())
}
}
}
return trackerRet
}
// IsUploadEnabled : Check if upload is enabled in config
func IsUploadEnabled(u *models.User) bool {
if config.Conf.Torrents.UploadsDisabled {
if config.Conf.Torrents.AdminsAreStillAllowedTo && u.IsModerator() {
return true
}
if config.Conf.Torrents.TrustedUsersAreStillAllowedTo && u.IsTrusted() {
return true
}
return false
}
return true
}

Voir le fichier

@ -30,16 +30,16 @@ type UserForm struct {
// UserSettingsForm is used when updating a user.
type UserSettingsForm struct {
NewTorrent bool `validate:"default=true" json:"new_torrent"`
NewTorrentEmail bool `validate:"default=true" json:"new_torrent_email"`
NewComment bool `validate:"default=true" json:"new_comment"`
NewCommentEmail bool `validate:"default=false" json:"new_comment_email"`
NewResponses bool `validate:"default=true" json:"new_responses"`
NewResponsesEmail bool `validate:"default=false" json:"new_responses_email"`
NewFollower bool `validate:"default=true" json:"new_follower"`
NewFollowerEmail bool `validate:"default=true" json:"new_follower_email"`
Followed bool `validate:"default=false" json:"followed"`
FollowedEmail bool `validate:"default=false" json:"followed_email"`
NewTorrent bool `validate:json:"new_torrent"`
NewTorrentEmail bool `validate:json:"new_torrent_email"`
NewComment bool `validate:json:"new_comment"`
NewCommentEmail bool `validate:json:"new_comment_email"`
NewResponses bool `validate:json:"new_responses"`
NewResponsesEmail bool `validate:json:"new_responses_email"`
NewFollower bool `validate:json:"new_follower"`
NewFollowerEmail bool `validate:json:"new_follower_email"`
Followed bool `validate:json:"followed"`
FollowedEmail bool `validate:json:"followed_email"`
}
// PasswordForm is used when updating a user password.