2017-11-11 04:06:16 +00:00
package common
2017-09-10 16:57:22 +00:00
2017-09-10 17:05:13 +00:00
import (
2019-10-11 00:57:33 +00:00
"crypto/subtle"
2017-09-10 17:05:13 +00:00
"html"
2019-10-11 00:57:33 +00:00
"io"
2017-09-10 17:05:13 +00:00
"net"
"net/http"
2019-10-11 00:57:33 +00:00
"os"
"regexp"
2018-01-14 12:03:20 +00:00
"strconv"
2017-09-10 17:05:13 +00:00
"strings"
2018-11-17 02:36:02 +00:00
"time"
2019-04-09 07:21:00 +00:00
"github.com/Azareal/Gosora/common/phrases"
2020-02-29 01:10:39 +00:00
"github.com/Azareal/Gosora/uutils"
2017-09-10 17:05:13 +00:00
)
// 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
2018-04-22 12:33:56 +00:00
var PanelUserCheck func ( http . ResponseWriter , * http . Request , * User ) ( * Header , PanelStats , RouteError ) = panelUserCheck
2017-11-11 04:06:16 +00:00
var SimplePanelUserCheck func ( http . ResponseWriter , * http . Request , * User ) ( * HeaderLite , RouteError ) = simplePanelUserCheck
var SimpleForumUserCheck func ( w http . ResponseWriter , r * http . Request , user * User , fid int ) ( headerLite * HeaderLite , err RouteError ) = simpleForumUserCheck
2018-11-12 09:23:36 +00:00
var ForumUserCheck func ( header * Header , w http . ResponseWriter , r * http . Request , user * User , fid int ) ( err RouteError ) = forumUserCheck
2017-11-11 04:06:16 +00:00
var SimpleUserCheck func ( w http . ResponseWriter , r * http . Request , user * User ) ( headerLite * HeaderLite , err RouteError ) = simpleUserCheck
2018-04-22 12:33:56 +00:00
var UserCheck func ( w http . ResponseWriter , r * http . Request , user * User ) ( header * Header , err RouteError ) = userCheck
2020-02-29 01:10:39 +00:00
var UserCheckNano func ( w http . ResponseWriter , r * http . Request , user * User , nano int64 ) ( header * Header , err RouteError ) = userCheck2
2017-09-22 02:21:17 +00:00
2018-10-21 13:54:32 +00:00
func simpleForumUserCheck ( w http . ResponseWriter , r * http . Request , user * User , fid int ) ( header * HeaderLite , rerr RouteError ) {
header , rerr = SimpleUserCheck ( w , r , user )
if rerr != nil {
return header , rerr
}
2017-11-23 05:37:08 +00:00
if ! Forums . Exists ( fid ) {
2017-10-30 09:57:08 +00:00
return nil , PreError ( "The target forum doesn't exist." , w , r )
2017-09-10 17:05:13 +00:00
}
// Is there a better way of doing the skip AND the success flag on this hook like multiple returns?
2018-10-21 13:54:32 +00:00
skip , rerr := header . Hooks . VhookSkippable ( "simple_forum_check_pre_perms" , w , r , user , & fid , & header )
if skip || rerr != nil {
return header , rerr
2017-09-10 17:05:13 +00:00
}
2017-11-23 05:37:08 +00:00
fperms , err := FPStore . Get ( fid , user . Group )
2018-04-24 04:09:31 +00:00
if err == ErrNoRows {
fperms = BlankForumPerms ( )
} else if err != nil {
2018-10-21 13:54:32 +00:00
return header , InternalError ( err , w , r )
2017-09-15 22:20:01 +00:00
}
2017-10-31 07:26:44 +00:00
cascadeForumPerms ( fperms , user )
2018-10-21 13:54:32 +00:00
return header , nil
2017-09-10 17:05:13 +00:00
}
2018-11-12 09:23:36 +00:00
func forumUserCheck ( header * Header , w http . ResponseWriter , r * http . Request , user * User , fid int ) ( rerr RouteError ) {
2017-11-23 05:37:08 +00:00
if ! Forums . Exists ( fid ) {
2018-11-12 09:23:36 +00:00
return NotFound ( w , r , header )
2017-09-10 17:05:13 +00:00
}
2018-10-21 13:54:32 +00:00
skip , rerr := header . Hooks . VhookSkippable ( "forum_check_pre_perms" , w , r , user , & fid , & header )
if skip || rerr != nil {
2018-11-12 09:23:36 +00:00
return rerr
2017-09-10 17:05:13 +00:00
}
2017-11-23 05:37:08 +00:00
fperms , err := FPStore . Get ( fid , user . Group )
2018-04-24 04:09:31 +00:00
if err == ErrNoRows {
fperms = BlankForumPerms ( )
} else if err != nil {
2018-11-12 09:23:36 +00:00
return InternalError ( err , w , r )
2017-09-15 22:20:01 +00:00
}
2017-10-31 07:26:44 +00:00
cascadeForumPerms ( fperms , user )
2020-03-18 09:21:34 +00:00
header . CurrentUser = user // TODO: Use a pointer instead for CurrentUser, so we don't have to do this
2018-11-12 09:23:36 +00:00
return rerr
2017-10-31 07:26:44 +00:00
}
// TODO: Put this on the user instance? Do we really want forum specific logic in there? Maybe, a method which spits a new pointer with the same contents as user?
2019-08-31 22:59:00 +00:00
func cascadeForumPerms ( fp * ForumPerms , u * User ) {
if fp . Overrides && ! u . IsSuperAdmin {
u . Perms . ViewTopic = fp . ViewTopic
u . Perms . LikeItem = fp . LikeItem
u . Perms . CreateTopic = fp . CreateTopic
u . Perms . EditTopic = fp . EditTopic
u . Perms . DeleteTopic = fp . DeleteTopic
u . Perms . CreateReply = fp . CreateReply
u . Perms . EditReply = fp . EditReply
u . Perms . DeleteReply = fp . DeleteReply
u . Perms . PinTopic = fp . PinTopic
u . Perms . CloseTopic = fp . CloseTopic
u . Perms . MoveTopic = fp . MoveTopic
if len ( fp . ExtData ) != 0 {
for name , perm := range fp . ExtData {
u . PluginPerms [ name ] = perm
2017-09-10 17:05:13 +00:00
}
}
}
}
// 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?
2020-02-28 04:52:45 +00:00
func panelUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( h * Header , stats PanelStats , rerr RouteError ) {
2019-04-29 12:23:17 +00:00
theme := GetThemeByReq ( r )
2020-02-28 04:52:45 +00:00
h = & Header {
2018-04-22 12:33:56 +00:00
Site : Site ,
Settings : SettingBox . Load ( ) . ( SettingMap ) ,
Themes : Themes ,
Theme : theme ,
2020-03-18 09:21:34 +00:00
CurrentUser : user ,
2018-10-21 13:54:32 +00:00
Hooks : GetHookTable ( ) ,
2018-04-22 12:33:56 +00:00
Zone : "panel" ,
Writer : w ,
2019-04-09 07:21:00 +00:00
IsoCode : phrases . GetLangPack ( ) . IsoCode ,
2020-02-29 01:10:39 +00:00
//StartedAt: time.Now(),
StartedAt : uutils . Nanotime ( ) ,
2017-09-10 17:05:13 +00:00
}
2018-04-22 12:33:56 +00:00
// TODO: We should probably initialise header.ExtData
2018-11-17 02:36:02 +00:00
// ? - Should we only show this in debug mode? It might be useful for detecting issues in production, if we show it there as-well
2020-02-23 09:08:47 +00:00
//if user.IsAdmin {
2020-02-28 04:52:45 +00:00
//h.StartedAt = time.Now()
2020-02-23 09:08:47 +00:00
//}
2017-09-10 17:05:13 +00:00
2020-02-28 04:52:45 +00:00
h . AddSheet ( theme . Name + "/main.css" )
h . AddSheet ( theme . Name + "/panel.css" )
2017-11-23 05:37:08 +00:00
if len ( theme . Resources ) > 0 {
rlist := theme . Resources
2020-02-23 09:08:47 +00:00
for _ , res := range rlist {
if res . Location == "global" || res . Location == "panel" {
extarr := strings . Split ( res . Name , "." )
2017-10-12 03:24:14 +00:00
ext := extarr [ len ( extarr ) - 1 ]
if ext == "css" {
2020-02-28 04:52:45 +00:00
h . AddSheet ( res . Name )
2017-10-12 03:24:14 +00:00
} else if ext == "js" {
2020-02-23 09:08:47 +00:00
if res . Async {
2020-02-28 04:52:45 +00:00
h . AddScriptAsync ( res . Name )
2019-03-21 22:59:41 +00:00
} else {
2020-02-28 04:52:45 +00:00
h . AddScript ( res . Name )
2019-03-21 22:59:41 +00:00
}
2017-09-10 17:05:13 +00:00
}
}
}
}
2018-08-11 15:53:42 +00:00
//h := w.Header()
//h.Set("Content-Security-Policy", "default-src 'self'")
2018-06-06 00:21:22 +00:00
// TODO: GDPR. Add a global control panel notice warning the admins of staff members who don't have 2FA enabled
2019-06-01 12:31:48 +00:00
stats . Users = Users . Count ( )
stats . Groups = Groups . Count ( )
stats . Forums = Forums . Count ( )
stats . Pages = Pages . Count ( )
2020-02-28 04:52:45 +00:00
stats . Settings = len ( h . Settings )
2018-08-04 11:46:36 +00:00
stats . WordFilters = WordFilters . EstCount ( )
2017-11-11 04:06:16 +00:00
stats . Themes = len ( Themes )
2017-09-10 17:05:13 +00:00
stats . Reports = 0 // TODO: Do the report count. Only show open threads?
2019-08-31 22:59:00 +00:00
addPreScript := func ( name string ) {
// TODO: Optimise this by removing a superfluous string alloc
2019-02-28 07:28:17 +00:00
var tname string
if theme . OverridenMap != nil {
2019-03-05 05:53:02 +00:00
_ , ok := theme . OverridenMap [ name ]
2019-02-28 07:28:17 +00:00
if ok {
tname = "_" + theme . Name
}
}
2020-02-28 04:52:45 +00:00
h . AddPreScriptAsync ( "template_" + name + tname + ".js" )
2019-02-28 07:28:17 +00:00
}
addPreScript ( "alert" )
2019-04-27 06:32:26 +00:00
addPreScript ( "notice" )
2019-02-28 07:28:17 +00:00
2020-02-28 04:52:45 +00:00
return h , stats , nil
2017-09-10 17:05:13 +00:00
}
2017-11-11 04:06:16 +00:00
func simplePanelUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerLite * HeaderLite , rerr RouteError ) {
2018-10-21 13:54:32 +00:00
return SimpleUserCheck ( w , r , user )
2017-09-15 22:20:01 +00:00
}
2017-09-10 17:39:16 +00:00
// SimpleUserCheck is back from the grave, yay :D
2017-11-11 04:06:16 +00:00
func simpleUserCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( headerLite * HeaderLite , rerr RouteError ) {
2018-05-27 09:36:35 +00:00
return & HeaderLite {
2017-11-11 04:06:16 +00:00
Site : Site ,
Settings : SettingBox . Load ( ) . ( SettingMap ) ,
2018-10-21 13:54:32 +00:00
Hooks : GetHookTable ( ) ,
2018-05-27 09:36:35 +00:00
} , nil
2017-09-10 17:05:13 +00:00
}
2019-04-29 12:23:17 +00:00
func GetThemeByReq ( r * http . Request ) * Theme {
2019-10-11 00:57:33 +00:00
theme := & Theme { Name : "" }
2017-09-10 17:05:13 +00:00
cookie , err := r . Cookie ( "current_theme" )
if err == nil {
2017-11-23 05:37:08 +00:00
inTheme , ok := Themes [ html . EscapeString ( cookie . Value ) ]
2017-09-10 17:05:13 +00:00
if ok && ! theme . HideFromThemes {
2017-11-23 05:37:08 +00:00
theme = inTheme
2017-09-10 17:05:13 +00:00
}
}
2017-11-23 05:37:08 +00:00
if theme . Name == "" {
theme = Themes [ DefaultThemeBox . Load ( ) . ( string ) ]
}
2017-09-10 17:05:13 +00:00
2019-04-18 01:00:39 +00:00
return theme
}
2020-02-29 01:10:39 +00:00
func userCheck ( w http . ResponseWriter , r * http . Request , user * User ) ( header * Header , rerr RouteError ) {
2020-03-18 09:21:34 +00:00
return userCheck2 ( w , r , user , uutils . Nanotime ( ) )
2020-02-29 01:10:39 +00:00
}
2019-04-18 01:00:39 +00:00
// TODO: Add the ability for admins to restrict certain themes to certain groups?
// ! Be careful about firing errors off here as CustomError uses this
2020-03-22 04:37:45 +00:00
func userCheck2 ( w http . ResponseWriter , r * http . Request , user * User , nano int64 ) ( h * Header , rerr RouteError ) {
2019-04-29 12:23:17 +00:00
theme := GetThemeByReq ( r )
2020-03-22 04:37:45 +00:00
h = & Header {
2019-02-28 07:34:56 +00:00
Site : Site ,
Settings : SettingBox . Load ( ) . ( SettingMap ) ,
Themes : Themes ,
Theme : theme ,
2020-03-18 09:21:34 +00:00
CurrentUser : user , // ! Some things rely on this being a pointer downstream from this function
2019-02-28 07:34:56 +00:00
Hooks : GetHookTable ( ) ,
Zone : "frontend" ,
Writer : w ,
2019-04-09 07:21:00 +00:00
IsoCode : phrases . GetLangPack ( ) . IsoCode ,
2020-03-18 09:21:34 +00:00
StartedAt : nano ,
2017-09-10 17:05:13 +00:00
}
2019-08-31 22:59:00 +00:00
// TODO: Optimise this by avoiding accessing a map string index
2020-03-22 04:37:45 +00:00
if ! user . Loggedin {
h . GoogSiteVerify = h . Settings [ "google_site_verify" ] . ( string )
}
2017-09-10 17:05:13 +00:00
if user . IsBanned {
2020-03-22 04:37:45 +00:00
h . AddNotice ( "account_banned" )
2017-09-10 17:05:13 +00:00
}
2018-03-08 03:59:47 +00:00
if user . Loggedin && ! user . Active {
2020-03-22 04:37:45 +00:00
h . AddNotice ( "account_inactive" )
2018-03-08 03:59:47 +00:00
}
2019-04-18 01:00:39 +00:00
2018-11-17 02:36:02 +00:00
// An optimisation so we don't populate StartedAt for users who shouldn't see the stat anyway
// ? - Should we only show this in debug mode? It might be useful for detecting issues in production, if we show it there as-well
2020-02-23 09:08:47 +00:00
//if user.IsAdmin {
2020-03-22 04:37:45 +00:00
//h.StartedAt = time.Now()
2020-02-23 09:08:47 +00:00
//}
2017-09-10 17:05:13 +00:00
2020-03-22 04:37:45 +00:00
//PrepResources(user,h,theme)
return h , nil
2019-04-18 01:00:39 +00:00
}
2020-03-22 04:37:45 +00:00
func PrepResources ( u * User , h * Header , theme * Theme ) {
2019-12-07 06:27:01 +00:00
h . AddSheet ( theme . Name + "/main.css" )
2019-04-18 01:00:39 +00:00
2017-11-23 05:37:08 +00:00
if len ( theme . Resources ) > 0 {
rlist := theme . Resources
2020-02-23 09:08:47 +00:00
for _ , res := range rlist {
2020-03-22 04:37:45 +00:00
if res . Loggedin && ! u . Loggedin {
2018-03-21 05:56:33 +00:00
continue
}
2020-02-23 09:08:47 +00:00
if res . Location == "global" || res . Location == "frontend" {
extarr := strings . Split ( res . Name , "." )
2017-10-12 03:24:14 +00:00
ext := extarr [ len ( extarr ) - 1 ]
if ext == "css" {
2020-02-23 09:08:47 +00:00
h . AddSheet ( res . Name )
2017-10-12 03:24:14 +00:00
} else if ext == "js" {
2020-02-23 09:08:47 +00:00
if res . Async {
h . AddScriptAsync ( res . Name )
2019-03-21 22:59:41 +00:00
} else {
2020-02-23 09:08:47 +00:00
h . AddScript ( res . Name )
2019-03-21 22:59:41 +00:00
}
2017-09-10 17:05:13 +00:00
}
}
}
}
2019-08-31 22:59:00 +00:00
addPreScript := func ( name string ) {
// TODO: Optimise this by removing a superfluous string alloc
2019-02-28 07:28:17 +00:00
var tname string
if theme . OverridenMap != nil {
//fmt.Printf("name %+v\n", name)
//fmt.Printf("theme.OverridenMap %+v\n", theme.OverridenMap)
2019-03-05 05:53:02 +00:00
_ , ok := theme . OverridenMap [ name ]
2019-02-28 07:28:17 +00:00
if ok {
tname = "_" + theme . Name
}
}
2019-03-05 05:53:02 +00:00
//fmt.Printf("tname %+v\n", tname)
2019-12-07 06:27:01 +00:00
h . AddPreScriptAsync ( "template_" + name + tname + ".js" )
2019-02-28 07:28:17 +00:00
}
addPreScript ( "topics_topic" )
addPreScript ( "paginator" )
addPreScript ( "alert" )
2019-04-27 06:32:26 +00:00
addPreScript ( "notice" )
2020-03-22 04:37:45 +00:00
if u . Loggedin {
2019-04-15 01:54:13 +00:00
addPreScript ( "topic_c_edit_post" )
addPreScript ( "topic_c_attach_item" )
2019-06-16 07:26:31 +00:00
addPreScript ( "topic_c_poll_input" )
2019-04-15 01:54:13 +00:00
}
2017-09-10 17:05:13 +00:00
}
func preRoute ( w http . ResponseWriter , r * http . Request ) ( User , bool ) {
2018-10-14 06:09:25 +00:00
userptr , halt := Auth . SessionCheck ( w , r )
2017-11-10 03:33:11 +00:00
if halt {
2018-10-14 06:09:25 +00:00
return * userptr , false
2017-11-10 03:33:11 +00:00
}
2017-11-11 05:22:33 +00:00
var usercpy * User = BlankUser ( )
2018-10-14 06:09:25 +00:00
* usercpy = * userptr
2018-10-14 06:16:42 +00:00
usercpy . Init ( ) // TODO: Can we reduce the amount of work we do here?
2017-11-10 03:33:11 +00:00
2018-11-29 07:27:17 +00:00
// TODO: Add a config setting to disable this header
// TODO: Have this header cover more things
2019-11-04 11:55:52 +00:00
if Config . SslSchema {
2018-11-29 07:27:17 +00:00
w . Header ( ) . Set ( "Content-Security-Policy" , "upgrade-insecure-requests" )
}
2017-11-10 00:16:15 +00:00
// TODO: WIP. Refactor this to eliminate the unnecessary query
2018-07-03 10:01:49 +00:00
// TODO: Better take proxies into consideration
2017-09-10 17:05:13 +00:00
host , _ , err := net . SplitHostPort ( r . RemoteAddr )
if err != nil {
PreError ( "Bad IP" , w , r )
2017-11-11 04:06:16 +00:00
return * usercpy , false
2017-09-10 17:05:13 +00:00
}
2018-07-03 10:01:49 +00:00
// TODO: Prefer Cf-Connecting-Ip header, fewer shenanigans
if Site . HasProxy {
// TODO: Check the right-most IP, might get tricky with multiple proxies, maybe have a setting for the number of hops we jump through
xForwardedFor := r . Header . Get ( "X-Forwarded-For" )
if xForwardedFor != "" {
forwardedFor := strings . Split ( xForwardedFor , "," )
// TODO: Check if this is a valid IP Address, reject if not
host = forwardedFor [ len ( forwardedFor ) - 1 ]
}
}
2020-01-02 06:04:10 +00:00
if ! Config . DisableLastIP && usercpy . Loggedin && host != usercpy . GetIP ( ) {
2019-12-31 21:57:54 +00:00
mon := time . Now ( ) . Month ( )
err = usercpy . UpdateIP ( strconv . Itoa ( int ( mon ) ) + "-" + host )
2017-09-10 17:05:13 +00:00
if err != nil {
2017-10-30 09:57:08 +00:00
InternalError ( err , w , r )
2017-11-11 04:06:16 +00:00
return * usercpy , false
2017-09-10 17:05:13 +00:00
}
}
2019-12-31 21:57:54 +00:00
usercpy . LastIP = host
2017-11-10 03:33:11 +00:00
2017-11-11 04:06:16 +00:00
return * usercpy , true
2017-09-10 17:05:13 +00:00
}
2017-10-30 09:57:08 +00:00
2020-03-18 09:21:34 +00:00
func UploadAvatar ( w http . ResponseWriter , r * http . Request , user * User , tuid int ) ( ext string , ferr RouteError ) {
2019-06-10 22:00:57 +00:00
// We don't want multiple files
// TODO: Are we doing this correctly?
filenameMap := make ( map [ string ] bool )
for _ , fheaders := range r . MultipartForm . File {
for _ , hdr := range fheaders {
if hdr . Filename == "" {
continue
}
filenameMap [ hdr . Filename ] = true
}
}
if len ( filenameMap ) > 1 {
return "" , LocalError ( "You may only upload one avatar" , w , r , user )
}
for _ , fheaders := range r . MultipartForm . File {
for _ , hdr := range fheaders {
if hdr . Filename == "" {
continue
}
2019-12-31 21:57:54 +00:00
inFile , err := hdr . Open ( )
2019-06-10 22:00:57 +00:00
if err != nil {
return "" , LocalError ( "Upload failed" , w , r , user )
}
2019-12-31 21:57:54 +00:00
defer inFile . Close ( )
2019-06-10 22:00:57 +00:00
if ext == "" {
extarr := strings . Split ( hdr . Filename , "." )
if len ( extarr ) < 2 {
return "" , LocalError ( "Bad file" , w , r , user )
}
ext = extarr [ len ( extarr ) - 1 ]
// TODO: Can we do this without a regex?
reg , err := regexp . Compile ( "[^A-Za-z0-9]+" )
if err != nil {
return "" , LocalError ( "Bad file extension" , w , r , user )
}
ext = reg . ReplaceAllString ( ext , "" )
ext = strings . ToLower ( ext )
if ! ImageFileExts . Contains ( ext ) {
return "" , LocalError ( "You can only use an image for your avatar" , w , r , user )
}
}
// TODO: Centralise this string, so we don't have to change it in two different places when it changes
2019-12-31 21:57:54 +00:00
outFile , err := os . Create ( "./uploads/avatar_" + strconv . Itoa ( tuid ) + "." + ext )
2019-06-10 22:00:57 +00:00
if err != nil {
return "" , LocalError ( "Upload failed [File Creation Failed]" , w , r , user )
}
2019-12-31 21:57:54 +00:00
defer outFile . Close ( )
2019-06-10 22:00:57 +00:00
2019-12-31 21:57:54 +00:00
_ , err = io . Copy ( outFile , inFile )
2019-06-10 22:00:57 +00:00
if err != nil {
return "" , LocalError ( "Upload failed [Copy Failed]" , w , r , user )
}
}
}
if ext == "" {
return "" , LocalError ( "No file" , w , r , user )
}
return ext , nil
}
2020-03-18 09:21:34 +00:00
func ChangeAvatar ( path string , w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2019-06-10 22:00:57 +00:00
err := user . ChangeAvatar ( path )
if err != nil {
return InternalError ( err , w , r )
}
// Clean up the old avatar data, so we don't end up with too many dead files in /uploads/
if len ( user . RawAvatar ) > 2 {
if user . RawAvatar [ 0 ] == '.' && user . RawAvatar [ 1 ] == '.' {
err := os . Remove ( "./uploads/avatar_" + strconv . Itoa ( user . ID ) + "_tmp" + user . RawAvatar [ 1 : ] )
if err != nil && ! os . IsNotExist ( err ) {
LogWarning ( err )
return LocalError ( "Something went wrong" , w , r , user )
}
err = os . Remove ( "./uploads/avatar_" + strconv . Itoa ( user . ID ) + "_w48" + user . RawAvatar [ 1 : ] )
if err != nil && ! os . IsNotExist ( err ) {
LogWarning ( err )
return LocalError ( "Something went wrong" , w , r , user )
}
}
}
return nil
}
2017-12-19 03:53:13 +00:00
// SuperAdminOnly makes sure that only super admin can access certain critical panel routes
2020-03-18 09:21:34 +00:00
func SuperAdminOnly ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2017-12-19 03:53:13 +00:00
if ! user . IsSuperAdmin {
return NoPermissions ( w , r , user )
}
return nil
}
2017-11-11 23:34:27 +00:00
// AdminOnly makes sure that only admins can access certain panel routes
2020-03-18 09:21:34 +00:00
func AdminOnly ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2017-11-11 23:34:27 +00:00
if ! user . IsAdmin {
return NoPermissions ( w , r , user )
}
return nil
}
2017-10-30 09:57:08 +00:00
// SuperModeOnly makes sure that only super mods or higher can access the panel routes
2020-03-18 09:21:34 +00:00
func SuperModOnly ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2017-10-30 09:57:08 +00:00
if ! user . IsSuperMod {
return NoPermissions ( w , r , user )
}
return nil
}
2017-11-05 09:55:34 +00:00
// MemberOnly makes sure that only logged in users can access this route
2020-03-18 09:21:34 +00:00
func MemberOnly ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2017-11-05 09:55:34 +00:00
if ! user . Loggedin {
2017-11-08 07:28:33 +00:00
return LoginRequired ( w , r , user )
}
return nil
}
// NoBanned stops any banned users from accessing this route
2020-03-18 09:21:34 +00:00
func NoBanned ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2017-11-08 07:28:33 +00:00
if user . IsBanned {
return Banned ( w , r , user )
}
return nil
}
2020-03-18 09:21:34 +00:00
func ParseForm ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2019-10-11 00:57:33 +00:00
if err := r . ParseForm ( ) ; err != nil {
2017-11-08 07:28:33 +00:00
return LocalError ( "Bad Form" , w , r , user )
}
return nil
}
2020-03-18 09:21:34 +00:00
func NoSessionMismatch ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2019-10-11 00:57:33 +00:00
if err := r . ParseForm ( ) ; err != nil {
2017-11-08 07:28:33 +00:00
return LocalError ( "Bad Form" , w , r , user )
}
2019-08-31 22:59:00 +00:00
// TODO: Try to eliminate some of these allocations
2019-08-30 10:59:50 +00:00
sess := [ ] byte ( user . Session )
if len ( sess ) == 0 {
return SecurityError ( w , r , user )
}
if subtle . ConstantTimeCompare ( [ ] byte ( r . FormValue ( "session" ) ) , sess ) != 1 && subtle . ConstantTimeCompare ( [ ] byte ( r . FormValue ( "s" ) ) , sess ) != 1 {
2017-11-08 07:28:33 +00:00
return SecurityError ( w , r , user )
2017-11-05 09:55:34 +00:00
}
return nil
}
2017-11-23 05:37:08 +00:00
func ReqIsJson ( r * http . Request ) bool {
return r . Header . Get ( "Content-type" ) == "application/json"
}
2018-01-14 12:03:20 +00:00
2020-03-18 09:21:34 +00:00
func HandleUploadRoute ( w http . ResponseWriter , r * http . Request , user * User , maxFileSize int ) RouteError {
2018-01-14 12:03:20 +00:00
// TODO: Reuse this code more
if r . ContentLength > int64 ( maxFileSize ) {
size , unit := ConvertByteUnit ( float64 ( maxFileSize ) )
2018-02-19 04:26:01 +00:00
return CustomError ( "Your upload is too big. Your files need to be smaller than " + strconv . Itoa ( int ( size ) ) + unit + "." , http . StatusExpectationFailed , "Error" , w , r , nil , user )
2018-01-14 12:03:20 +00:00
}
2018-12-27 05:42:41 +00:00
r . Body = http . MaxBytesReader ( w , r . Body , r . ContentLength )
2018-01-14 12:03:20 +00:00
err := r . ParseMultipartForm ( int64 ( Megabyte ) )
if err != nil {
return LocalError ( "Bad Form" , w , r , user )
}
return nil
}
2020-03-18 09:21:34 +00:00
func NoUploadSessionMismatch ( w http . ResponseWriter , r * http . Request , user * User ) RouteError {
2019-08-31 22:59:00 +00:00
// TODO: Try to eliminate some of these allocations
2019-08-30 10:59:50 +00:00
sess := [ ] byte ( user . Session )
if len ( sess ) == 0 {
return SecurityError ( w , r , user )
}
if subtle . ConstantTimeCompare ( [ ] byte ( r . FormValue ( "session" ) ) , sess ) != 1 && subtle . ConstantTimeCompare ( [ ] byte ( r . FormValue ( "s" ) ) , sess ) != 1 {
2018-01-14 12:03:20 +00:00
return SecurityError ( w , r , user )
}
return nil
}