31f506c50c
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.
330 lines
11 KiB
Go
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
|
|
}
|