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.