2017-09-10 16:57:22 +00:00
package main
2017-09-10 17:05:13 +00:00
import (
"html"
"html/template"
2017-09-15 22:20:01 +00:00
"log"
2017-09-10 17:05:13 +00:00
"net"
"net/http"
2017-09-15 22:20:01 +00:00
"strconv"
2017-09-10 17:05:13 +00:00
"strings"
)
// nolint
var PreRoute func ( http . ResponseWriter , * http . Request ) ( User , bool ) = preRoute
2017-09-15 22:20:01 +00:00
// TODO: Come up with a better middleware solution
2017-09-10 17:05:13 +00:00
// nolint We need these types so people can tell what they are without scrolling to the bottom of the file
2017-09-10 17:39:16 +00:00
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
2017-09-15 22:20:01 +00:00
var MemberCheck func ( w http . ResponseWriter , r * http . Request , user * User ) ( headerVars * HeaderVars , success bool ) = memberCheck
2017-09-10 17:39:16 +00:00
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
2017-09-10 17:05:13 +00:00
2017-09-22 02:21:17 +00:00
// 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 }
}
2017-09-10 17:05:13 +00:00
// 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 )
}
}
}
2017-09-10 17:39:16 +00:00
func simpleForumUserCheck ( w http . ResponseWriter , r * http . Request , user * User , fid int ) ( headerLite * HeaderLite , success bool ) {
2017-09-10 17:05:13 +00:00
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
}
}
2017-09-15 22:20:01 +00:00
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 ]
2017-09-10 17:05:13 +00:00
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
}
2017-09-10 17:39:16 +00:00
func forumUserCheck ( w http . ResponseWriter , r * http . Request , user * User , fid int ) ( headerVars * HeaderVars , success bool ) {
headerVars , success = UserCheck ( w , r , user )
2017-09-10 17:05:13 +00:00
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
}
}
2017-09-15 22:20:01 +00:00
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 ]
2017-09-10 17:05:13 +00:00
//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?
2017-09-10 17:39:16 +00:00
func panelUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerVars * HeaderVars , stats PanelStats , success bool ) {
2017-09-10 17:05:13 +00:00
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 )
}
}
}
}
2017-09-18 17:03:52 +00:00
err = groupCountStmt . QueryRow ( ) . Scan ( & stats . Groups )
2017-09-10 17:05:13 +00:00
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
}
2017-09-10 17:39:16 +00:00
func simplePanelUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerLite * HeaderLite , success bool ) {
2017-09-10 17:05:13 +00:00
if ! user . IsSuperMod {
NoPermissions ( w , r , * user )
return headerLite , false
}
headerLite = & HeaderLite {
Site : site ,
Settings : settingBox . Load ( ) . ( SettingBox ) ,
}
return headerLite , true
}
2017-09-15 22:20:01 +00:00
// 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
}
2017-09-10 17:39:16 +00:00
// SimpleUserCheck is back from the grave, yay :D
func simpleUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerLite * HeaderLite , success bool ) {
2017-09-10 17:05:13 +00:00
headerLite = & HeaderLite {
Site : site ,
Settings : settingBox . Load ( ) . ( SettingBox ) ,
}
return headerLite , true
}
// TODO: Add the ability for admins to restrict certain themes to certain groups?
2017-09-10 17:39:16 +00:00
func userCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerVars * HeaderVars , success bool ) {
2017-09-10 17:05:13 +00:00
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 {
2017-09-18 17:03:52 +00:00
_ , err = updateLastIPStmt . Exec ( host , user . ID )
2017-09-10 17:05:13 +00:00
if err != nil {
InternalError ( err , w )
return * user , false
}
2017-09-22 02:21:17 +00:00
user . LastIP = host // ! - Is this racey?
2017-09-10 17:05:13 +00:00
}
h := w . Header ( )
h . Set ( "X-Frame-Options" , "deny" )
//h.Set("X-XSS-Protection", "1")
// TODO: Set the content policy header
return * user , true
}