gosora/routes_common.go
Azareal 31f506c50c Removed the images for Cosmo from /images/, it's very saddening to see it go :(
Moved Reload from the UserStore into the UserCache.
Replaced many of the calls to Reload with CacheRemove.
Simplified the error functions.
Fixed a bug in the errors where they didn't have any CSS. Oops.
Added CustomErrorJS(), unused for now, but it may be useful in the future, especially for plugins.
Fixed many problems in the themes.
Added the Stick(), Unstick(), CreateActionReply(), Lock() and Unlock() methods to *Topic.
Renamed ip-search.html to ip-search-results.html
Added the Activate() and initPerms methods to *User.

Tempra Cursive is hiding from sight for this one commit.
Added Font Awesome to /public/, it isn't used yet.
2017-09-22 03:21:17 +01:00

330 lines
11 KiB
Go

package main
import (
"html"
"html/template"
"log"
"net"
"net/http"
"strconv"
"strings"
)
// nolint
var PreRoute func(http.ResponseWriter, *http.Request) (User, bool) = preRoute
// TODO: Come up with a better middleware solution
// nolint We need these types so people can tell what they are without scrolling to the bottom of the file
var PanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderVars, PanelStats, bool) = panelUserCheck
var SimplePanelUserCheck func(http.ResponseWriter, *http.Request, *User) (*HeaderLite, bool) = simplePanelUserCheck
var SimpleForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, success bool) = simpleForumUserCheck
var ForumUserCheck func(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, success bool) = forumUserCheck
var MemberCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) = memberCheck
var SimpleUserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, success bool) = simpleUserCheck
var UserCheck func(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) = userCheck
// This is mostly for errors.go, please create *HeaderVars on the spot instead of relying on this or the atomic store underlying it, if possible
// TODO: Write a test for this
func getDefaultHeaderVar() *HeaderVars {
return &HeaderVars{Site: site, ThemeName: fallbackTheme}
}
// TODO: Support for left sidebars and sidebars on both sides
// http.Request is for context.Context middleware. Mostly for plugin_socialgroups right now
func BuildWidgets(zone string, data interface{}, headerVars *HeaderVars, r *http.Request) {
if vhooks["intercept_build_widgets"] != nil {
if runVhook("intercept_build_widgets", zone, data, headerVars, r).(bool) {
return
}
}
//log.Print("themes[headerVars.ThemeName].Sidebars",themes[headerVars.ThemeName].Sidebars)
if themes[headerVars.ThemeName].Sidebars == "right" {
if len(docks.RightSidebar) != 0 {
var sbody string
for _, widget := range docks.RightSidebar {
if widget.Enabled {
if widget.Location == "global" || widget.Location == zone {
sbody += widget.Body
}
}
}
headerVars.Widgets.RightSidebar = template.HTML(sbody)
}
}
}
func simpleForumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerLite *HeaderLite, success bool) {
if !fstore.Exists(fid) {
PreError("The target forum doesn't exist.", w, r)
return nil, false
}
success = true
// Is there a better way of doing the skip AND the success flag on this hook like multiple returns?
if vhooks["simple_forum_check_pre_perms"] != nil {
if runVhook("simple_forum_check_pre_perms", w, r, user, &fid, &success, &headerLite).(bool) {
return headerLite, success
}
}
group, err := gstore.Get(user.Group)
if err != nil {
PreError("Something weird happened", w, r)
log.Print("Group #" + strconv.Itoa(user.Group) + " doesn't exist despite being used by User #" + strconv.Itoa(user.ID))
return
}
fperms := group.Forums[fid]
if fperms.Overrides && !user.IsSuperAdmin {
user.Perms.ViewTopic = fperms.ViewTopic
user.Perms.LikeItem = fperms.LikeItem
user.Perms.CreateTopic = fperms.CreateTopic
user.Perms.EditTopic = fperms.EditTopic
user.Perms.DeleteTopic = fperms.DeleteTopic
user.Perms.CreateReply = fperms.CreateReply
user.Perms.EditReply = fperms.EditReply
user.Perms.DeleteReply = fperms.DeleteReply
user.Perms.PinTopic = fperms.PinTopic
user.Perms.CloseTopic = fperms.CloseTopic
if len(fperms.ExtData) != 0 {
for name, perm := range fperms.ExtData {
user.PluginPerms[name] = perm
}
}
}
return headerLite, true
}
func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int) (headerVars *HeaderVars, success bool) {
headerVars, success = UserCheck(w, r, user)
if !fstore.Exists(fid) {
NotFound(w, r)
return headerVars, false
}
if vhooks["forum_check_pre_perms"] != nil {
if runVhook("forum_check_pre_perms", w, r, user, &fid, &success, &headerVars).(bool) {
return headerVars, success
}
}
group, err := gstore.Get(user.Group)
if err != nil {
PreError("Something weird happened", w, r)
log.Print("Group #" + strconv.Itoa(user.Group) + " doesn't exist despite being used by User #" + strconv.Itoa(user.ID))
return
}
fperms := group.Forums[fid]
//log.Printf("user.Perms: %+v\n", user.Perms)
//log.Printf("fperms: %+v\n", fperms)
if fperms.Overrides && !user.IsSuperAdmin {
user.Perms.ViewTopic = fperms.ViewTopic
user.Perms.LikeItem = fperms.LikeItem
user.Perms.CreateTopic = fperms.CreateTopic
user.Perms.EditTopic = fperms.EditTopic
user.Perms.DeleteTopic = fperms.DeleteTopic
user.Perms.CreateReply = fperms.CreateReply
user.Perms.EditReply = fperms.EditReply
user.Perms.DeleteReply = fperms.DeleteReply
user.Perms.PinTopic = fperms.PinTopic
user.Perms.CloseTopic = fperms.CloseTopic
if len(fperms.ExtData) != 0 {
for name, perm := range fperms.ExtData {
user.PluginPerms[name] = perm
}
}
}
return headerVars, success
}
// Even if they have the right permissions, the control panel is only open to supermods+. There are many areas without subpermissions which assume that the current user is a supermod+ and admins are extremely unlikely to give these permissions to someone who isn't at-least a supermod to begin with
// TODO: Do a panel specific theme?
func panelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, stats PanelStats, success bool) {
var themeName = defaultThemeBox.Load().(string)
cookie, err := r.Cookie("current_theme")
if err == nil {
cookie := html.EscapeString(cookie.Value)
theme, ok := themes[cookie]
if ok && !theme.HideFromThemes {
themeName = cookie
}
}
headerVars = &HeaderVars{
Site: site,
Settings: settingBox.Load().(SettingBox),
Themes: themes,
ThemeName: themeName,
}
// TODO: We should probably initialise headerVars.ExtData
if !user.IsSuperMod {
NoPermissions(w, r, *user)
return headerVars, stats, false
}
headerVars.Stylesheets = append(headerVars.Stylesheets, headerVars.ThemeName+"/panel.css")
if len(themes[headerVars.ThemeName].Resources) != 0 {
rlist := themes[headerVars.ThemeName].Resources
for _, resource := range rlist {
if resource.Location == "global" || resource.Location == "panel" {
halves := strings.Split(resource.Name, ".")
if len(halves) != 2 {
continue
}
if halves[1] == "css" {
headerVars.Stylesheets = append(headerVars.Stylesheets, resource.Name)
} else if halves[1] == "js" {
headerVars.Scripts = append(headerVars.Scripts, resource.Name)
}
}
}
}
err = groupCountStmt.QueryRow().Scan(&stats.Groups)
if err != nil {
InternalError(err, w)
return headerVars, stats, false
}
stats.Users = users.GetGlobalCount()
stats.Forums = fstore.GetGlobalCount() // TODO: Stop it from showing the blanked forums
stats.Settings = len(headerVars.Settings)
stats.WordFilters = len(wordFilterBox.Load().(WordFilterBox))
stats.Themes = len(themes)
stats.Reports = 0 // TODO: Do the report count. Only show open threads?
pusher, ok := w.(http.Pusher)
if ok {
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
pusher.Push("/static/"+headerVars.ThemeName+"/panel.css", nil)
pusher.Push("/static/global.js", nil)
pusher.Push("/static/jquery-3.1.1.min.js", nil)
// TODO: Push the theme CSS files
// TODO: Push the theme scripts
// TODO: Push avatars?
}
return headerVars, stats, true
}
func simplePanelUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, success bool) {
if !user.IsSuperMod {
NoPermissions(w, r, *user)
return headerLite, false
}
headerLite = &HeaderLite{
Site: site,
Settings: settingBox.Load().(SettingBox),
}
return headerLite, true
}
// TODO: Add this to the member routes
func memberCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) {
headerVars, success = UserCheck(w, r, user)
if !user.Loggedin {
NoPermissions(w, r, *user)
return headerVars, false
}
return headerVars, success
}
// SimpleUserCheck is back from the grave, yay :D
func simpleUserCheck(w http.ResponseWriter, r *http.Request, user *User) (headerLite *HeaderLite, success bool) {
headerLite = &HeaderLite{
Site: site,
Settings: settingBox.Load().(SettingBox),
}
return headerLite, true
}
// TODO: Add the ability for admins to restrict certain themes to certain groups?
func userCheck(w http.ResponseWriter, r *http.Request, user *User) (headerVars *HeaderVars, success bool) {
var themeName = defaultThemeBox.Load().(string)
cookie, err := r.Cookie("current_theme")
if err == nil {
cookie := html.EscapeString(cookie.Value)
theme, ok := themes[cookie]
if ok && !theme.HideFromThemes {
themeName = cookie
}
}
headerVars = &HeaderVars{
Site: site,
Settings: settingBox.Load().(SettingBox),
Themes: themes,
ThemeName: themeName,
}
if user.IsBanned {
headerVars.NoticeList = append(headerVars.NoticeList, "Your account has been suspended. Some of your permissions may have been revoked.")
}
if len(themes[headerVars.ThemeName].Resources) != 0 {
rlist := themes[headerVars.ThemeName].Resources
for _, resource := range rlist {
if resource.Location == "global" || resource.Location == "frontend" {
halves := strings.Split(resource.Name, ".")
if len(halves) != 2 {
continue
}
if halves[1] == "css" {
headerVars.Stylesheets = append(headerVars.Stylesheets, resource.Name)
} else if halves[1] == "js" {
headerVars.Scripts = append(headerVars.Scripts, resource.Name)
}
}
}
}
pusher, ok := w.(http.Pusher)
if ok {
pusher.Push("/static/"+headerVars.ThemeName+"/main.css", nil)
pusher.Push("/static/global.js", nil)
pusher.Push("/static/jquery-3.1.1.min.js", nil)
// TODO: Push the theme CSS files
// TODO: Push the theme scripts
// TODO: Push avatars?
}
return headerVars, true
}
func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) {
user, halt := auth.SessionCheck(w, r)
if halt {
return *user, false
}
if user == &guestUser {
return *user, true
}
host, _, err := net.SplitHostPort(r.RemoteAddr)
if err != nil {
PreError("Bad IP", w, r)
return *user, false
}
if host != user.LastIP {
_, err = updateLastIPStmt.Exec(host, user.ID)
if err != nil {
InternalError(err, w)
return *user, false
}
user.LastIP = host // ! - Is this racey?
}
h := w.Header()
h.Set("X-Frame-Options", "deny")
//h.Set("X-XSS-Protection", "1")
// TODO: Set the content policy header
return *user, true
}