diff --git a/common/torrent.go b/common/torrent.go
index 8302cc67..1f3970a3 100644
--- a/common/torrent.go
+++ b/common/torrent.go
@@ -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)
diff --git a/controllers/api_handler.go b/controllers/api_handler.go
index daad3ae0..21045b13 100644
--- a/controllers/api_handler.go
+++ b/controllers/api_handler.go
@@ -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)
}
diff --git a/controllers/database_dump_handler.go b/controllers/database_dump_handler.go
index 939f118b..f843ed97 100644
--- a/controllers/database_dump_handler.go
+++ b/controllers/database_dump_handler.go
@@ -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(),
diff --git a/controllers/helpers.go b/controllers/helpers.go
index daee9abc..e767edd9 100644
--- a/controllers/helpers.go
+++ b/controllers/helpers.go
@@ -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
}
diff --git a/controllers/modpanel.go b/controllers/modpanel.go
index edf39dfe..1e0b6567 100644
--- a/controllers/modpanel.go
+++ b/controllers/modpanel.go
@@ -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)
diff --git a/controllers/rss_handler.go b/controllers/rss_handler.go
index c22b37cb..e1495da4 100644
--- a/controllers/rss_handler.go
+++ b/controllers/rss_handler.go
@@ -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")
diff --git a/controllers/search_handler.go b/controllers/search_handler.go
index b2f1c8e0..fd50a941 100644
--- a/controllers/search_handler.go
+++ b/controllers/search_handler.go
@@ -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)
}
diff --git a/controllers/template.go b/controllers/template.go
index cf5bf678..7e217039 100644
--- a/controllers/template.go
+++ b/controllers/template.go
@@ -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)
diff --git a/controllers/template_functions.go b/controllers/template_functions.go
index b414db1a..37fc3d6b 100644
--- a/controllers/template_functions.go
+++ b/controllers/template_functions.go
@@ -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("" + username + "")
})
- 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
diff --git a/controllers/template_test.go b/controllers/template_test.go
index d34f5572..65c45707 100644
--- a/controllers/template_test.go
+++ b/controllers/template_test.go
@@ -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
}
diff --git a/controllers/upload_handler.go b/controllers/upload_handler.go
index f793ef23..8156f5a5 100644
--- a/controllers/upload_handler.go
+++ b/controllers/upload_handler.go
@@ -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())
diff --git a/controllers/user_handler.go b/controllers/user_handler.go
index 9ea02c7b..85e3a572 100644
--- a/controllers/user_handler.go
+++ b/controllers/user_handler.go
@@ -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 {
diff --git a/controllers/view_torrent_handler.go b/controllers/view_torrent_handler.go
index 698c5030..70b3c28c 100644
--- a/controllers/view_torrent_handler.go
+++ b/controllers/view_torrent_handler.go
@@ -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)
diff --git a/models/report.go b/models/report.go
index d4a4d683..19d4e0f7 100644
--- a/models/report.go
+++ b/models/report.go
@@ -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"`
diff --git a/models/torrent.go b/models/torrent.go
index b02d8785..e6135e3a 100644
--- a/models/torrent.go
+++ b/models/torrent.go
@@ -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 == "" {
diff --git a/models/users/delete.go b/models/users/delete.go
index edcb43c1..63cb5286 100644
--- a/models/users/delete.go
+++ b/models/users/delete.go
@@ -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")
diff --git a/models/users/update.go b/models/users/update.go
index c8ab02fb..a15fbe9a 100644
--- a/models/users/update.go
+++ b/models/users/update.go
@@ -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 {
diff --git a/service/api/api.go b/service/api/api.go
deleted file mode 100644
index 10494d40..00000000
--- a/service/api/api.go
+++ /dev/null
@@ -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
-}
diff --git a/service/api/auth.go b/service/api/auth.go
deleted file mode 100644
index 2d71039a..00000000
--- a/service/api/auth.go
+++ /dev/null
@@ -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
-}
\ No newline at end of file
diff --git a/service/api/errors.go b/service/api/errors.go
deleted file mode 100644
index 644a701b..00000000
--- a/service/api/errors.go
+++ /dev/null
@@ -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")
diff --git a/translations/en-us.all.json b/translations/en-us.all.json
index 33c0aa97..da52dbc9 100644
--- a/translations/en-us.all.json
+++ b/translations/en-us.all.json
@@ -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: Trackers List"
+ },
+ {
+ "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"
}
]
diff --git a/util/cookies/user.go b/util/cookies/user.go
index 638b5a03..75abf3d9 100644
--- a/util/cookies/user.go
+++ b/util/cookies/user.go
@@ -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
}
diff --git a/util/email/verification.go b/util/email/verification.go
index 93fe3682..40b18530 100644
--- a/util/email/verification.go
+++ b/util/email/verification.go
@@ -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),
diff --git a/util/filelist/filelist.go b/util/filelist/filelist.go
index c4f2ea98..56dd8498 100644
--- a/util/filelist/filelist.go
+++ b/util/filelist/filelist.go
@@ -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: "",
diff --git a/util/format.go b/util/format/format.go
similarity index 88%
rename from util/format.go
rename to util/format/format.go
index c33b6db2..e0c720e1 100644
--- a/util/format.go
+++ b/util/format/format.go
@@ -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 {
diff --git a/util/util_test.go b/util/format/format_test.go
similarity index 59%
rename from util/util_test.go
rename to util/format/format_test.go
index e3f978e5..4d3bab5c 100644
--- a/util/util_test.go
+++ b/util/format/format_test.go
@@ -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{
- "'": "'",
- "&": "&",
- "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{
- "'": "'",
- "&": "&",
- "http://exemple.com": "http://exemple.com",
- "test": "<em>test</em><script>lol();</script>",
- }
- 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)
- }
- }
-}
diff --git a/util/magnet.go b/util/format/magnet.go
similarity index 96%
rename from util/magnet.go
rename to util/format/magnet.go
index 42e1aa3c..a7102077 100644
--- a/util/magnet.go
+++ b/util/format/magnet.go
@@ -1,4 +1,4 @@
-package util
+package format
import (
"fmt"
diff --git a/util/metainfo/errors.go b/util/metainfo/errors.go
index 6ca3b141..71d75c13 100644
--- a/util/metainfo/errors.go
+++ b/util/metainfo/errors.go
@@ -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")
diff --git a/util/markdown.go b/util/sanitize/markdown.go
similarity index 99%
rename from util/markdown.go
rename to util/sanitize/markdown.go
index dbe882e7..92d79409 100644
--- a/util/markdown.go
+++ b/util/sanitize/markdown.go
@@ -1,4 +1,4 @@
-package util
+package sanitize
import (
"bytes"
diff --git a/util/safe.go b/util/sanitize/safe.go
similarity index 93%
rename from util/safe.go
rename to util/sanitize/safe.go
index c7284879..24685457 100644
--- a/util/safe.go
+++ b/util/sanitize/safe.go
@@ -1,4 +1,4 @@
-package util
+package sanitize
import (
"html"
diff --git a/util/sanitize/safe_test.go b/util/sanitize/safe_test.go
new file mode 100644
index 00000000..a5c6d07c
--- /dev/null
+++ b/util/sanitize/safe_test.go
@@ -0,0 +1,32 @@
+package sanitize
+
+import "testing"
+
+func TestSafe(t *testing.T) {
+ safeString := map[string]string{
+ "'": "'",
+ "&": "&",
+ "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{
+ "'": "'",
+ "&": "&",
+ "http://exemple.com": "http://exemple.com",
+ "test": "<em>test</em><script>lol();</script>",
+ }
+ 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)
+ }
+ }
+}
diff --git a/util/search/search.go b/util/search/search.go
index 4734f0a9..066ed73f 100644
--- a/util/search/search.go
+++ b/util/search/search.go
@@ -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 {
diff --git a/util/upload/upload.go b/util/upload/upload.go
index ec289585..faed7b1a 100644
--- a/util/upload/upload.go
+++ b/util/upload/upload.go
@@ -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
}
diff --git a/util/validator/torrent/forms.go b/util/validator/torrent/forms.go
index 8121af19..6077cb25 100644
--- a/util/validator/torrent/forms.go
+++ b/util/validator/torrent/forms.go
@@ -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:"-"`
diff --git a/util/validator/torrent/functions.go b/util/validator/torrent/functions.go
new file mode 100644
index 00000000..2bb7aba1
--- /dev/null
+++ b/util/validator/torrent/functions.go
@@ -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
+}
diff --git a/util/validator/torrent/helpers.go b/util/validator/torrent/helpers.go
new file mode 100644
index 00000000..ab6a4ffb
--- /dev/null
+++ b/util/validator/torrent/helpers.go
@@ -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
+}
diff --git a/util/validator/user/forms.go b/util/validator/user/forms.go
index 28a77e0a..1feb258a 100644
--- a/util/validator/user/forms.go
+++ b/util/validator/user/forms.go
@@ -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.