From 14378fbf390d8f1930fa81ae0bbecb546e0b0f5c Mon Sep 17 00:00:00 2001 From: Chris MacLeod Date: Tue, 9 May 2017 22:34:40 -0400 Subject: [PATCH] Consistency, formatting, error checking, cleanup, and a couple bug fixes (#245) * Checkpoint: it builds The config, db, model, network, os, and public packages have had some fixes to glaringly obvious flaws, dead code removed, and stylistic changes. * Style changes and old code removal in router Router needs a lot of work done to its (lack of) error handling. * Dead code removal and style changes Now up to util/email/email.go. After I'm finished with the initial sweep I'll go back and fix error handling and security issues. Then I'll fix the broken API. Then I'll go through to add documentation and fix code visibility. * Finish dead code removal and style changes Vendored libraries not touched. Everything still needs security fixes and documentation. There's also one case of broken functionality. * Fix accidental find-and-replace * Style, error checking, saftey, bug fix changes * Redo error checking erased during merge * Re-add merge-erased fix. Make Safe safe. --- config/config.go | 47 +++++---- config/email.go | 6 +- config/env.go | 11 ++- config/i2p.go | 7 +- config/logger.go | 1 - config/navigation.go | 6 +- config/sorting.go | 6 +- config/trackers.go | 3 +- config/upload.go | 4 +- db/gorm.go | 31 +++--- main.go | 21 ++-- model/comment.go | 16 +-- model/language.go | 3 +- model/torrent.go | 112 ++++++++++----------- model/user.go | 16 +-- router/apiHandler.go | 71 +++++++------ router/homeHandler.go | 31 +++--- router/router.go | 4 +- router/rssHandler.go | 35 +++---- router/searchHandler.go | 27 ++--- router/templateFunctions.go | 22 ++--- router/templateVariables.go | 22 ++--- router/upload.go | 70 +++++++------ router/uploadHandler.go | 67 +++++++------ router/userHandler.go | 83 ++++++++-------- router/viewTorrentHandler.go | 16 +-- service/api/api.go | 6 +- service/captcha/captcha.go | 8 +- service/torrent/torrent.go | 49 +++++---- service/user/cookieHelper.go | 24 ++--- service/user/form/formValidator.go | 43 ++++---- service/user/permission/permission.go | 31 +++--- service/user/user.go | 110 +++++++++++---------- service/user/userHelper.go | 16 +-- service/user/verification.go | 20 ++-- templates/_badgemenu.html | 8 +- templates/_profile_edit.html | 4 +- templates/_user_list_torrents.html | 8 +- templates/home.html | 6 +- templates/user/profile.html | 16 +-- templates/user/profile_edit.html | 14 +-- templates/view.html | 73 +++++++------- util/crypto/crypto.go | 12 ++- util/email/email.go | 7 +- util/format.go | 43 ++++---- util/languages/translation.go | 2 +- util/log/error.go | 6 +- util/log/logger.go | 9 +- util/markdown.go | 2 +- util/metainfo/metainfo.go | 9 +- util/modelHelper/modelHelper.go | 137 +++++++++++++------------- util/safe.go | 2 +- util/search/search.go | 44 ++++----- util/signals/win32.go | 1 + util/timeHelper/timeHelper.go | 6 -- util/unzlib.go | 16 +-- 56 files changed, 750 insertions(+), 720 deletions(-) diff --git a/config/config.go b/config/config.go index 6627fe5e..9f14548d 100644 --- a/config/config.go +++ b/config/config.go @@ -3,7 +3,6 @@ package config import ( "bufio" "encoding/json" - "errors" "flag" "fmt" "io" @@ -11,19 +10,20 @@ import ( ) const ( - // Highest torrent ID that was copied from nyaa - LastOldTorrentId = 923000 + // LastOldTorrentID is the highest torrent ID + // that was copied from the original Nyaa + LastOldTorrentID = 923000 ) type Config struct { - Host string `json: "host"` - Port int `json: "port"` - DBType string `json: "db_type"` - // This will be directly passed to Gorm, and its internal + Host string `json:"host"` + Port int `json:"port"` + DBType string `json:"db_type"` + // DBParams will be directly passed to Gorm, and its internal // structure depends on the dialect for each db type - DBParams string `json: "db_params"` + DBParams string `json:"db_params"` // optional i2p configuration - I2P *I2PConfig `json: "i2p"` + I2P *I2PConfig `json:"i2p"` } var Defaults = Config{"localhost", 9999, "sqlite3", "./nyaa.db?cache_size=50", nil} @@ -35,7 +35,7 @@ var allowedDatabaseTypes = map[string]bool{ "mssql": true, } -func NewConfig() *Config { +func New() *Config { var config Config config.Host = Defaults.Host config.Port = Defaults.Port @@ -44,28 +44,25 @@ func NewConfig() *Config { return &config } -type processFlags func() error - -func (config *Config) BindFlags() processFlags { - // This function returns a function which is to be used after - // flag.Parse to check and copy the flags' values to the Config instance. - - conf_file := flag.String("conf", "", "path to the configuration file") - db_type := flag.String("dbtype", Defaults.DBType, "database backend") +// BindFlags returns a function which is to be used after +// flag.Parse to check and copy the flags' values to the Config instance. +func (config *Config) BindFlags() func() error { + confFile := flag.String("conf", "", "path to the configuration file") + dbType := flag.String("dbtype", Defaults.DBType, "database backend") host := flag.String("host", Defaults.Host, "binding address of the server") port := flag.Int("port", Defaults.Port, "port of the server") - db_params := flag.String("dbparams", Defaults.DBParams, "parameters to open the database (see Gorm's doc)") + dbParams := flag.String("dbparams", Defaults.DBParams, "parameters to open the database (see Gorm's doc)") return func() error { // You can override fields in the config file with flags. config.Host = *host config.Port = *port - config.DBParams = *db_params - err := config.SetDBType(*db_type) + config.DBParams = *dbParams + err := config.SetDBType(*dbType) if err != nil { return err } - err = config.HandleConfFileFlag(*conf_file) + err = config.HandleConfFileFlag(*confFile) return err } } @@ -74,12 +71,12 @@ func (config *Config) HandleConfFileFlag(path string) error { if path != "" { file, err := os.Open(path) if err != nil { - return errors.New(fmt.Sprintf("Can't read file '%s'.", path)) + return fmt.Errorf("can't read file '%s'", path) } err = config.Read(bufio.NewReader(file)) if err != nil { - return errors.New(fmt.Sprintf("Failed to parse file '%s' (%s).", path, err)) + return fmt.Errorf("failed to parse file '%s' (%s)", path, err) } } return nil @@ -87,7 +84,7 @@ func (config *Config) HandleConfFileFlag(path string) error { func (config *Config) SetDBType(db_type string) error { if !allowedDatabaseTypes[db_type] { - return errors.New(fmt.Sprintf("Unknown database backend '%s'.", db_type)) + return fmt.Errorf("unknown database backend '%s'", db_type) } config.DBType = db_type return nil diff --git a/config/email.go b/config/email.go index 0940ba5c..4bdc3566 100644 --- a/config/email.go +++ b/config/email.go @@ -2,6 +2,9 @@ package config import "time" +// TODO: Perform email configuration at runtime +// Future hosts shouldn't have to rebuild the binary to update a setting + const ( SendEmail = true EmailFrom = "donotrespond@nyaa.pantsu.cat" @@ -10,8 +13,7 @@ const ( EmailUsername = "" EmailPassword = "" EmailPort = 465 - // EmailTimeout = 80 * time.Millisecond - EmailTimeout = 10 * time.Second + EmailTimeout = 10 * time.Second ) var EmailTokenHashKey = []byte("CHANGE_THIS_BEFORE_DEPLOYING_YOU_RETARD") diff --git a/config/env.go b/config/env.go index 92739674..1f2d26cc 100644 --- a/config/env.go +++ b/config/env.go @@ -1,10 +1,11 @@ package config -// Constants for environment. +// TODO: Perform environment configuration at runtime +// Future hosts shouldn't have to rebuild the binary to update a setting + const ( - // DEVELOPMENT | TEST | PRODUCTION - Environment = "DEVELOPMENT" - // Environment = "PRODUCTION" - WebAddress = "nyaa.pantsu.cat" + // Environment should be one of: DEVELOPMENT, TEST, PRODUCTION + Environment = "DEVELOPMENT" + WebAddress = "nyaa.pantsu.cat" AuthTokenExpirationDay = 1000 ) diff --git a/config/i2p.go b/config/i2p.go index 60f0f966..313de7e1 100644 --- a/config/i2p.go +++ b/config/i2p.go @@ -1,8 +1,7 @@ package config -// i2p connectivity config type I2PConfig struct { - Name string `json: "name"` - Addr string `json: "samaddr"` - Keyfile string `json: "keyfile"` + Name string `json:"name"` + Addr string `json:"samaddr"` + Keyfile string `json:"keyfile"` } diff --git a/config/logger.go b/config/logger.go index dcdccdbd..971ec605 100644 --- a/config/logger.go +++ b/config/logger.go @@ -1,6 +1,5 @@ package config -// Constants for logger. const ( AccessLogFilePath = "log/access" AccessLogFileExtension = ".txt" diff --git a/config/navigation.go b/config/navigation.go index ade0972d..91374b94 100644 --- a/config/navigation.go +++ b/config/navigation.go @@ -1,6 +1,8 @@ package config -// Constants for pagination. +// TODO: Perform navigation configuration at runtime +// Future hosts shouldn't have to rebuild the binary to update a setting + const ( - TorrentsPerPage = 50 + TorrentsPerPage = 50 ) diff --git a/config/sorting.go b/config/sorting.go index 41df588a..85a136fa 100644 --- a/config/sorting.go +++ b/config/sorting.go @@ -1,7 +1,9 @@ package config -// Constants for ordering models. +// TODO: Perform sorting configuration at runtime +// Future hosts shouldn't have to rebuild the binary to update a setting + const ( - TorrentOrder = "torrent_id" + TorrentOrder = "torrent_id" TorrentSort = "DESC" ) diff --git a/config/trackers.go b/config/trackers.go index 1723283d..b3e0f5b9 100644 --- a/config/trackers.go +++ b/config/trackers.go @@ -1,6 +1,7 @@ package config -// remember to update the FAQ when updating these +// TODO: Update FAQ template to use this variable + var Trackers = []string{ "udp://tracker.coppersurfer.tk:6969", "udp://zer0day.to:1337/announce", diff --git a/config/upload.go b/config/upload.go index d38a3526..2099c0d4 100644 --- a/config/upload.go +++ b/config/upload.go @@ -8,7 +8,5 @@ const ( // TODO: deprecate this and move all files to the same server TorrentCacheLink = "http://anicache.com/torrent/%s.torrent" - - //disable uploads by default - UploadsDisabled = 1 + UploadsDisabled = true ) diff --git a/db/gorm.go b/db/gorm.go index 03c68936..01d10832 100644 --- a/db/gorm.go +++ b/db/gorm.go @@ -5,36 +5,33 @@ import ( "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/util/log" "github.com/jinzhu/gorm" - _ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/postgres" - // _ "github.com/go-sql-driver/mysql" + _ "github.com/jinzhu/gorm/dialects/sqlite" ) var ORM *gorm.DB // GormInit init gorm ORM. func GormInit(conf *config.Config) (*gorm.DB, error) { - db, err := gorm.Open(conf.DBType, conf.DBParams) - // db, err := gorm.Open("mysql", config.MysqlDSL()) - //db, err := gorm.Open("sqlite3", "/tmp/gorm.db") + db, openErr := gorm.Open(conf.DBType, conf.DBParams) + if openErr != nil { + log.CheckError(openErr) + return nil, openErr + } - // Get database connection handle [*sql.DB](http://golang.org/pkg/database/sql/#DB) - db.DB() - - // Then you could invoke `*sql.DB`'s functions with it - db.DB().Ping() + connectionErr := db.DB().Ping() + if connectionErr != nil { + log.CheckError(connectionErr) + return nil, connectionErr + } db.DB().SetMaxIdleConns(10) db.DB().SetMaxOpenConns(100) - // Disable table name's pluralization - // db.SingularTable(true) + // TODO: Enable Gorm initialization for non-development builds if config.Environment == "DEVELOPMENT" { db.LogMode(true) - db.AutoMigrate(&model.Torrents{}, &model.UserFollows{}, &model.User{}, &model.Comment{}, &model.OldComment{}) - // db.Model(&model.User{}).AddIndex("idx_user_token", "token") - + db.AutoMigrate(&model.Torrent{}, &model.UserFollows{}, &model.User{}, &model.Comment{}, &model.OldComment{}) } - log.CheckError(err) - return db, err + return db, nil } diff --git a/main.go b/main.go index 7ed73008..dbec6dae 100644 --- a/main.go +++ b/main.go @@ -48,17 +48,23 @@ func RunServer(conf *config.Config) { } func main() { - conf := config.NewConfig() - process_flags := conf.BindFlags() + conf := config.New() + processFlags := conf.BindFlags() defaults := flag.Bool("print-defaults", false, "print the default configuration file on stdout") flag.Parse() if *defaults { stdout := bufio.NewWriter(os.Stdout) - conf.Pretty(stdout) - stdout.Flush() + err := conf.Pretty(stdout) + if err != nil { + log.Fatal(err.Error()) + } + err = stdout.Flush() + if err != nil { + log.Fatal(err.Error()) + } os.Exit(0) } else { - err := process_flags() + err := processFlags() if err != nil { log.CheckError(err) } @@ -69,7 +75,10 @@ func main() { initI18N() go signals.Handle() if len(config.TorrentFileStorage) > 0 { - os.MkdirAll(config.TorrentFileStorage, 0755) + err := os.MkdirAll(config.TorrentFileStorage, 0700) + if err != nil { + log.Fatal(err.Error()) + } } RunServer(conf) } diff --git a/model/comment.go b/model/comment.go index 1d64a90d..26659329 100644 --- a/model/comment.go +++ b/model/comment.go @@ -5,27 +5,29 @@ import ( ) type Comment struct { - Id uint `gorm:"column:comment_id;primary_key"` - TorrentId uint `gorm:"column:torrent_id"` - UserId uint `gorm:"column:user_id"` + ID uint `gorm:"column:comment_id;primary_key"` + TorrentID uint `gorm:"column:torrent_id"` + UserID uint `gorm:"column:user_id"` Content string `gorm:"column:content"` CreatedAt time.Time `gorm:"column:created_at"` UpdatedAt time.Time `gorm:"column:updated_at"` - Torrent *Torrents `gorm:"ForeignKey:torrent_id"` - User *User `gorm:"ForeignKey:user_id"` + Torrent *Torrent `gorm:"ForeignKey:torrent_id"` + User *User `gorm:"ForeignKey:user_id"` } type OldComment struct { - TorrentId uint `gorm:"column:torrent_id"` + TorrentID uint `gorm:"column:torrent_id"` Username string `gorm:"column:username"` Content string `gorm:"column:content"` Date time.Time `gorm:"column:date"` - Torrent *Torrents `gorm:"ForeignKey:torrent_id"` + Torrent *Torrent `gorm:"ForeignKey:torrent_id"` } func (c OldComment) TableName() string { // cba to rename this in the db + // TODO: Update database schema to fix this hack + // I find this odd considering how often the schema changes already return "comments_old" } diff --git a/model/language.go b/model/language.go index 0e5eb2f4..99c5fcae 100644 --- a/model/language.go +++ b/model/language.go @@ -1,7 +1,6 @@ package model -// Language is a language model. type Language struct { - Id uint `json:"id"` + ID uint `json:"id"` Name string `json:"name"` } diff --git a/model/torrent.go b/model/torrent.go index 58920334..bda36357 100644 --- a/model/torrent.go +++ b/model/torrent.go @@ -5,6 +5,7 @@ import ( "github.com/ewhal/nyaa/util" "fmt" + "html" "html/template" "strconv" "strings" @@ -12,100 +13,101 @@ import ( ) type Feed struct { - Id int + ID int Name string Hash string Magnet string Timestamp string } -type Torrents struct { - Id uint `gorm:"column:torrent_id;primary_key"` - Name string `gorm:"column:torrent_name"` - Hash string `gorm:"column:torrent_hash"` - Category int `gorm:"column:category"` - Sub_Category int `gorm:"column:sub_category"` - Status int `gorm:"column:status"` - Date time.Time `gorm:"column:date"` - UploaderId uint `gorm:"column:uploader"` - Downloads int `gorm:"column:downloads"` - Stardom int `gorm:"column:stardom"` - Filesize int64 `gorm:"column:filesize"` - Description string `gorm:"column:description"` - WebsiteLink string `gorm:"column:website_link"` +type Torrent struct { + ID uint `gorm:"column:torrent_id;primary_key"` + Name string `gorm:"column:torrent_name"` + Hash string `gorm:"column:torrent_hash"` + Category int `gorm:"column:category"` + SubCategory int `gorm:"column:sub_category"` + Status int `gorm:"column:status"` + Date time.Time `gorm:"column:date"` + UploaderID uint `gorm:"column:uploader"` + Downloads int `gorm:"column:downloads"` + Stardom int `gorm:"column:stardom"` + Filesize int64 `gorm:"column:filesize"` + Description string `gorm:"column:description"` + WebsiteLink string `gorm:"column:website_link"` Uploader *User `gorm:"ForeignKey:UploaderId"` OldComments []OldComment `gorm:"ForeignKey:torrent_id"` Comments []Comment `gorm:"ForeignKey:torrent_id"` } -/* We need JSON Object instead because of Magnet URL that is not in the database but generated dynamically */ +/* We need a JSON object instead of a Gorm structure because magnet URLs are + not in the database and have to be generated dynamically */ -type ApiResultJson struct { - Torrents []TorrentsJson `json:"torrents"` - QueryRecordCount int `json:"queryRecordCount"` - TotalRecordCount int `json:"totalRecordCount"` +type ApiResultJSON struct { + Torrents []TorrentJSON `json:"torrents"` + QueryRecordCount int `json:"queryRecordCount"` + TotalRecordCount int `json:"totalRecordCount"` } -type CommentsJson struct { +type CommentJSON struct { Username string `json:"username"` Content template.HTML `json:"content"` Date time.Time `json:"date"` } -type TorrentsJson struct { - Id string `json:"id"` - Name string `json:"name"` - Status int `json:"status"` - Hash string `json:"hash"` - Date string `json:"date"` - Filesize string `json:"filesize"` - Description template.HTML `json:"description"` - Comments []CommentsJson `json:"comments"` - Sub_Category string `json:"sub_category"` - Category string `json:"category"` - Downloads int `json:"downloads"` - UploaderId uint `json:"uploader_id"` - UploaderName template.HTML `json:"uploader_name"` - WebsiteLink template.URL `json:"website_link"` - Magnet template.URL `json:"magnet"` - TorrentLink template.URL `json:"torrent"` +type TorrentJSON struct { + ID string `json:"id"` + Name string `json:"name"` + Status int `json:"status"` + Hash string `json:"hash"` + Date string `json:"date"` + Filesize string `json:"filesize"` + Description template.HTML `json:"description"` + Comments []CommentJSON `json:"comments"` + SubCategory string `json:"sub_category"` + Category string `json:"category"` + Downloads int `json:"downloads"` + UploaderID uint `json:"uploader_id"` + UploaderName template.HTML `json:"uploader_name"` + WebsiteLink template.URL `json:"website_link"` + Magnet template.URL `json:"magnet"` + TorrentLink template.URL `json:"torrent"` } -/* Model Conversion to Json */ - -func (t *Torrents) ToJson() TorrentsJson { +// ToJSON converts a model.Torrent to its equivalent JSON structure +func (t *Torrent) ToJSON() TorrentJSON { magnet := util.InfoHashToMagnet(strings.TrimSpace(t.Hash), t.Name, config.Trackers...) - commentsJson := make([]CommentsJson, 0, len(t.OldComments)+len(t.Comments)) + commentsJSON := make([]CommentJSON, 0, len(t.OldComments)+len(t.Comments)) for _, c := range t.OldComments { - commentsJson = append(commentsJson, CommentsJson{Username: c.Username, Content: template.HTML(c.Content), Date: c.Date}) + escapedContent := template.HTML(html.EscapeString(c.Content)) + commentsJSON = append(commentsJSON, CommentJSON{Username: c.Username, Content: escapedContent, Date: c.Date}) } for _, c := range t.Comments { - commentsJson = append(commentsJson, CommentsJson{Username: c.User.Username, Content: util.MarkdownToHTML(c.Content), Date: c.CreatedAt}) + commentsJSON = append(commentsJSON, CommentJSON{Username: c.User.Username, Content: util.MarkdownToHTML(c.Content), Date: c.CreatedAt}) } uploader := "" if t.Uploader != nil { uploader = t.Uploader.Username } torrentlink := "" - if t.Id <= config.LastOldTorrentId && len(config.TorrentCacheLink) > 0 { + if t.ID <= config.LastOldTorrentID && len(config.TorrentCacheLink) > 0 { torrentlink = fmt.Sprintf(config.TorrentCacheLink, t.Hash) - } else if t.Id > config.LastOldTorrentId && len(config.TorrentStorageLink) > 0 { - torrentlink = fmt.Sprintf(config.TorrentStorageLink, t.Hash) + } else if t.ID > config.LastOldTorrentID && len(config.TorrentStorageLink) > 0 { + torrentlink = fmt.Sprintf(config.TorrentStorageLink, t.Hash) // TODO: Fix as part of configuration changes } - res := TorrentsJson{ - Id: strconv.FormatUint(uint64(t.Id), 10), + res := TorrentJSON{ + ID: strconv.FormatUint(uint64(t.ID), 10), Name: t.Name, Status: t.Status, Hash: t.Hash, Date: t.Date.Format(time.RFC3339), Filesize: util.FormatFilesize2(t.Filesize), Description: util.MarkdownToHTML(t.Description), - Comments: commentsJson, - Sub_Category: strconv.Itoa(t.Sub_Category), + Comments: commentsJSON, + SubCategory: strconv.Itoa(t.SubCategory), Category: strconv.Itoa(t.Category), Downloads: t.Downloads, - UploaderId: t.UploaderId, + UploaderID: t.UploaderID, UploaderName: util.SafeText(uploader), WebsiteLink: util.Safe(t.WebsiteLink), Magnet: util.Safe(magnet), @@ -117,10 +119,10 @@ func (t *Torrents) ToJson() TorrentsJson { /* Complete the functions when necessary... */ // Map Torrents to TorrentsToJSON without reallocations -func TorrentsToJSON(t []Torrents) []TorrentsJson { - json := make([]TorrentsJson, len(t)) +func TorrentsToJSON(t []Torrent) []TorrentJSON { // TODO: Convert to singular version + json := make([]TorrentJSON, len(t)) for i := range t { - json[i] = t[i].ToJson() + json[i] = t[i].ToJSON() } return json } diff --git a/model/user.go b/model/user.go index 04601459..019477dc 100644 --- a/model/user.go +++ b/model/user.go @@ -5,7 +5,7 @@ import ( ) type User struct { - Id uint `gorm:"column:user_id;primary_key"` + ID uint `gorm:"column:user_id;primary_key"` Username string `gorm:"column:username"` Password string `gorm:"column:password"` Email string `gorm:"column:email"` @@ -17,17 +17,17 @@ type User struct { Language string `gorm:"column:language"` // TODO: move this to PublicUser - LikingCount int `json:"likingCount" gorm:"-"` - LikedCount int `json:"likedCount" gorm:"-"` - Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"` - Liked []User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"` + LikingCount int `json:"likingCount" gorm:"-"` + LikedCount int `json:"likedCount" gorm:"-"` + Likings []User // Don't work `gorm:"foreignkey:user_id;associationforeignkey:follower_id;many2many:user_follows"` + Liked []User // Don't work `gorm:"foreignkey:follower_id;associationforeignkey:user_id;many2many:user_follows"` - Md5 string `json:"md5"` // Used for gravatar - Torrents []Torrents `gorm:"ForeignKey:UploaderId"` + MD5 string `json:"md5"` // Hash of email address, used for Gravatar + Torrents []Torrent `gorm:"ForeignKey:UploaderId"` } type PublicUser struct { - User *User + User *User } // different users following eachother diff --git a/router/apiHandler.go b/router/apiHandler.go index 6e66cad9..a01fb58e 100644 --- a/router/apiHandler.go +++ b/router/apiHandler.go @@ -5,7 +5,6 @@ import ( "fmt" "html" "net/http" - //"sort" "strconv" "time" @@ -15,6 +14,7 @@ import ( "github.com/ewhal/nyaa/service/api" "github.com/ewhal/nyaa/service/torrent" "github.com/ewhal/nyaa/util" + "github.com/ewhal/nyaa/util/log" "github.com/gorilla/mux" ) @@ -40,26 +40,32 @@ func ApiHandler(w http.ResponseWriter, r *http.Request) { whereParams = req.ToParams() } else { - var errConv error - req.MaxPerPage, errConv = strconv.Atoi(r.URL.Query().Get("max")) - if errConv != nil || req.MaxPerPage == 0 { - req.MaxPerPage = 50 // default Value maxPerPage + var err error + maxString := r.URL.Query().Get("max") + if maxString != "" { + req.MaxPerPage, err = strconv.Atoi(maxString) + if !log.CheckError(err) { + req.MaxPerPage = 50 // default Value maxPerPage + } } - req.Page, _ = strconv.Atoi(html.EscapeString(page)) - if req.Page == 0 { - req.Page = 1 + req.Page = 1 + if page != "" { + req.Page, err = strconv.Atoi(html.EscapeString(page)) + if !log.CheckError(err) { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } } - nbTorrents := 0 torrents, nbTorrents, err := torrentService.GetTorrents(whereParams, req.MaxPerPage, req.MaxPerPage*(req.Page-1)) if err != nil { util.SendError(w, err, 400) return } - b := model.ApiResultJson{ + b := model.ApiResultJSON{ Torrents: model.TorrentsToJSON(torrents), } b.QueryRecordCount = req.MaxPerPage @@ -73,13 +79,11 @@ func ApiHandler(w http.ResponseWriter, r *http.Request) { } func ApiViewHandler(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) id := vars["id"] torrent, err := torrentService.GetTorrentById(id) - b := torrent.ToJson() - + b := torrent.ToJSON() w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(b) @@ -90,7 +94,7 @@ func ApiViewHandler(w http.ResponseWriter, r *http.Request) { } func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { - if config.UploadsDisabled == 1 { + if config.UploadsDisabled { http.Error(w, "Error uploads are disabled", http.StatusInternalServerError) return } @@ -100,13 +104,16 @@ func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") user := model.User{} db.ORM.Where("api_token = ?", token).First(&user) //i don't like this - if user.Id == 0 { + if user.ID == 0 { http.Error(w, apiService.ErrApiKey.Error(), http.StatusForbidden) return } defer r.Body.Close() + //verify token + //token := r.Header.Get("Authorization") + upload := apiService.TorrentRequest{} d := json.NewDecoder(r.Body) if err := d.Decode(&upload); err != nil { @@ -119,17 +126,17 @@ func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { return } - torrent := model.Torrents{ - Name: upload.Name, - Category: upload.Category, - Sub_Category: upload.SubCategory, - Status: 1, - Hash: upload.Hash, - Date: time.Now(), - Filesize: 0, //? - Description: upload.Description, - UploaderId: user.Id, - Uploader: &user, + torrent := model.Torrent{ + Name: upload.Name, + Category: upload.Category, + SubCategory: upload.SubCategory, + Status: 1, + Hash: upload.Hash, + Date: time.Now(), + Filesize: 0, //? + Description: upload.Description, + UploaderID: user.ID, + Uploader: &user, } db.ORM.Create(&torrent) @@ -142,7 +149,7 @@ func ApiUploadHandler(w http.ResponseWriter, r *http.Request) { } func ApiUpdateHandler(w http.ResponseWriter, r *http.Request) { - if config.UploadsDisabled == 1 { + if config.UploadsDisabled { http.Error(w, "Error uploads are disabled", http.StatusInternalServerError) return } @@ -152,7 +159,7 @@ func ApiUpdateHandler(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") user := model.User{} db.ORM.Where("api_token = ?", token).First(&user) //i don't like this - if user.Id == 0 { + if user.ID == 0 { http.Error(w, apiService.ErrApiKey.Error(), http.StatusForbidden) return } @@ -166,14 +173,14 @@ func ApiUpdateHandler(w http.ResponseWriter, r *http.Request) { return } - id := update.Id - torrent := model.Torrents{} + id := update.ID + torrent := model.Torrent{} db.ORM.Where("torrent_id = ?", id).First(&torrent) - if torrent.Id == 0 { + if torrent.ID == 0 { http.Error(w, apiService.ErrTorrentId.Error(), http.StatusBadRequest) return } - if torrent.UploaderId != 0 && torrent.UploaderId != user.Id { //&& user.Status != mod + if torrent.UploaderID != 0 && torrent.UploaderID != user.ID { //&& user.Status != mod http.Error(w, apiService.ErrRights.Error(), http.StatusForbidden) return } diff --git a/router/homeHandler.go b/router/homeHandler.go index 7ffdf097..55b16d90 100644 --- a/router/homeHandler.go +++ b/router/homeHandler.go @@ -1,16 +1,15 @@ package router import ( - "html" - "net/http" - "strconv" - "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/service/torrent" "github.com/ewhal/nyaa/util" "github.com/ewhal/nyaa/util/languages" "github.com/ewhal/nyaa/util/log" "github.com/gorilla/mux" + "html" + "net/http" + "strconv" ) func HomeHandler(w http.ResponseWriter, r *http.Request) { @@ -18,19 +17,27 @@ func HomeHandler(w http.ResponseWriter, r *http.Request) { page := vars["page"] // db params url - maxPerPage, errConv := strconv.Atoi(r.URL.Query().Get("max")) - if errConv != nil { - maxPerPage = 50 // default Value maxPerPage + var err error + maxPerPage := 50 + maxString := r.URL.Query().Get("max") + if maxString != "" { + maxPerPage, err = strconv.Atoi(maxString) + if !log.CheckError(err) { + maxPerPage = 50 // default Value maxPerPage + } } - nbTorrents := 0 - pagenum, _ := strconv.Atoi(html.EscapeString(page)) - if pagenum == 0 { - pagenum = 1 + pagenum := 1 + if page != "" { + pagenum, err = strconv.Atoi(html.EscapeString(page)) + if !log.CheckError(err) { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } torrents, nbTorrents, err := torrentService.GetAllTorrents(maxPerPage, maxPerPage*(pagenum-1)) - if err != nil { + if !log.CheckError(err) { util.SendError(w, err, 400) return } diff --git a/router/router.go b/router/router.go index 32a97dcd..a31c9cfa 100644 --- a/router/router.go +++ b/router/router.go @@ -26,7 +26,7 @@ func init() { gzipAPIUploadHandler := handlers.CompressHandler(http.HandlerFunc(ApiUploadHandler)) gzipAPIUpdateHandler := handlers.CompressHandler(http.HandlerFunc(ApiUpdateHandler)) gzipFaqHandler := handlers.CompressHandler(http.HandlerFunc(FaqHandler)) - gzipRssHandler := handlers.CompressHandler(http.HandlerFunc(RssHandler)) + gzipRSSHandler := handlers.CompressHandler(http.HandlerFunc(RSSHandler)) gzipViewHandler := handlers.CompressHandler(http.HandlerFunc(ViewHandler)) gzipUploadHandler := handlers.CompressHandler(http.HandlerFunc(UploadHandler)) gzipUserRegisterFormHandler := handlers.CompressHandler(http.HandlerFunc(UserRegisterFormHandler)) @@ -55,7 +55,7 @@ func init() { Router.Handle("/api/upload", gzipAPIUploadHandler).Methods("POST") Router.Handle("/api/update", gzipAPIUpdateHandler).Methods("PUT") Router.Handle("/faq", gzipFaqHandler).Name("faq") - Router.Handle("/feed", gzipRssHandler).Name("feed") + Router.Handle("/feed", gzipRSSHandler).Name("feed") Router.Handle("/view/{id}", gzipViewHandler).Methods("GET").Name("view_torrent") Router.HandleFunc("/view/{id}", PostCommentHandler).Methods("POST").Name("post_comment") Router.Handle("/upload", gzipUploadHandler).Name("upload") diff --git a/router/rssHandler.go b/router/rssHandler.go index 381b30a7..422b0622 100644 --- a/router/rssHandler.go +++ b/router/rssHandler.go @@ -1,53 +1,54 @@ package router import ( - "net/http" - "strconv" - "time" - "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/util" "github.com/ewhal/nyaa/util/search" "github.com/gorilla/feeds" + "net/http" + "strconv" + "time" ) -func RssHandler(w http.ResponseWriter, r *http.Request) { - +func RSSHandler(w http.ResponseWriter, r *http.Request) { _, torrents, err := search.SearchByQueryNoCount(r, 1) if err != nil { util.SendError(w, err, 400) return } - created_as_time := time.Now() + createdAsTime := time.Now() if len(torrents) > 0 { - created_as_time = torrents[0].Date + createdAsTime = torrents[0].Date } feed := &feeds.Feed{ Title: "Nyaa Pantsu", Link: &feeds.Link{Href: "https://" + config.WebAddress + "/"}, - Created: created_as_time, + Created: createdAsTime, } feed.Items = []*feeds.Item{} feed.Items = make([]*feeds.Item, len(torrents)) - for i, _ := range torrents { - torrent_json := torrents[i].ToJson() + for i := range torrents { + torrentJSON := torrents[i].ToJSON() feed.Items[i] = &feeds.Item{ // need a torrent view first - Id: "https://" + config.WebAddress + "/view/" + strconv.FormatUint(uint64(torrents[i].Id), 10), + Id: "https://" + config.WebAddress + "/view/" + strconv.FormatUint(uint64(torrents[i].ID), 10), Title: torrents[i].Name, - Link: &feeds.Link{Href: string(torrent_json.Magnet)}, + Link: &feeds.Link{Href: string(torrentJSON.Magnet)}, Description: "", Created: torrents[0].Date, Updated: torrents[0].Date, } } - rss, err := feed.ToRss() - if err == nil { - w.Write([]byte(rss)) - } else { + rss, rssErr := feed.ToRss() + if rssErr != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + _, writeErr := w.Write([]byte(rss)) + if writeErr != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } diff --git a/router/searchHandler.go b/router/searchHandler.go index dc0713f3..57bfe1f1 100644 --- a/router/searchHandler.go +++ b/router/searchHandler.go @@ -1,15 +1,15 @@ package router import ( - "html" - "net/http" - "strconv" - "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/util" "github.com/ewhal/nyaa/util/languages" + "github.com/ewhal/nyaa/util/log" "github.com/ewhal/nyaa/util/search" "github.com/gorilla/mux" + "html" + "net/http" + "strconv" ) func SearchHandler(w http.ResponseWriter, r *http.Request) { @@ -17,12 +17,17 @@ func SearchHandler(w http.ResponseWriter, r *http.Request) { page := vars["page"] // db params url - pagenum, _ := strconv.Atoi(html.EscapeString(page)) - if pagenum == 0 { - pagenum = 1 + var err error + pagenum := 1 + if page != "" { + pagenum, err = strconv.Atoi(html.EscapeString(page)) + if !log.CheckError(err) { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } - search_param, torrents, nbTorrents, err := search.SearchByQuery(r, pagenum) + searchParam, torrents, nbTorrents, err := search.SearchByQuery(r, pagenum) if err != nil { util.SendError(w, err, 400) return @@ -30,11 +35,11 @@ func SearchHandler(w http.ResponseWriter, r *http.Request) { b := model.TorrentsToJSON(torrents) - navigationTorrents := Navigation{nbTorrents, int(search_param.Max), pagenum, "search_page"} + navigationTorrents := Navigation{nbTorrents, int(searchParam.Max), pagenum, "search_page"} // Convert back to strings for now. searchForm := SearchForm{ - SearchParam: search_param, - Category: search_param.Category.String(), + SearchParam: searchParam, + Category: searchParam.Category.String(), HideAdvancedSearch: false, } htv := HomeTemplateVariables{b, searchForm, navigationTorrents, GetUser(r), r.URL, mux.CurrentRoute(r)} diff --git a/router/templateFunctions.go b/router/templateFunctions.go index 0fc3f959..9f974f71 100644 --- a/router/templateFunctions.go +++ b/router/templateFunctions.go @@ -1,14 +1,14 @@ package router import ( + "github.com/ewhal/nyaa/service/user/permission" + "github.com/nicksnyder/go-i18n/i18n" "html/template" "log" "math" "net/url" "strconv" - "github.com/nicksnyder/go-i18n/i18n" - "github.com/ewhal/nyaa/service/user/permission" - ) +) var FuncMap = template.FuncMap{ "min": math.Min, @@ -22,7 +22,7 @@ var FuncMap = template.FuncMap{ "genRouteWithQuery": func(name string, currentUrl *url.URL, params ...string) template.HTML { url, err := Router.Get(name).URL(params...) if err == nil { - return template.HTML(url.String()+ "?" + currentUrl.RawQuery) + return template.HTML(template.HTMLEscapeString(url.String() + "?" + currentUrl.RawQuery)) // TODO: Review application of character escaping } return "error" }, @@ -38,7 +38,7 @@ var FuncMap = template.FuncMap{ if nav.CurrentPage > pagesSelectable/2 { startValue = (int(math.Min((float64(nav.CurrentPage)+math.Floor(float64(pagesSelectable)/2)), maxPages)) - pagesSelectable + 1) } - endValue := (startValue + pagesSelectable -1) + endValue := (startValue + pagesSelectable - 1) if endValue > int(maxPages) { endValue = int(maxPages) } @@ -60,12 +60,12 @@ var FuncMap = template.FuncMap{ return template.HTML(ret) }, "T": i18n.IdentityTfunc, - "getAvatar": func (hash string, size int) string { - return "https://www.gravatar.com/avatar/"+hash+"?s="+strconv.Itoa(size) + "getAvatar": func(hash string, size int) string { + return "https://www.gravatar.com/avatar/" + hash + "?s=" + strconv.Itoa(size) }, - "CurrentOrAdmin": userPermission.CurrentOrAdmin, + "CurrentOrAdmin": userPermission.CurrentOrAdmin, "CurrentUserIdentical": userPermission.CurrentUserIdentical, - "HasAdmin": userPermission.HasAdmin, - "GetRole": userPermission.GetRole, - "IsFollower": userPermission.IsFollower, + "HasAdmin": userPermission.HasAdmin, + "GetRole": userPermission.GetRole, + "IsFollower": userPermission.IsFollower, } diff --git a/router/templateVariables.go b/router/templateVariables.go index 8d8761e5..280a15f6 100644 --- a/router/templateVariables.go +++ b/router/templateVariables.go @@ -35,7 +35,7 @@ type NotFoundTemplateVariables struct { } type ViewTemplateVariables struct { - Torrent model.TorrentsJson + Torrent model.TorrentJSON Captcha captcha.Captcha Search SearchForm Navigation Navigation @@ -55,15 +55,15 @@ type UserRegisterTemplateVariables struct { } type UserProfileEditVariables struct { - UserProfile *model.User - UserForm userForms.UserForm - FormErrors map[string][]string - FormInfos map[string][]string - Search SearchForm - Navigation Navigation - User *model.User - URL *url.URL // For parsing Url in templates - Route *mux.Route // For getting current route in templates + UserProfile *model.User + UserForm userForms.UserForm + FormErrors map[string][]string + FormInfos map[string][]string + Search SearchForm + Navigation Navigation + User *model.User + URL *url.URL // For parsing Url in templates + Route *mux.Route // For getting current route in templates } type UserVerifyTemplateVariables struct { @@ -96,7 +96,7 @@ type UserProfileVariables struct { } type HomeTemplateVariables struct { - ListTorrents []model.TorrentsJson + ListTorrents []model.TorrentJSON Search SearchForm Navigation Navigation User *model.User diff --git a/router/upload.go b/router/upload.go index 9bb04200..c0ba05bf 100644 --- a/router/upload.go +++ b/router/upload.go @@ -31,8 +31,8 @@ type UploadForm struct { captcha.Captcha Infohash string - CategoryId int - SubCategoryId int + CategoryID int + SubCategoryID int Filesize int64 Filepath string } @@ -86,11 +86,11 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { f.Captcha = captcha.Extract(r) if !captcha.Authenticate(f.Captcha) { - // TODO: Prettier passing of mistyoed captcha errors + // TODO: Prettier passing of mistyped Captcha errors return errors.New(captcha.ErrInvalidCaptcha.Error()) } - // trim whitespaces + // trim whitespace f.Name = util.TrimWhitespaces(f.Name) f.Description = p.Sanitize(util.TrimWhitespaces(f.Description)) f.Magnet = util.TrimWhitespaces(f.Magnet) @@ -98,17 +98,17 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { catsSplit := strings.Split(f.Category, "_") // need this to prevent out of index panics if len(catsSplit) == 2 { - CatId, err := strconv.Atoi(catsSplit[0]) + CatID, err := strconv.Atoi(catsSplit[0]) if err != nil { return ErrInvalidTorrentCategory } - SubCatId, err := strconv.Atoi(catsSplit[1]) + SubCatID, err := strconv.Atoi(catsSplit[1]) if err != nil { return ErrInvalidTorrentCategory } - f.CategoryId = CatId - f.SubCategoryId = SubCatId + f.CategoryID = CatID + f.SubCategoryID = SubCatID } else { return ErrInvalidTorrentCategory } @@ -119,7 +119,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { var torrent metainfo.TorrentFile // decode torrent - tfile.Seek(0, io.SeekStart) + _, seekErr := tfile.Seek(0, io.SeekStart) + if seekErr != nil { + return seekErr + } err = bencode.NewDecoder(tfile).Decode(&torrent) if err != nil { return metainfo.ErrInvalidTorrentFile @@ -143,7 +146,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { if len(f.Magnet) != 0 { return ErrTorrentPlusMagnet } - binInfohash := torrent.Infohash() + binInfohash, err := torrent.Infohash() + if err != nil { + return err + } f.Infohash = strings.ToUpper(hex.EncodeToString(binInfohash[:])) f.Magnet = util.InfoHashToMagnet(f.Infohash, f.Name, trackers...) @@ -151,11 +157,11 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { f.Filesize = int64(torrent.TotalSize()) } else { // No torrent file provided - magnetUrl, parseErr := url.Parse(f.Magnet) + magnetURL, parseErr := url.Parse(f.Magnet) if parseErr != nil { return metainfo.ErrInvalidTorrentFile } - exactTopic := magnetUrl.Query().Get("xt") + exactTopic := magnetURL.Query().Get("xt") if !strings.HasPrefix(exactTopic, "urn:btih:") { return metainfo.ErrInvalidTorrentFile } @@ -174,10 +180,6 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { return ErrInvalidTorrentName } - //if len(f.Description) == 0 { - // return ErrInvalidTorrentDescription - //} - // after data has been checked & extracted, write it to disk if len(config.TorrentFileStorage) > 0 { err := WriteTorrentToDisk(tfile, f.Infohash+".torrent", &f.Filepath) @@ -192,7 +194,10 @@ func (f *UploadForm) ExtractInfo(r *http.Request) error { } func WriteTorrentToDisk(file multipart.File, name string, fullpath *string) error { - file.Seek(0, io.SeekStart) + _, seekErr := file.Seek(0, io.SeekStart) + if seekErr != nil { + return seekErr + } b, err := ioutil.ReadAll(file) if err != nil { return err @@ -201,29 +206,30 @@ func WriteTorrentToDisk(file multipart.File, name string, fullpath *string) erro return ioutil.WriteFile(*fullpath, b, 0644) } -var dead_trackers = []string{ // substring matches! - "://open.nyaatorrents.info:6544", - "://tracker.openbittorrent.com:80", - "://tracker.publicbt.com:80", - "://stats.anisource.net:2710", - "://exodus.desync.com", - "://open.demonii.com:1337", - "://tracker.istole.it:80", - "://tracker.ccc.de:80", - "://bt2.careland.com.cn:6969", - "://announce.torrentsmd.com:8080"} - func CheckTrackers(trackers []string) bool { + // TODO: move to runtime configuration + var deadTrackers = []string{ // substring matches! + "://open.nyaatorrents.info:6544", + "://tracker.openbittorrent.com:80", + "://tracker.publicbt.com:80", + "://stats.anisource.net:2710", + "://exodus.desync.com", + "://open.demonii.com:1337", + "://tracker.istole.it:80", + "://tracker.ccc.de:80", + "://bt2.careland.com.cn:6969", + "://announce.torrentsmd.com:8080"} + var numGood int for _, t := range trackers { - var good bool = true - for _, check := range dead_trackers { + good := true + for _, check := range deadTrackers { if strings.Contains(t, check) { good = false } } if good { - numGood += 1 + numGood++ } } return numGood > 0 diff --git a/router/uploadHandler.go b/router/uploadHandler.go index 316f2396..7a358da8 100644 --- a/router/uploadHandler.go +++ b/router/uploadHandler.go @@ -11,59 +11,58 @@ import ( "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/service/captcha" "github.com/ewhal/nyaa/service/user" - "github.com/ewhal/nyaa/util" "github.com/ewhal/nyaa/util/languages" "github.com/gorilla/mux" ) func UploadHandler(w http.ResponseWriter, r *http.Request) { - if config.UploadsDisabled == 1 { + if config.UploadsDisabled { http.Error(w, "Error uploads are disabled", http.StatusInternalServerError) return } - var err error var uploadForm UploadForm if r.Method == "POST" { defer r.Body.Close() // validation is done in ExtractInfo() - err = uploadForm.ExtractInfo(r) - if err == nil { - user, _, err := userService.RetrieveCurrentUser(r) - if err != nil { - fmt.Printf("error %+v\n", err) - } - //add to db and redirect depending on result - torrent := model.Torrents{ - Name: uploadForm.Name, - Category: uploadForm.CategoryId, - Sub_Category: uploadForm.SubCategoryId, - Status: 1, - Hash: uploadForm.Infohash, - Date: time.Now(), - Filesize: uploadForm.Filesize, // FIXME: should set to NULL instead of 0 - Description: uploadForm.Description, - UploaderId: user.Id} - err = db.ORM.Create(&torrent).Error - if err != nil { - util.SendError(w, err, 500) - return - } - fmt.Printf("%+v\n", torrent) - url, err := Router.Get("view_torrent").URL("id", strconv.FormatUint(uint64(torrent.Id), 10)) - if err == nil { - http.Redirect(w, r, url.String(), 302) - } + err := uploadForm.ExtractInfo(r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return } + user, _, err := userService.RetrieveCurrentUser(r) + if err != nil { + fmt.Printf("error %+v\n", err) + } + //add to db and redirect depending on result + torrent := model.Torrent{ + Name: uploadForm.Name, + Category: uploadForm.CategoryID, + SubCategory: uploadForm.SubCategoryID, + Status: 1, + Hash: uploadForm.Infohash, + Date: time.Now(), + Filesize: uploadForm.Filesize, + Description: uploadForm.Description, + UploaderID: user.ID} + db.ORM.Create(&torrent) + fmt.Printf("%+v\n", torrent) + url, err := Router.Get("view_torrent").URL("id", strconv.FormatUint(uint64(torrent.ID), 10)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + http.Redirect(w, r, url.String(), 302) } else if r.Method == "GET" { uploadForm.CaptchaID = captcha.GetID() htv := UploadTemplateVariables{uploadForm, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} languages.SetTranslationFromRequest(uploadTemplate, r, "en-us") - err = uploadTemplate.ExecuteTemplate(w, "index.html", htv) + err := uploadTemplate.ExecuteTemplate(w, "index.html", htv) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } else { w.WriteHeader(http.StatusMethodNotAllowed) return } - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } } diff --git a/router/userHandler.go b/router/userHandler.go index 7fac397b..03290e84 100644 --- a/router/userHandler.go +++ b/router/userHandler.go @@ -5,7 +5,6 @@ import ( "net/http" "strconv" - "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/service/captcha" "github.com/ewhal/nyaa/service/user" @@ -16,13 +15,10 @@ import ( "github.com/gorilla/mux" ) -//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html")) -//var viewTemplate = template.Must(template.New("view").Funcs(FuncMap).ParseFiles("templates/index.html", "templates/view.html")) - // Getting View User Registration func UserRegisterFormHandler(w http.ResponseWriter, r *http.Request) { _, errorUser := userService.CurrentUser(r) - if (errorUser != nil) { + if errorUser != nil { b := form.RegistrationForm{} modelHelper.BindValueForm(&b, r) b.CaptchaID = captcha.GetID() @@ -56,15 +52,15 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) id := vars["id"] userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) - if (errorUser == nil) { - currentUser := GetUser(r) - view := r.URL.Query()["edit"] - follow := r.URL.Query()["followed"] - unfollow := r.URL.Query()["unfollowed"] - infosForm := form.NewInfos() + if errorUser == nil { + currentUser := GetUser(r) + view := r.URL.Query()["edit"] + follow := r.URL.Query()["followed"] + unfollow := r.URL.Query()["unfollowed"] + infosForm := form.NewInfos() + deleteVar := r.URL.Query()["delete"] - deleteVar := r.URL.Query()["delete"] - if ((view != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { + if (view != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) { b := form.UserForm{} modelHelper.BindValueForm(&b, r) languages.SetTranslationFromRequest(viewProfileEditTemplate, r, "en-us") @@ -74,10 +70,10 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) { if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } - } else if ((deleteVar != nil)&&(userPermission.CurrentOrAdmin(currentUser, userProfile.Id))) { + } else if (deleteVar != nil) && (userPermission.CurrentOrAdmin(currentUser, userProfile.ID)) { err := form.NewErrors() _, errUser := userService.DeleteUser(w, currentUser, id) - if (errUser != nil) { + if errUser != nil { err["errors"] = append(err["errors"], errUser.Error()) } languages.SetTranslationFromRequest(viewUserDeleteTemplate, r, "en-us") @@ -86,14 +82,14 @@ func UserProfileHandler(w http.ResponseWriter, r *http.Request) { if errorTmpl != nil { http.Error(w, errorTmpl.Error(), http.StatusInternalServerError) } - } else { + } else { T := languages.SetTranslationFromRequest(viewProfileTemplate, r, "en-us") - if (follow != nil) { + if follow != nil { infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_followed_msg"), userProfile.Username)) } - if (unfollow != nil) { + if unfollow != nil { infosForm["infos"] = append(infosForm["infos"], fmt.Sprintf(T("user_unfollowed_msg"), userProfile.Username)) - } + } htv := UserProfileVariables{&userProfile, infosForm, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} err := viewProfileTemplate.ExecuteTemplate(w, "index.html", htv) @@ -119,8 +115,8 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) { id := vars["id"] currentUser := GetUser(r) userProfile, _, errorUser := userService.RetrieveUserForAdmin(id) - if (errorUser == nil) { - if (userPermission.CurrentOrAdmin(currentUser, userProfile.Id)) { + if errorUser == nil { + if userPermission.CurrentOrAdmin(currentUser, userProfile.ID) { b := form.UserForm{} err := form.NewErrors() infos := form.NewInfos() @@ -129,20 +125,20 @@ func UserProfileFormHandler(w http.ResponseWriter, r *http.Request) { _, err = form.EmailValidation(r.PostFormValue("email"), err) } if len(r.PostFormValue("username")) > 0 { - _, err = form.ValidateUsername(r.PostFormValue("username"), err) + _, err = form.ValidateUsername(r.PostFormValue("username"), err) } - if (len(err) == 0) { + if len(err) == 0 { modelHelper.BindValueForm(&b, r) err = modelHelper.ValidateForm(&b, err) - if (len(err) == 0) { + if len(err) == 0 { userProfile, _, errorUser = userService.UpdateUser(w, &b, currentUser, id) - if (errorUser != nil) { + if errorUser != nil { err["errors"] = append(err["errors"], errorUser.Error()) } - if (len(err) == 0) { + if len(err) == 0 { infos["infos"] = append(infos["infos"], T("profile_updated")) - } - } + } + } } htv := UserProfileEditVariables{&userProfile, b, err, infos, NewSearchForm(), Navigation{}, currentUser, r.URL, mux.CurrentRoute(r)} errorTmpl := viewProfileEditTemplate.ExecuteTemplate(w, "index.html", htv) @@ -179,20 +175,20 @@ func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) { if !captcha.Authenticate(captcha.Extract(r)) { err["errors"] = append(err["errors"], "Wrong captcha!") } - if (len(err) == 0) { + if len(err) == 0 { if len(r.PostFormValue("email")) > 0 { _, err = form.EmailValidation(r.PostFormValue("email"), err) } _, err = form.ValidateUsername(r.PostFormValue("username"), err) - if (len(err) == 0) { + if len(err) == 0 { modelHelper.BindValueForm(&b, r) err = modelHelper.ValidateForm(&b, err) - if (len(err) == 0) { + if len(err) == 0 { _, errorUser := userService.CreateUser(w, r) - if (errorUser != nil) { + if errorUser != nil { err["errors"] = append(err["errors"], errorUser.Error()) } - if (len(err) == 0) { + if len(err) == 0 { languages.SetTranslationFromRequest(viewRegisterSuccessTemplate, r, "en-us") u := model.User{ Email: r.PostFormValue("email"), // indicate whether user had email set @@ -202,11 +198,11 @@ func UserRegisterPostHandler(w http.ResponseWriter, r *http.Request) { if errorTmpl != nil { http.Error(w, errorTmpl.Error(), http.StatusInternalServerError) } - } - } - } + } + } + } } - if (len(err) > 0) { + if len(err) > 0 { b.CaptchaID = captcha.GetID() languages.SetTranslationFromRequest(viewRegisterTemplate, r, "en-us") htv := UserRegisterTemplateVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} @@ -222,7 +218,7 @@ func UserVerifyEmailHandler(w http.ResponseWriter, r *http.Request) { token := vars["token"] err := form.NewErrors() _, errEmail := userService.EmailVerification(token, w) - if (errEmail != nil) { + if errEmail != nil { err["errors"] = append(err["errors"], errEmail.Error()) } languages.SetTranslationFromRequest(viewVerifySuccessTemplate, r, "en-us") @@ -239,9 +235,9 @@ func UserLoginPostHandler(w http.ResponseWriter, r *http.Request) { modelHelper.BindValueForm(&b, r) err := form.NewErrors() err = modelHelper.ValidateForm(&b, err) - if (len(err) == 0) { + if len(err) == 0 { _, errorUser := userService.CreateUserAuthentication(w, r) - if (errorUser != nil) { + if errorUser != nil { err["errors"] = append(err["errors"], errorUser.Error()) languages.SetTranslationFromRequest(viewLoginTemplate, r, "en-us") htv := UserLoginFormVariables{b, err, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} @@ -265,15 +261,14 @@ func UserLogoutHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, url.String(), http.StatusSeeOther) } - func UserFollowHandler(w http.ResponseWriter, r *http.Request) { var followAction string vars := mux.Vars(r) id := vars["id"] currentUser := GetUser(r) user, _, errorUser := userService.RetrieveUserForAdmin(id) - if (errorUser == nil) { - if (!userPermission.IsFollower(&user, currentUser)) { + if errorUser == nil { + if !userPermission.IsFollower(&user, currentUser) { followAction = "followed" userService.SetFollow(&user, currentUser) } else { @@ -281,6 +276,6 @@ func UserFollowHandler(w http.ResponseWriter, r *http.Request) { userService.RemoveFollow(&user, currentUser) } } - url, _ := Router.Get("user_profile").URL("id", strconv.Itoa(int(user.Id)), "username", user.Username) + url, _ := Router.Get("user_profile").URL("id", strconv.Itoa(int(user.ID)), "username", user.Username) http.Redirect(w, r, url.String()+"?"+followAction, http.StatusSeeOther) } diff --git a/router/viewTorrentHandler.go b/router/viewTorrentHandler.go index 9e6414a7..56da69d5 100644 --- a/router/viewTorrentHandler.go +++ b/router/viewTorrentHandler.go @@ -24,7 +24,7 @@ func ViewHandler(w http.ResponseWriter, r *http.Request) { NotFoundHandler(w, r) return } - b := torrent.ToJson() + b := torrent.ToJSON() htv := ViewTemplateVariables{b, captcha.Captcha{CaptchaID: captcha.GetID()}, NewSearchForm(), Navigation{}, GetUser(r), r.URL, mux.CurrentRoute(r)} languages.SetTranslationFromRequest(viewTemplate, r, "en-us") @@ -45,14 +45,14 @@ func PostCommentHandler(w http.ResponseWriter, r *http.Request) { currentUser := GetUser(r) content := p.Sanitize(r.FormValue("comment")) - idNum_, err := strconv.Atoi(id) - var idNum uint = uint(idNum_) - var userId uint = 0 - if currentUser.Id > 0 { - userId = currentUser.Id + idNum, err := strconv.Atoi(id) + if currentUser.ID <= 0 { + http.Error(w, "Invalid user ID", http.StatusInternalServerError) } - comment := model.Comment{TorrentId: idNum, UserId: userId, Content: content, CreatedAt: time.Now()} - err = db.ORM.Create(&comment).Error + userID := currentUser.ID + comment := model.Comment{TorrentID: uint(idNum), UserID: userID, Content: content, CreatedAt: time.Now()} + +err = db.ORM.Create(&comment).Error if err != nil { util.SendError(w, err, 500) return diff --git a/service/api/api.go b/service/api/api.go index 482a96b3..e3806c05 100644 --- a/service/api/api.go +++ b/service/api/api.go @@ -36,7 +36,7 @@ type TorrentRequest struct { } type UpdateRequest struct { - Id int `json:"id"` + ID int `json:"id"` Update TorrentRequest `json:"update"` } @@ -155,7 +155,7 @@ func (r *TorrentRequest) ValidateUpdate() (err error, code int) { } //rewrite with reflect ? -func (r *UpdateRequest) UpdateTorrent(t *model.Torrents) { +func (r *UpdateRequest) UpdateTorrent(t *model.Torrent) { if r.Update.Name != "" { t.Name = r.Update.Name } @@ -166,7 +166,7 @@ func (r *UpdateRequest) UpdateTorrent(t *model.Torrents) { t.Category = r.Update.Category } if r.Update.SubCategory != 0 { - t.Sub_Category = r.Update.SubCategory + t.SubCategory = r.Update.SubCategory } if r.Update.Description != "" { t.Description = r.Update.Description diff --git a/service/captcha/captcha.go b/service/captcha/captcha.go index bc5cb821..b90c5e18 100644 --- a/service/captcha/captcha.go +++ b/service/captcha/captcha.go @@ -19,12 +19,12 @@ func init() { captcha.SetCustomStore(captcha.NewMemoryStore(1<<10, lifetime)) } -// Captcha is to be embedded into any form struct requiring a captcha +// Captcha is to be embedded into any form struct requiring a Captcha type Captcha struct { CaptchaID, Solution string } -// GetID returns a new captcha id +// GetID returns a new Captcha ID func GetID() string { return captcha.New() } @@ -37,12 +37,12 @@ func Extract(r *http.Request) Captcha { } } -// ServeFiles serves captcha images and audio +// ServeFiles serves Captcha images and audio func ServeFiles(w http.ResponseWriter, r *http.Request) { server.ServeHTTP(w, r) } -// Authenticate check's if a captcha solution is valid +// Authenticate check's if a Captcha solution is valid func Authenticate(req Captcha) bool { return captcha.VerifyString(req.CaptchaID, req.Solution) } diff --git a/service/torrent/torrent.go b/service/torrent/torrent.go index 02ac59dc..66c55338 100644 --- a/service/torrent/torrent.go +++ b/service/torrent/torrent.go @@ -2,13 +2,12 @@ package torrentService import ( "errors" - "strconv" - "strings" - "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/util" + "strconv" + "strings" ) type WhereParams struct { @@ -36,20 +35,20 @@ func GetFeeds() (result []model.Feed, err error) { for rows.Next() { item := model.Feed{} - err = rows.Scan(&item.Id, &item.Name, &item.Hash, &item.Timestamp) + err = rows.Scan(&item.ID, &item.Name, &item.Hash, &item.Timestamp) if err != nil { return } magnet := util.InfoHashToMagnet(strings.TrimSpace(item.Hash), item.Name, config.Trackers...) item.Magnet = magnet - // memory hog + // TODO: memory hog result = append(result, item) } err = rows.Err() return } -func GetTorrentById(id string) (torrent model.Torrents, err error) { +func GetTorrentById(id string) (torrent model.Torrent, err error) { id_int, err := strconv.Atoi(id) if err != nil { return @@ -60,7 +59,7 @@ func GetTorrentById(id string) (torrent model.Torrents, err error) { if err != nil { return } - if id_int <= config.LastOldTorrentId { + if id_int <= config.LastOldTorrentID { // only preload old comments if they could actually exist tmp = tmp.Preload("OldComments") } @@ -71,10 +70,10 @@ func GetTorrentById(id string) (torrent model.Torrents, err error) { // GORM relly likes not doing its job correctly // (or maybe I'm just retarded) torrent.Uploader = new(model.User) - db.ORM.Where("user_id = ?", torrent.UploaderId).Find(torrent.Uploader) + db.ORM.Where("user_id = ?", torrent.UploaderID).Find(torrent.Uploader) for i := range torrent.Comments { torrent.Comments[i].User = new(model.User) - err = db.ORM.Where("user_id = ?", torrent.Comments[i].UserId).Find(torrent.Comments[i].User).Error + err = db.ORM.Where("user_id = ?", torrent.Comments[i].UserID).Find(torrent.Comments[i].User).Error if err != nil { return } @@ -83,22 +82,22 @@ func GetTorrentById(id string) (torrent model.Torrents, err error) { return } -func GetTorrentsOrderByNoCount(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrents, err error) { +func GetTorrentsOrderByNoCount(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, err error) { torrents, _, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, false) return } -func GetTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrents, count int, err error) { +func GetTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int) (torrents []model.Torrent, count int, err error) { torrents, count, err = getTorrentsOrderBy(parameters, orderBy, limit, offset, true) return } func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offset int, countAll bool) ( - torrents []model.Torrents, count int, err error, + torrents []model.Torrent, count int, err error, ) { var conditionArray []string if strings.HasPrefix(orderBy, "filesize") { - // torrents w/ NULL filesize fuck up the sorting on postgres + // torrents w/ NULL filesize fuck up the sorting on Postgres conditionArray = append(conditionArray, "filesize IS NOT NULL") } var params []interface{} @@ -137,32 +136,28 @@ func getTorrentsOrderBy(parameters *WhereParams, orderBy string, limit int, offs return } -/* Functions to simplify the get parameters of the main function - * - * Get Torrents with where parameters and limits, order by default - */ -func GetTorrents(parameters WhereParams, limit int, offset int) ([]model.Torrents, int, error) { +// GetTorrents obtain a list of torrents matching 'parameters' from the +// database. The list will be of length 'limit' and in default order. +// GetTorrents returns the first records found. Later records may be retrieved +// by providing a positive 'offset' +func GetTorrents(parameters WhereParams, limit int, offset int) ([]model.Torrent, int, error) { return GetTorrentsOrderBy(¶meters, "", limit, offset) } -/* Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db) - */ -func GetTorrentsDB(parameters WhereParams) ([]model.Torrents, int, error) { +// Get Torrents with where parameters but no limit and order by default (get all the torrents corresponding in the db) +func GetTorrentsDB(parameters WhereParams) ([]model.Torrent, int, error) { return GetTorrentsOrderBy(¶meters, "", 0, 0) } -/* Function to get all torrents - */ - -func GetAllTorrentsOrderBy(orderBy string, limit int, offset int) ([]model.Torrents, int, error) { +func GetAllTorrentsOrderBy(orderBy string, limit int, offset int) ([]model.Torrent, int, error) { return GetTorrentsOrderBy(nil, orderBy, limit, offset) } -func GetAllTorrents(limit int, offset int) ([]model.Torrents, int, error) { +func GetAllTorrents(limit int, offset int) ([]model.Torrent, int, error) { return GetTorrentsOrderBy(nil, "", limit, offset) } -func GetAllTorrentsDB() ([]model.Torrents, int, error) { +func GetAllTorrentsDB() ([]model.Torrent, int, error) { return GetTorrentsOrderBy(nil, "", 0, 0) } diff --git a/service/user/cookieHelper.go b/service/user/cookieHelper.go index 3ecc0be1..d63cc762 100644 --- a/service/user/cookieHelper.go +++ b/service/user/cookieHelper.go @@ -2,8 +2,6 @@ package userService import ( "errors" - "net/http" - "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" formStruct "github.com/ewhal/nyaa/service/user/form" @@ -11,12 +9,14 @@ import ( "github.com/ewhal/nyaa/util/modelHelper" "github.com/gorilla/securecookie" "golang.org/x/crypto/bcrypt" + "net/http" ) var cookieHandler = securecookie.New( securecookie.GenerateRandomKey(64), securecookie.GenerateRandomKey(32)) +// TODO: Figure out what this is about before I delete it // // UserName get username from a cookie. // func UserName(c *gin.Context) (string, error) { // var userName string @@ -47,7 +47,7 @@ func Token(r *http.Request) (string, error) { } token = cookieValue["token"] if len(token) == 0 { - return token, errors.New("Token is empty.") + return token, errors.New("token is empty") } return token, nil } @@ -91,17 +91,17 @@ func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, er if isValidEmail { log.Debug("User entered valid email.") if db.ORM.Where("email = ?", email).First(&user).RecordNotFound() { - return http.StatusNotFound, errors.New("User is not found.") + return http.StatusNotFound, errors.New("user not found") } } else { log.Debug("User entered username.") if db.ORM.Where("username = ?", email).First(&user).RecordNotFound() { - return http.StatusNotFound, errors.New("User is not found.") + return http.StatusNotFound, errors.New("user not found") } } err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(pass)) if err != nil { - return http.StatusUnauthorized, errors.New("Password incorrect.") + return http.StatusUnauthorized, errors.New("password incorrect") } status, err := SetCookie(w, user.Token) if err != nil { @@ -109,17 +109,13 @@ func SetCookieHandler(w http.ResponseWriter, email string, pass string) (int, er } w.Header().Set("X-Auth-Token", user.Token) return http.StatusOK, nil - } else { - return http.StatusNotFound, errors.New("User is not found.") } + return http.StatusNotFound, errors.New("user not found") } // RegisterHanderFromForm sets cookie from a RegistrationForm. func RegisterHanderFromForm(w http.ResponseWriter, registrationForm formStruct.RegistrationForm) (int, error) { email := registrationForm.Email - if email == "" { - email = registrationForm.Username - } pass := registrationForm.Password log.Debugf("RegisterHandler UserEmail : %s", email) log.Debugf("RegisterHandler UserPassword : %s", pass) @@ -140,16 +136,16 @@ func CurrentUser(r *http.Request) (model.User, error) { var err error token = r.Header.Get("X-Auth-Token") if len(token) > 0 { - log.Debug("header token exist.") + log.Debug("header token exists") } else { token, err = Token(r) - log.Debug("header token not exist.") + log.Debug("header token does not exist") if err != nil { return user, err } } if db.ORM.Where("api_token = ?", token).First(&user).RecordNotFound() { - return user, errors.New("User is not found.") + return user, errors.New("user not found") } err = db.ORM.Model(&user).Error return user, err diff --git a/service/user/form/formValidator.go b/service/user/form/formValidator.go index 24ee85f7..a24cf84b 100644 --- a/service/user/form/formValidator.go +++ b/service/user/form/formValidator.go @@ -15,11 +15,12 @@ func EmailValidation(email string, err map[string][]string) (bool, map[string][] if exp.MatchString(email) { return true, err } - } + } err["email"] = append(err["email"], "Email Address is not valid") return false, err } -func ValidateUsername(username string, err map[string][]string) (bool, map[string][]string) { + +func ValidateUsername(username string, err map[string][]string) (bool, map[string][]string) { exp, errorRegex := regexp.Compile(USERNAME_REGEX) if regexpCompiled := log.CheckError(errorRegex); regexpCompiled { if exp.MatchString(username) { @@ -40,38 +41,36 @@ func NewInfos() map[string][]string { infos := make(map[string][]string) return infos } -func IsAgreed(t_and_c string) bool { - if t_and_c == "1" { - return true - } - return false + +func IsAgreed(termsAndConditions string) bool { // TODO: Inline function + return termsAndConditions == "1" } // RegistrationForm is used when creating a user. type RegistrationForm struct { - Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` - Email string `form:"email" needed:"true"` - Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"Confirm_Password"` - Confirm_Password string `form:"password_confirmation" omit:"true" needed:"true"` - CaptchaID string `form:"captchaID" omit:"true" needed:"true"` - T_and_C bool `form:"t_and_c" omit:"true" needed:"true" equal:"true" hum_name:"Terms and Conditions"` + Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` + Email string `form:"email" needed:"true"` + Password string `form:"password" needed:"true" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"` + ConfirmPassword string `form:"password_confirmation" omit:"true" needed:"true"` + CaptchaID string `form:"captchaID" omit:"true" needed:"true"` + TermsAndConditions bool `form:"t_and_c" omit:"true" needed:"true" equal:"true" hum_name:"Terms and Conditions"` } // LoginForm is used when a user logs in. type LoginForm struct { - Username string `form:"username" needed="true"` - Password string `form:"password" needed="true"` + Username string `form:"username" needed:"true"` + Password string `form:"password" needed:"true"` } // UserForm is used when updating a user. type UserForm struct { - Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` - Email string `form:"email" needed:"true"` - Language string `form:"language" default:"en-us"` - CurrentPassword string `form:"password" len_min:"6" len_max:"25" omit:"true"` - Password string `form:"password" len_min:"6" len_max:"25" equalInput:"Confirm_Password"` - Confirm_Password string `form:"password_confirmation" omit:"true"` - Status int `form:"language" default:"0"` + Username string `form:"username" needed:"true" len_min:"3" len_max:"20"` + Email string `form:"email" needed:"true"` + Language string `form:"language" default:"en-us"` + CurrentPassword string `form:"password" len_min:"6" len_max:"25" omit:"true"` + Password string `form:"password" len_min:"6" len_max:"25" equalInput:"ConfirmPassword"` + ConfirmPassword string `form:"password_confirmation" omit:"true"` + Status int `form:"language" default:"0"` } // PasswordForm is used when updating a user password. diff --git a/service/user/permission/permission.go b/service/user/permission/permission.go index db85c304..b7dcae2b 100644 --- a/service/user/permission/permission.go +++ b/service/user/permission/permission.go @@ -1,9 +1,9 @@ package userPermission import ( + "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" "github.com/ewhal/nyaa/util/log" - "github.com/ewhal/nyaa/db" ) // HasAdmin checks that user has an admin permission. @@ -12,29 +12,26 @@ func HasAdmin(user *model.User) bool { } // CurrentOrAdmin check that user has admin permission or user is the current user. -func CurrentOrAdmin(user *model.User, userId uint) bool { - log.Debugf("user.Id == userId %d %d %s", user.Id, userId, user.Id == userId) - return (HasAdmin(user) || user.Id == userId) +func CurrentOrAdmin(user *model.User, userID uint) bool { + log.Debugf("user.ID == userID %d %d %s", user.ID, userID, user.ID == userID) + return (HasAdmin(user) || user.ID == userID) } -// CurrentUserIdentical check that userId is same as current user's Id. -func CurrentUserIdentical(user *model.User, userId uint) (bool) { - if user.Id != userId { - return false - } - - return true +// CurrentUserIdentical check that userID is same as current user's ID. +// TODO: Inline this +func CurrentUserIdentical(user *model.User, userID uint) bool { + return user.ID != userID } func GetRole(user *model.User) string { switch user.Status { - case -1 : + case -1: return "Banned" - case 0 : + case 0: return "Member" - case 1 : + case 1: return "Trusted Member" - case 2 : + case 2: return "Moderator" } return "Member" @@ -42,9 +39,9 @@ func GetRole(user *model.User) string { func IsFollower(user *model.User, currentUser *model.User) bool { var likingUserCount int - db.ORM.Model(&model.UserFollows{}).Where("user_id = ? and following = ?", user.Id, currentUser.Id).Count(&likingUserCount) + db.ORM.Model(&model.UserFollows{}).Where("user_id = ? and following = ?", user.ID, currentUser.ID).Count(&likingUserCount) if likingUserCount != 0 { return true } return false -} \ No newline at end of file +} diff --git a/service/user/user.go b/service/user/user.go index b2579339..29aec157 100644 --- a/service/user/user.go +++ b/service/user/user.go @@ -19,8 +19,6 @@ import ( "golang.org/x/crypto/bcrypt" ) -var userFields []string = []string{"name", "email", "createdAt", "updatedAt"} - // SuggestUsername suggest user's name if user's name already occupied. func SuggestUsername(username string) string { var count int @@ -29,17 +27,16 @@ func SuggestUsername(username string) string { log.Debugf("count Before : %d", count) if count == 0 { return username - } else { - var postfix int - for { - usernameCandidate = username + strconv.Itoa(postfix) - log.Debugf("usernameCandidate: %s\n", usernameCandidate) - db.ORM.Model(model.User{}).Where(&model.User{Username: usernameCandidate}).Count(&count) - log.Debugf("count after : %d\n", count) - postfix = postfix + 1 - if count == 0 { - break - } + } + var postfix int + for { + usernameCandidate = username + strconv.Itoa(postfix) + log.Debugf("usernameCandidate: %s\n", usernameCandidate) + db.ORM.Model(model.User{}).Where(&model.User{Username: usernameCandidate}).Count(&count) + log.Debugf("count after : %d\n", count) + postfix = postfix + 1 + if count == 0 { + break } } return usernameCandidate @@ -63,20 +60,24 @@ func CreateUserFromForm(registrationForm formStruct.RegistrationForm) (model.Use log.Debugf("registrationForm %+v\n", registrationForm) modelHelper.AssignValue(&user, ®istrationForm) if user.Email == "" { - user.Md5 = "" + user.MD5 = "" } else { - user.Md5 = crypto.GenerateMD5Hash(user.Email) + var err error + user.MD5, err = crypto.GenerateMD5Hash(user.Email) + if err != nil { + return user, err + } } token, err := crypto.GenerateRandomToken32() if err != nil { - return user, errors.New("Token not generated.") + return user, errors.New("token not generated") } user.Token = token user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay) log.Debugf("user %+v\n", user) if db.ORM.Create(&user).Error != nil { - return user, errors.New("User is not created.") + return user, errors.New("user not created") } user.CreatedAt = time.Now() @@ -96,7 +97,7 @@ func CreateUser(w http.ResponseWriter, r *http.Request) (int, error) { return http.StatusInternalServerError, fmt.Errorf("Username already taken, you can choose: %s", usernameCandidate) } if CheckEmail(registrationForm.Email) { - return http.StatusInternalServerError, errors.New("Email Address already in our database!") + return http.StatusInternalServerError, errors.New("email address already in database") } password, err := bcrypt.GenerateFromPassword([]byte(registrationForm.Password), 10) if err != nil { @@ -115,20 +116,19 @@ func CreateUser(w http.ResponseWriter, r *http.Request) (int, error) { // RetrieveUser retrieves a user. func RetrieveUser(r *http.Request, id string) (*model.PublicUser, bool, uint, int, error) { var user model.User - var currentUserId uint + var currentUserID uint var isAuthor bool - // var publicUser *model.PublicUser - // publicUser.User = &user + if db.ORM.First(&user, id).RecordNotFound() { - return nil, isAuthor, currentUserId, http.StatusNotFound, errors.New("User is not found.") + return nil, isAuthor, currentUserID, http.StatusNotFound, errors.New("user not found") } currentUser, err := CurrentUser(r) if err == nil { - currentUserId = currentUser.Id - isAuthor = currentUser.Id == user.Id + currentUserID = currentUser.ID + isAuthor = currentUser.ID == user.ID } - return &model.PublicUser{User: &user}, isAuthor, currentUserId, http.StatusOK, nil + return &model.PublicUser{User: &user}, isAuthor, currentUserID, http.StatusOK, nil } // RetrieveUsers retrieves users. @@ -144,19 +144,23 @@ func RetrieveUsers() []*model.PublicUser { // UpdateUserCore updates a user. (Applying the modifed data of user). func UpdateUserCore(user *model.User) (int, error) { if user.Email == "" { - user.Md5 = "" + user.MD5 = "" } else { - user.Md5 = crypto.GenerateMD5Hash(user.Email) + var err error + user.MD5, err = crypto.GenerateMD5Hash(user.Email) + if err != nil { + return http.StatusInternalServerError, err + } } token, err := crypto.GenerateRandomToken32() if err != nil { - return http.StatusInternalServerError, errors.New("Token not generated.") + return http.StatusInternalServerError, err } user.Token = token user.TokenExpiration = timeHelper.FewDaysLater(config.AuthTokenExpirationDay) if db.ORM.Save(user).Error != nil { - return http.StatusInternalServerError, errors.New("User is not updated.") + return http.StatusInternalServerError, err } user.UpdatedAt = time.Now() @@ -167,22 +171,20 @@ func UpdateUserCore(user *model.User) (int, error) { func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *model.User, id string) (model.User, int, error) { var user model.User if db.ORM.First(&user, id).RecordNotFound() { - return user, http.StatusNotFound, errors.New("User is not found.") + return user, http.StatusNotFound, errors.New("user not found") } if form.Password != "" { err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(form.CurrentPassword)) if err != nil && !userPermission.HasAdmin(currentUser) { log.Error("Password Incorrect.") - return user, http.StatusInternalServerError, errors.New("User is not updated. Password Incorrect.") - } else { - newPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), 10) - if err != nil { - return user, http.StatusInternalServerError, errors.New("User is not updated. Password not Generated.") - } else { - form.Password = string(newPassword) - } + return user, http.StatusInternalServerError, errors.New("password incorrect") } + newPassword, err := bcrypt.GenerateFromPassword([]byte(form.Password), 10) + if err != nil { + return user, http.StatusInternalServerError, errors.New("password not generated") + } + form.Password = string(newPassword) } else { // Then no change of password form.Password = user.Password } @@ -197,7 +199,7 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m if err != nil { return user, status, err } - if userPermission.CurrentUserIdentical(currentUser, user.Id) { + if userPermission.CurrentUserIdentical(currentUser, user.ID) { status, err = SetCookie(w, user.Token) } return user, status, err @@ -207,12 +209,12 @@ func UpdateUser(w http.ResponseWriter, form *formStruct.UserForm, currentUser *m func DeleteUser(w http.ResponseWriter, currentUser *model.User, id string) (int, error) { var user model.User if db.ORM.First(&user, id).RecordNotFound() { - return http.StatusNotFound, errors.New("User is not found.") + return http.StatusNotFound, errors.New("user not found") } if db.ORM.Delete(&user).Error != nil { - return http.StatusInternalServerError, errors.New("User is not deleted.") + return http.StatusInternalServerError, errors.New("user not deleted") } - if userPermission.CurrentUserIdentical(currentUser, user.Id) { + if userPermission.CurrentUserIdentical(currentUser, user.ID) { return ClearCookie(w) } return http.StatusOK, nil @@ -231,7 +233,7 @@ func RetrieveCurrentUser(r *http.Request) (model.User, int, error) { func RetrieveUserByEmail(email string) (*model.PublicUser, string, int, error) { var user model.User if db.ORM.Unscoped().Where("email = ?", email).First(&user).RecordNotFound() { - return &model.PublicUser{User: &user}, email, http.StatusNotFound, errors.New("User is not found.") + return &model.PublicUser{User: &user}, email, http.StatusNotFound, errors.New("user not found") } return &model.PublicUser{User: &user}, email, http.StatusOK, nil } @@ -251,7 +253,7 @@ func RetrieveUsersByEmail(email string) []*model.PublicUser { func RetrieveUserByUsername(username string) (*model.PublicUser, string, int, error) { var user model.User if db.ORM.Where("username = ?", username).First(&user).RecordNotFound() { - return &model.PublicUser{User: &user}, username, http.StatusNotFound, errors.New("User is not found.") + return &model.PublicUser{User: &user}, username, http.StatusNotFound, errors.New("user not found") } return &model.PublicUser{User: &user}, username, http.StatusOK, nil } @@ -260,11 +262,11 @@ func RetrieveUserByUsername(username string) (*model.PublicUser, string, int, er func RetrieveUserForAdmin(id string) (model.User, int, error) { var user model.User if db.ORM.Preload("Torrents").First(&user, id).RecordNotFound() { - return user, http.StatusNotFound, errors.New("User is not found.") + return user, http.StatusNotFound, errors.New("user not found") } - var liked,likings []model.User - db.ORM.Joins("JOIN user_follows on user_follows.user_id=?", user.Id).Where("users.user_id = user_follows.following").Group("users.user_id").Find(&likings) - db.ORM.Joins("JOIN user_follows on user_follows.following=?", user.Id).Where("users.user_id = user_follows.user_id").Group("users.user_id").Find(&liked) + var liked, likings []model.User + db.ORM.Joins("JOIN user_follows on user_follows.user_id=?", user.ID).Where("users.user_id = user_follows.following").Group("users.user_id").Find(&likings) + db.ORM.Joins("JOIN user_follows on user_follows.following=?", user.ID).Where("users.user_id = user_follows.user_id").Group("users.user_id").Find(&liked) user.Likings = likings user.Liked = liked return user, http.StatusOK, nil @@ -277,7 +279,7 @@ func RetrieveUsersForAdmin() []model.User { db.ORM.Find(&users) for _, user := range users { db.ORM.Model(&user) - db.ORM.Model(&user).Related("Torrents").Related("Likings").Related("Liked").Find(&model.Torrents{}) + db.ORM.Model(&user).Related("Torrents").Related("Likings").Related("Liked").Find(&model.Torrent{}) userArr = append(userArr, user) } return userArr @@ -294,15 +296,15 @@ func CreateUserAuthentication(w http.ResponseWriter, r *http.Request) (int, erro } func SetFollow(user *model.User, follower *model.User) { - if (follower.Id > 0 && user.Id > 0) { - var userFollows = model.UserFollows{UserID: user.Id, FollowerID: follower.Id} + if follower.ID > 0 && user.ID > 0 { + var userFollows = model.UserFollows{UserID: user.ID, FollowerID: follower.ID} db.ORM.Create(&userFollows) } } func RemoveFollow(user *model.User, follower *model.User) { - if (follower.Id > 0 && user.Id > 0) { - var userFollows = model.UserFollows{UserID: user.Id, FollowerID: follower.Id} + if follower.ID > 0 && user.ID > 0 { + var userFollows = model.UserFollows{UserID: user.ID, FollowerID: follower.ID} db.ORM.Delete(&userFollows) } -} \ No newline at end of file +} diff --git a/service/user/userHelper.go b/service/user/userHelper.go index d56710d0..57e4dd09 100644 --- a/service/user/userHelper.go +++ b/service/user/userHelper.go @@ -1,9 +1,9 @@ package userService import ( + "errors" "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" - "errors" "net/http" "github.com/ewhal/nyaa/util/log" @@ -23,14 +23,14 @@ func FindUserByUserName(userName string) (model.User, int, error) { func FindOrCreateUser(username string) (model.User, int, error) { var user model.User if db.ORM.Where("username=?", username).First(&user).RecordNotFound() { - var user model.User - user.Username = username - log.Debugf("user %+v\n", user) - if db.ORM.Create(&user).Error != nil { - return user, http.StatusBadRequest, errors.New("User is not created.") + var newUser model.User + newUser.Username = username + log.Debugf("user %+v\n", newUser) + if db.ORM.Create(&newUser).Error != nil { + return newUser, http.StatusBadRequest, errors.New("user not created") } - log.Debugf("retrived User %v\n", user) - return user, http.StatusOK, nil + log.Debugf("retrieved User %v\n", newUser) + return newUser, http.StatusOK, nil } return user, http.StatusBadRequest, nil } diff --git a/service/user/verification.go b/service/user/verification.go index 07b6e1d1..bfb0d122 100644 --- a/service/user/verification.go +++ b/service/user/verification.go @@ -10,6 +10,7 @@ import ( "github.com/ewhal/nyaa/config" "github.com/ewhal/nyaa/db" "github.com/ewhal/nyaa/model" + // "github.com/ewhal/nyaa/util/crypto" "github.com/ewhal/nyaa/util/email" "github.com/ewhal/nyaa/util/timeHelper" "github.com/gorilla/securecookie" @@ -20,9 +21,12 @@ var verificationHandler = securecookie.New(config.EmailTokenHashKey, nil) // SendEmailVerfication sends an email verification token via email. func SendEmailVerification(to string, token string, locale string) error { - T, _ := i18n.Tfunc(locale) - content := T("link")+" : https://"+config.WebAddress+"/verify/email/"+token - content_html := T("verify_email_content")+"
"+""+config.WebAddress+"/verify/email/"+token+"" + T, err := i18n.Tfunc(locale) + if err != nil { + return err + } + content := T("link") + " : https://" + config.WebAddress + "/verify/email/" + token + content_html := T("verify_email_content") + "
" + "" + config.WebAddress + "/verify/email/" + token + "" return email.SendEmailFromAdmin(to, T("verify_email_title"), content, content_html) return nil } @@ -31,8 +35,8 @@ func SendEmailVerification(to string, token string, locale string) error { func SendVerificationToUser(user model.User) (int, error) { validUntil := timeHelper.TwentyFourHoursLater() // TODO: longer duration? value := map[string]string{ - "t": strconv.FormatInt(validUntil.Unix(), 10), - "u": strconv.FormatUint(uint64(user.Id), 10), + "t": strconv.FormatInt(validUntil.Unix(), 10), + "u": strconv.FormatUint(uint64(user.ID), 10), "e": user.Email, } encoded, err := verificationHandler.Encode("", value) @@ -51,10 +55,10 @@ func SendVerification(r *http.Request) (int, error) { var user model.User currentUser, err := CurrentUser(r) if err != nil { - return http.StatusUnauthorized, errors.New("Unauthorized.") + return http.StatusUnauthorized, errors.New("unauthorized") } - if db.ORM.First(&user, currentUser.Id).RecordNotFound() { - return http.StatusNotFound, errors.New("User is not found.") + if db.ORM.First(&user, currentUser.ID).RecordNotFound() { + return http.StatusNotFound, errors.New("user not found") } status, err := SendVerificationToUser(user) return status, err diff --git a/templates/_badgemenu.html b/templates/_badgemenu.html index 8dbdd136..7697bc70 100644 --- a/templates/_badgemenu.html +++ b/templates/_badgemenu.html @@ -2,11 +2,11 @@ {{with .User}}