diff --git a/.codebeatignore b/.codebeatignore index e78cbe1d..5978505e 100644 --- a/.codebeatignore +++ b/.codebeatignore @@ -1,6 +1,9 @@ /public/trumbowyg/* /public/jquery-emojiarea/* /public/font-awesome-4.7.0/* +/public/jquery-3.1.1.min.js +/public/EQCSS.min.js +/public/EQCSS.js /schema/* template_list.go diff --git a/gen_router.go b/gen_router.go index d3e27aff..1e0de7be 100644 --- a/gen_router.go +++ b/gen_router.go @@ -142,6 +142,18 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { return } + err = NoBanned(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + err = NoSessionMismatch(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + switch(req.URL.Path) { case "/report/submit/": err = routeReportSubmit(w,req,user,extra_data) diff --git a/member_routes.go b/member_routes.go index e3888699..e31777b0 100644 --- a/member_routes.go +++ b/member_routes.go @@ -238,6 +238,7 @@ func routeTopicCreateSubmit(w http.ResponseWriter, r *http.Request, user User) R func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) RouteError { // TODO: Reduce this to 1MB for attachments for each file? + // TODO: Reuse this code more if r.ContentLength > int64(config.MaxRequestSize) { size, unit := convertByteUnit(float64(config.MaxRequestSize)) return CustomError("Your attachments are too big. Your files need to be smaller than "+strconv.Itoa(int(size))+unit+".", http.StatusExpectationFailed, "Error", w, r, user) @@ -343,7 +344,7 @@ func routeCreateReply(w http.ResponseWriter, r *http.Request, user User) RouteEr return LocalError("Bad IP", w, r, user) } - _, err = rstore.Create(tid, content, ipaddress, topic.ParentID, user.ID) + _, err = rstore.Create(topic, content, ipaddress, user.ID) if err != nil { return InternalError(err, w, r) } @@ -409,18 +410,10 @@ func routeLikeTopic(w http.ResponseWriter, r *http.Request, user User) RouteErro if !user.Perms.ViewTopic || !user.Perms.LikeItem { return NoPermissions(w, r, user) } - if topic.CreatedBy == user.ID { return LocalError("You can't like your own topics", w, r, user) } - err = stmts.hasLikedTopic.QueryRow(user.ID, tid).Scan(&tid) - if err != nil && err != ErrNoRows { - return InternalError(err, w, r) - } else if err != ErrNoRows { - return LocalError("You already liked this!", w, r, user) - } - _, err = users.Get(topic.CreatedBy) if err != nil && err == ErrNoRows { return LocalError("The target user doesn't exist", w, r, user) @@ -429,13 +422,10 @@ func routeLikeTopic(w http.ResponseWriter, r *http.Request, user User) RouteErro } score := 1 - _, err = stmts.createLike.Exec(score, tid, "topics", user.ID) - if err != nil { - return InternalError(err, w, r) - } - - _, err = stmts.addLikesToTopic.Exec(1, tid) - if err != nil { + err = topic.Like(score, user.ID) + if err == ErrAlreadyLiked { + return LocalError("You already liked this", w, r, user) + } else if err != nil { return InternalError(err, w, r) } @@ -456,11 +446,6 @@ func routeLikeTopic(w http.ResponseWriter, r *http.Request, user User) RouteErro // Live alerts, if the poster is online and WebSockets is enabled _ = wsHub.pushAlert(topic.CreatedBy, int(lastID), "like", "topic", user.ID, topic.CreatedBy, tid) - // Flush the topic out of the cache - tcache, ok := topics.(TopicCache) - if ok { - tcache.CacheRemove(tid) - } http.Redirect(w, r, "/topic/"+strconv.Itoa(tid), http.StatusSeeOther) return nil } @@ -499,7 +484,6 @@ func routeReplyLikeSubmit(w http.ResponseWriter, r *http.Request, user User) Rou if !user.Perms.ViewTopic || !user.Perms.LikeItem { return NoPermissions(w, r, user) } - if reply.CreatedBy == user.ID { return LocalError("You can't like your own replies", w, r, user) } @@ -573,26 +557,10 @@ func routeProfileReplyCreate(w http.ResponseWriter, r *http.Request, user User) } func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemID string) RouteError { - if !user.Loggedin { - return LoginRequired(w, r, user) - } - if user.IsBanned { - return Banned(w, r, user) - } - - err := r.ParseForm() - if err != nil { - return LocalError("Bad Form", w, r, user) - } - if r.FormValue("session") != user.Session { - return SecurityError(w, r, user) - } - itemID, err := strconv.Atoi(sitemID) if err != nil { return LocalError("Bad ID", w, r, user) } - itemType := r.FormValue("type") var fid = 1 @@ -649,23 +617,16 @@ func routeReportSubmit(w http.ResponseWriter, r *http.Request, user User, sitemI } var count int - rows, err := stmts.reportExists.Query(itemType + "_" + strconv.Itoa(itemID)) + err = stmts.reportExists.QueryRow(itemType + "_" + strconv.Itoa(itemID)).Scan(&count) if err != nil && err != ErrNoRows { return InternalError(err, w, r) } - - for rows.Next() { - err = rows.Scan(&count) - if err != nil { - return InternalError(err, w, r) - } - } if count != 0 { return LocalError("Someone has already reported this!", w, r, user) } // TODO: Repost attachments in the reports forum, so that the mods can see them - // ? - Can we do this via the TopicStore? + // ? - Can we do this via the TopicStore? Should we do a ReportStore? res, err := stmts.createReport.Exec(title, content, parseMessage(content, 0, ""), user.ID, user.ID, itemType+"_"+strconv.Itoa(itemID)) if err != nil { return InternalError(err, w, r) diff --git a/misc_test.go b/misc_test.go index 2182994e..1e25b72c 100644 --- a/misc_test.go +++ b/misc_test.go @@ -750,7 +750,9 @@ func TestReplyStore(t *testing.T) { // TODO: Test Create and Get //Create(tid int, content string, ipaddress string, fid int, uid int) (id int, err error) - rid, err := rstore.Create(1, "Fofofo", "::1", 2, 1) + topic, err := topics.Get(1) + expectNilErr(t, err) + rid, err := rstore.Create(topic, "Fofofo", "::1", 1) expectNilErr(t, err) expect(t, rid == 2, fmt.Sprintf("The next reply ID should be 2 not %d", rid)) diff --git a/reply_store.go b/reply_store.go index 40c3ce55..e054b0f2 100644 --- a/reply_store.go +++ b/reply_store.go @@ -7,7 +7,7 @@ var rstore ReplyStore type ReplyStore interface { Get(id int) (*Reply, error) - Create(tid int, content string, ipaddress string, fid int, uid int) (id int, err error) + Create(topic *Topic, content string, ipaddress string, uid int) (id int, err error) } type SQLReplyStore struct { @@ -30,24 +30,16 @@ func (store *SQLReplyStore) Get(id int) (*Reply, error) { } // TODO: Write a test for this -func (store *SQLReplyStore) Create(tid int, content string, ipaddress string, fid int, uid int) (id int, err error) { +func (store *SQLReplyStore) Create(topic *Topic, content string, ipaddress string, uid int) (id int, err error) { wcount := wordCount(content) - res, err := store.create.Exec(tid, content, parseMessage(content, fid, "forums"), ipaddress, wcount, uid) - if err != nil { - return 0, err - } - lastID, err := res.LastInsertId() + res, err := store.create.Exec(topic.ID, content, parseMessage(content, topic.ParentID, "forums"), ipaddress, wcount, uid) if err != nil { return 0, err } - _, err = stmts.addRepliesToTopic.Exec(1, uid, tid) + lastID, err := res.LastInsertId() if err != nil { - return int(lastID), err + return 0, err } - tcache, ok := topics.(TopicCache) - if ok { - tcache.CacheRemove(tid) - } - return int(lastID), err + return int(lastID), topic.AddReply(uid) } diff --git a/router_gen/route_group.go b/router_gen/route_group.go new file mode 100644 index 00000000..330ded1f --- /dev/null +++ b/router_gen/route_group.go @@ -0,0 +1,45 @@ +package main + +type RouteGroup struct { + Path string + RouteList []*RouteImpl + RunBefore []Runnable +} + +func newRouteGroup(path string, routes ...*RouteImpl) *RouteGroup { + return &RouteGroup{path, routes, []Runnable{}} +} + +func (group *RouteGroup) Not(path ...string) *RouteSubset { + routes := make([]*RouteImpl, len(group.RouteList)) + copy(routes, group.RouteList) + for i, route := range routes { + if inStringList(route.Path, path) { + routes = append(routes[:i], routes[i+1:]...) + } + } + return &RouteSubset{routes} +} + +func inStringList(needle string, list []string) bool { + for _, item := range list { + if item == needle { + return true + } + } + return false +} + +func (group *RouteGroup) Before(line string, literal ...bool) *RouteGroup { + var litItem bool + if len(literal) > 0 { + litItem = literal[0] + } + group.RunBefore = append(group.RunBefore, Runnable{line, litItem}) + return group +} + +func (group *RouteGroup) Routes(routes ...*RouteImpl) *RouteGroup { + group.RouteList = append(group.RouteList, routes...) + return group +} diff --git a/router_gen/route_subset.go b/router_gen/route_subset.go new file mode 100644 index 00000000..918bcc6c --- /dev/null +++ b/router_gen/route_subset.go @@ -0,0 +1,25 @@ +package main + +type RouteSubset struct { + RouteList []*RouteImpl +} + +func (set *RouteSubset) Before(line string, literal ...bool) *RouteSubset { + var litItem bool + if len(literal) > 0 { + litItem = literal[0] + } + for _, route := range set.RouteList { + route.RunBefore = append(route.RunBefore, Runnable{line, litItem}) + } + return set +} + +func (set *RouteSubset) Not(path ...string) *RouteSubset { + for i, route := range set.RouteList { + if inStringList(route.Path, path) { + set.RouteList = append(set.RouteList[:i], set.RouteList[i+1:]...) + } + } + return set +} diff --git a/router_gen/routes.go b/router_gen/routes.go index 45c80b5a..b96cafea 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -7,12 +7,6 @@ type RouteImpl struct { RunBefore []Runnable } -type RouteGroup struct { - Path string - RouteList []*RouteImpl - RunBefore []Runnable -} - type Runnable struct { Contents string Literal bool @@ -31,27 +25,10 @@ func (route *RouteImpl) Before(item string, literal ...bool) *RouteImpl { return route } -func newRouteGroup(path string, routes ...*RouteImpl) *RouteGroup { - return &RouteGroup{path, routes, []Runnable{}} -} - func addRouteGroup(routeGroup *RouteGroup) { routeGroups = append(routeGroups, routeGroup) } -func (group *RouteGroup) Before(line string, literal ...bool) *RouteGroup { - var litItem bool - if len(literal) > 0 { - litItem = literal[0] - } - group.RunBefore = append(group.RunBefore, Runnable{line, litItem}) - return group -} - -func (group *RouteGroup) Routes(routes ...*RouteImpl) { - group.RouteList = append(group.RouteList, routes...) -} - func blankRoute() *RouteImpl { return &RouteImpl{"", "", []string{}, []Runnable{}} } @@ -74,9 +51,10 @@ func routes() { addRoute(Route("routeChangeTheme", "/theme/")) addRoute(Route("routeShowAttachment", "/attachs/", "extra_data")) + // TODO: Reduce the number of Befores. With a new method, perhaps? reportGroup := newRouteGroup("/report/", Route("routeReportSubmit", "/report/submit/", "extra_data"), - ).Before("MemberOnly") + ).Before("MemberOnly").Before("NoBanned").Before("NoSessionMismatch") addRouteGroup(reportGroup) topicGroup := newRouteGroup("/topics/", @@ -90,20 +68,19 @@ func routes() { } // TODO: Test the email token route -// TODO: Add a BeforeExcept method? func buildUserRoutes() { - userGroup := newRouteGroup("/user/") //.Before("MemberOnly") + userGroup := newRouteGroup("/user/") userGroup.Routes( Route("routeProfile", "/user/").Before("req.URL.Path += extra_data", true), - Route("routeAccountOwnEditCritical", "/user/edit/critical/").Before("MemberOnly"), - Route("routeAccountOwnEditCriticalSubmit", "/user/edit/critical/submit/").Before("MemberOnly"), - Route("routeAccountOwnEditAvatar", "/user/edit/avatar/").Before("MemberOnly"), - Route("routeAccountOwnEditAvatarSubmit", "/user/edit/avatar/submit/").Before("MemberOnly"), - Route("routeAccountOwnEditUsername", "/user/edit/username/").Before("MemberOnly"), - Route("routeAccountOwnEditUsernameSubmit", "/user/edit/username/submit/").Before("MemberOnly"), - Route("routeAccountOwnEditEmail", "/user/edit/email/").Before("MemberOnly"), - Route("routeAccountOwnEditEmailTokenSubmit", "/user/edit/token/", "extra_data").Before("MemberOnly"), - ) + Route("routeAccountOwnEditCritical", "/user/edit/critical/"), + Route("routeAccountOwnEditCriticalSubmit", "/user/edit/critical/submit/"), + Route("routeAccountOwnEditAvatar", "/user/edit/avatar/"), + Route("routeAccountOwnEditAvatarSubmit", "/user/edit/avatar/submit/"), + Route("routeAccountOwnEditUsername", "/user/edit/username/"), + Route("routeAccountOwnEditUsernameSubmit", "/user/edit/username/submit/"), + Route("routeAccountOwnEditEmail", "/user/edit/email/"), + Route("routeAccountOwnEditEmailTokenSubmit", "/user/edit/token/", "extra_data"), + ).Not("/user/").Before("MemberOnly") addRouteGroup(userGroup) } diff --git a/routes_common.go b/routes_common.go index 0bdddee6..fc4d908a 100644 --- a/routes_common.go +++ b/routes_common.go @@ -310,7 +310,34 @@ func SuperModOnly(w http.ResponseWriter, r *http.Request, user User) RouteError // MemberOnly makes sure that only logged in users can access this route func MemberOnly(w http.ResponseWriter, r *http.Request, user User) RouteError { if !user.Loggedin { - return NoPermissions(w, r, user) // TODO: Do an error telling them to login instead? + return LoginRequired(w, r, user) + } + return nil +} + +// NoBanned stops any banned users from accessing this route +func NoBanned(w http.ResponseWriter, r *http.Request, user User) RouteError { + if user.IsBanned { + return Banned(w, r, user) + } + return nil +} + +func ParseForm(w http.ResponseWriter, r *http.Request, user User) RouteError { + err := r.ParseForm() + if err != nil { + return LocalError("Bad Form", w, r, user) + } + return nil +} + +func NoSessionMismatch(w http.ResponseWriter, r *http.Request, user User) RouteError { + err := r.ParseForm() + if err != nil { + return LocalError("Bad Form", w, r, user) + } + if r.FormValue("session") != user.Session { + return SecurityError(w, r, user) } return nil } diff --git a/setting.go b/setting.go index 102c287a..d11f2848 100644 --- a/setting.go +++ b/setting.go @@ -77,11 +77,8 @@ func (sBox SettingBox) ParseSetting(sname string, scontent string, stype string, } con1, err := strconv.Atoi(cons[0]) - if err != nil { - return "Invalid contraint! The constraint field wasn't an integer!" - } - con2, err := strconv.Atoi(cons[1]) - if err != nil { + con2, err2 := strconv.Atoi(cons[1]) + if err != nil || err2 != nil { return "Invalid contraint! The constraint field wasn't an integer!" } diff --git a/template_list.go b/template_list.go index ae1ce3f7..4d87476b 100644 --- a/template_list.go +++ b/template_list.go @@ -505,11 +505,11 @@ var profile_0 = []byte(`