2017-06-13 07:12:58 +00:00
package main
2017-08-06 15:22:18 +00:00
import (
2017-09-03 04:50:31 +00:00
"database/sql"
2017-08-06 15:22:18 +00:00
"errors"
2017-09-03 04:50:31 +00:00
"log"
2017-08-06 15:22:18 +00:00
"strconv"
2017-09-03 04:50:31 +00:00
"strings"
"sync"
2017-08-06 15:22:18 +00:00
"./query_gen/lib"
"golang.org/x/crypto/bcrypt"
)
2017-06-13 07:12:58 +00:00
2017-09-10 16:57:22 +00:00
// TODO: Add the watchdog goroutine
2017-06-13 07:12:58 +00:00
var users UserStore
2017-09-03 04:50:31 +00:00
var errAccountExists = errors . New ( "this username is already in use" )
2017-06-13 07:12:58 +00:00
type UserStore interface {
Load ( id int ) error
Get ( id int ) ( * User , error )
GetUnsafe ( id int ) ( * User , error )
CascadeGet ( id int ) ( * User , error )
2017-08-06 15:22:18 +00:00
//BulkCascadeGet(ids []int) ([]*User, error)
BulkCascadeGetMap ( ids [ ] int ) ( map [ int ] * User , error )
2017-06-13 07:12:58 +00:00
BypassGet ( id int ) ( * User , error )
Set ( item * User ) error
Add ( item * User ) error
AddUnsafe ( item * User ) error
Remove ( id int ) error
RemoveUnsafe ( id int ) error
2017-06-25 09:56:39 +00:00
CreateUser ( username string , password string , email string , group int , active int ) ( int , error )
2017-06-13 07:12:58 +00:00
GetLength ( ) int
GetCapacity ( ) int
2017-08-15 13:47:56 +00:00
GetGlobalCount ( ) int
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
type MemoryUserStore struct {
2017-09-10 16:57:22 +00:00
items map [ int ] * User
length int
capacity int
get * sql . Stmt
register * sql . Stmt
usernameExists * sql . Stmt
userCount * sql . Stmt
2017-06-13 07:12:58 +00:00
sync . RWMutex
}
2017-09-13 15:09:13 +00:00
// NewMemoryUserStore gives you a new instance of MemoryUserStore
2017-06-15 11:40:35 +00:00
func NewMemoryUserStore ( capacity int ) * MemoryUserStore {
2017-09-10 16:57:22 +00:00
getStmt , err := qgen . Builder . SimpleSelect ( "users" , "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group" , "uid = ?" , "" , "" )
2017-06-13 08:56:48 +00:00
if err != nil {
log . Fatal ( err )
}
2017-06-25 09:56:39 +00:00
// Add an admin version of register_stmt with more flexibility?
// create_account_stmt, err = db.Prepare("INSERT INTO
2017-09-10 16:57:22 +00:00
registerStmt , err := qgen . Builder . SimpleInsert ( "users" , "name, email, password, salt, group, is_super_admin, session, active, message" , "?,?,?,?,?,0,'',?,''" )
2017-06-25 09:56:39 +00:00
if err != nil {
log . Fatal ( err )
}
2017-09-10 16:57:22 +00:00
usernameExistsStmt , err := qgen . Builder . SimpleSelect ( "users" , "name" , "name = ?" , "" , "" )
2017-06-25 09:56:39 +00:00
if err != nil {
log . Fatal ( err )
}
2017-09-10 16:57:22 +00:00
userCountStmt , err := qgen . Builder . SimpleCount ( "users" , "" , "" )
2017-08-15 13:47:56 +00:00
if err != nil {
log . Fatal ( err )
}
2017-06-15 11:40:35 +00:00
return & MemoryUserStore {
2017-09-10 16:57:22 +00:00
items : make ( map [ int ] * User ) ,
capacity : capacity ,
get : getStmt ,
register : registerStmt ,
usernameExists : usernameExistsStmt ,
userCount : userCountStmt ,
2017-06-13 08:56:48 +00:00
}
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) Get ( id int ) ( * User , error ) {
2017-06-13 08:56:48 +00:00
sus . RLock ( )
item , ok := sus . items [ id ]
sus . RUnlock ( )
2017-06-13 07:12:58 +00:00
if ok {
return item , nil
}
2017-06-28 12:05:26 +00:00
return item , ErrNoRows
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) GetUnsafe ( id int ) ( * User , error ) {
2017-06-13 08:56:48 +00:00
item , ok := sus . items [ id ]
2017-06-13 07:12:58 +00:00
if ok {
return item , nil
}
2017-06-28 12:05:26 +00:00
return item , ErrNoRows
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) CascadeGet ( id int ) ( * User , error ) {
2017-06-13 08:56:48 +00:00
sus . RLock ( )
user , ok := sus . items [ id ]
sus . RUnlock ( )
2017-06-13 07:12:58 +00:00
if ok {
return user , nil
}
2017-09-03 04:50:31 +00:00
user = & User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( user )
2017-06-13 07:12:58 +00:00
if err == nil {
2017-06-13 08:56:48 +00:00
sus . Set ( user )
2017-06-13 07:12:58 +00:00
}
return user , err
}
2017-08-06 15:22:18 +00:00
// WARNING: We did a little hack to make this as thin and quick as possible to reduce lock contention, use the * Cascade* methods instead for normal use
func ( sus * MemoryUserStore ) bulkGet ( ids [ ] int ) ( list [ ] * User ) {
2017-09-03 04:50:31 +00:00
list = make ( [ ] * User , len ( ids ) )
2017-08-06 15:22:18 +00:00
sus . RLock ( )
for i , id := range ids {
list [ i ] = sus . items [ id ]
}
sus . RUnlock ( )
return list
}
2017-09-10 16:57:22 +00:00
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
// TODO: ID of 0 should always error?
2017-08-06 15:22:18 +00:00
func ( sus * MemoryUserStore ) BulkCascadeGetMap ( ids [ ] int ) ( list map [ int ] * User , err error ) {
2017-09-10 16:57:22 +00:00
var idCount = len ( ids )
2017-08-06 15:22:18 +00:00
list = make ( map [ int ] * User )
2017-09-03 04:50:31 +00:00
if idCount == 0 {
2017-08-06 15:22:18 +00:00
return list , nil
}
2017-09-03 04:50:31 +00:00
var stillHere [ ] int
sliceList := sus . bulkGet ( ids )
for i , sliceItem := range sliceList {
if sliceItem != nil {
list [ sliceItem . ID ] = sliceItem
2017-08-06 15:22:18 +00:00
} else {
2017-09-03 04:50:31 +00:00
stillHere = append ( stillHere , ids [ i ] )
2017-08-06 15:22:18 +00:00
}
}
2017-09-03 04:50:31 +00:00
ids = stillHere
2017-08-06 15:22:18 +00:00
// If every user is in the cache, then return immediately
if len ( ids ) == 0 {
return list , nil
}
var qlist string
var uidList [ ] interface { }
for _ , id := range ids {
2017-09-03 04:50:31 +00:00
uidList = append ( uidList , strconv . Itoa ( id ) )
2017-08-06 15:22:18 +00:00
qlist += "?,"
}
2017-09-03 04:50:31 +00:00
qlist = qlist [ 0 : len ( qlist ) - 1 ]
2017-08-06 15:22:18 +00:00
2017-09-03 04:50:31 +00:00
stmt , err := qgen . Builder . SimpleSelect ( "users" , "uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group" , "uid IN(" + qlist + ")" , "" , "" )
2017-08-06 15:22:18 +00:00
if err != nil {
return nil , err
}
rows , err := stmt . Query ( uidList ... )
if err != nil {
return nil , err
}
for rows . Next ( ) {
2017-09-03 04:50:31 +00:00
user := & User { Loggedin : true }
err := rows . Scan ( & user . ID , & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-08-06 15:22:18 +00:00
if err != nil {
return nil , err
}
// Initialise the user
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-08-06 15:22:18 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , user . ID )
2017-08-06 15:22:18 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( user )
2017-08-06 15:22:18 +00:00
// Add it to the cache...
2017-09-03 04:50:31 +00:00
_ = sus . Set ( user )
2017-08-06 15:22:18 +00:00
// Add it to the list to be returned
list [ user . ID ] = user
}
// Did we miss any users?
2017-09-03 04:50:31 +00:00
if idCount > len ( list ) {
var sidList string
2017-08-06 15:22:18 +00:00
for _ , id := range ids {
_ , ok := list [ id ]
if ! ok {
2017-09-03 04:50:31 +00:00
sidList += strconv . Itoa ( id ) + ","
2017-08-06 15:22:18 +00:00
}
}
// We probably don't need this, but it might be useful in case of bugs in BulkCascadeGetMap
2017-09-03 04:50:31 +00:00
if sidList == "" {
2017-08-06 15:22:18 +00:00
if dev . DebugMode {
2017-08-13 11:22:34 +00:00
log . Print ( "This data is sampled later in the BulkCascadeGetMap function, so it might miss the cached IDs" )
2017-09-03 04:50:31 +00:00
log . Print ( "idCount" , idCount )
log . Print ( "ids" , ids )
log . Print ( "list" , list )
2017-08-06 15:22:18 +00:00
}
return list , errors . New ( "We weren't able to find a user, but we don't know which one" )
}
2017-09-03 04:50:31 +00:00
sidList = sidList [ 0 : len ( sidList ) - 1 ]
2017-08-06 15:22:18 +00:00
2017-09-03 04:50:31 +00:00
return list , errors . New ( "Unable to find the users with the following IDs: " + sidList )
2017-08-06 15:22:18 +00:00
}
return list , nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) BypassGet ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := & User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( user )
2017-06-13 07:12:58 +00:00
return user , err
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) Load ( id int ) error {
2017-09-03 04:50:31 +00:00
user := & User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if err != nil {
2017-06-13 08:56:48 +00:00
sus . Remove ( id )
2017-06-13 07:12:58 +00:00
return err
}
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( user )
_ = sus . Set ( user )
2017-06-13 07:12:58 +00:00
return nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) Set ( item * User ) error {
2017-06-13 08:56:48 +00:00
sus . Lock ( )
user , ok := sus . items [ item . ID ]
2017-06-13 07:12:58 +00:00
if ok {
2017-06-13 08:56:48 +00:00
sus . Unlock ( )
2017-06-13 07:12:58 +00:00
* user = * item
2017-06-13 08:56:48 +00:00
} else if sus . length >= sus . capacity {
sus . Unlock ( )
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
} else {
2017-06-13 08:56:48 +00:00
sus . items [ item . ID ] = item
sus . Unlock ( )
sus . length ++
2017-06-13 07:12:58 +00:00
}
return nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) Add ( item * User ) error {
2017-06-13 08:56:48 +00:00
if sus . length >= sus . capacity {
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
}
2017-06-13 08:56:48 +00:00
sus . Lock ( )
sus . items [ item . ID ] = item
sus . Unlock ( )
sus . length ++
2017-06-13 07:12:58 +00:00
return nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) AddUnsafe ( item * User ) error {
2017-06-13 08:56:48 +00:00
if sus . length >= sus . capacity {
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
}
2017-06-13 08:56:48 +00:00
sus . items [ item . ID ] = item
sus . length ++
2017-06-13 07:12:58 +00:00
return nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) Remove ( id int ) error {
2017-06-13 08:56:48 +00:00
sus . Lock ( )
2017-09-03 04:50:31 +00:00
delete ( sus . items , id )
2017-06-13 08:56:48 +00:00
sus . Unlock ( )
sus . length --
2017-06-13 07:12:58 +00:00
return nil
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) RemoveUnsafe ( id int ) error {
2017-09-03 04:50:31 +00:00
delete ( sus . items , id )
2017-06-13 08:56:48 +00:00
sus . length --
2017-06-13 07:12:58 +00:00
return nil
}
2017-06-25 09:56:39 +00:00
func ( sus * MemoryUserStore ) CreateUser ( username string , password string , email string , group int , active int ) ( int , error ) {
// Is this username already taken..?
2017-09-10 16:57:22 +00:00
err := sus . usernameExists . QueryRow ( username ) . Scan ( & username )
2017-06-28 12:05:26 +00:00
if err != ErrNoRows {
2017-09-03 04:50:31 +00:00
return 0 , errAccountExists
2017-06-25 09:56:39 +00:00
}
salt , err := GenerateSafeString ( saltLength )
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
hashedPassword , err := bcrypt . GenerateFromPassword ( [ ] byte ( password + salt ) , bcrypt . DefaultCost )
2017-06-25 09:56:39 +00:00
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
res , err := sus . register . Exec ( username , email , string ( hashedPassword ) , salt , group , active )
2017-06-25 09:56:39 +00:00
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
lastID , err := res . LastInsertId ( )
return int ( lastID ) , err
2017-06-25 09:56:39 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) GetLength ( ) int {
2017-06-13 08:56:48 +00:00
return sus . length
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) SetCapacity ( capacity int ) {
2017-06-13 08:56:48 +00:00
sus . capacity = capacity
2017-06-13 07:12:58 +00:00
}
2017-06-15 11:40:35 +00:00
func ( sus * MemoryUserStore ) GetCapacity ( ) int {
2017-06-13 08:56:48 +00:00
return sus . capacity
2017-06-13 07:12:58 +00:00
}
2017-08-15 13:47:56 +00:00
// Return the total number of users registered on the forums
func ( sus * MemoryUserStore ) GetGlobalCount ( ) int {
var ucount int
2017-09-10 16:57:22 +00:00
err := sus . userCount . QueryRow ( ) . Scan ( & ucount )
2017-08-15 13:47:56 +00:00
if err != nil {
LogError ( err )
}
return ucount
}
2017-09-10 16:57:22 +00:00
type SQLUserStore struct {
2017-09-03 04:50:31 +00:00
get * sql . Stmt
register * sql . Stmt
usernameExists * sql . Stmt
userCount * sql . Stmt
2017-06-13 07:12:58 +00:00
}
2017-09-10 16:57:22 +00:00
func NewSQLUserStore ( ) * SQLUserStore {
2017-09-03 04:50:31 +00:00
getStmt , err := qgen . Builder . SimpleSelect ( "users" , "name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group" , "uid = ?" , "" , "" )
2017-06-13 08:56:48 +00:00
if err != nil {
log . Fatal ( err )
}
2017-06-25 09:56:39 +00:00
// Add an admin version of register_stmt with more flexibility?
// create_account_stmt, err = db.Prepare("INSERT INTO
2017-09-03 04:50:31 +00:00
registerStmt , err := qgen . Builder . SimpleInsert ( "users" , "name, email, password, salt, group, is_super_admin, session, active, message" , "?,?,?,?,?,0,'',?,''" )
2017-06-25 09:56:39 +00:00
if err != nil {
log . Fatal ( err )
}
2017-09-03 04:50:31 +00:00
usernameExistsStmt , err := qgen . Builder . SimpleSelect ( "users" , "name" , "name = ?" , "" , "" )
2017-06-25 09:56:39 +00:00
if err != nil {
log . Fatal ( err )
}
2017-09-03 04:50:31 +00:00
userCountStmt , err := qgen . Builder . SimpleCount ( "users" , "" , "" )
2017-08-15 13:47:56 +00:00
if err != nil {
log . Fatal ( err )
}
2017-09-10 16:57:22 +00:00
return & SQLUserStore {
2017-09-03 04:50:31 +00:00
get : getStmt ,
register : registerStmt ,
usernameExists : usernameExistsStmt ,
userCount : userCountStmt ,
2017-06-25 09:56:39 +00:00
}
2017-06-13 07:12:58 +00:00
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) Get ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( & user )
2017-06-13 07:12:58 +00:00
return & user , err
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) GetUnsafe ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( & user )
2017-06-13 07:12:58 +00:00
return & user , err
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) CascadeGet ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( & user )
2017-06-13 07:12:58 +00:00
return & user , err
}
2017-09-10 16:57:22 +00:00
// TODO: Optimise the query to avoid preparing it on the spot? Maybe, use knowledge of the most common IN() parameter counts?
func ( sus * SQLUserStore ) BulkCascadeGetMap ( ids [ ] int ) ( list map [ int ] * User , err error ) {
2017-08-06 15:22:18 +00:00
var qlist string
var uidList [ ] interface { }
for _ , id := range ids {
2017-09-03 04:50:31 +00:00
uidList = append ( uidList , strconv . Itoa ( id ) )
2017-08-06 15:22:18 +00:00
qlist += "?,"
}
2017-09-03 04:50:31 +00:00
qlist = qlist [ 0 : len ( qlist ) - 1 ]
2017-08-06 15:22:18 +00:00
2017-09-03 04:50:31 +00:00
stmt , err := qgen . Builder . SimpleSelect ( "users" , "uid, name, group, is_super_admin, session, email, avatar, message, url_prefix, url_name, level, score, last_ip, temp_group" , "uid IN(" + qlist + ")" , "" , "" )
2017-08-06 15:22:18 +00:00
if err != nil {
return nil , err
}
rows , err := stmt . Query ( uidList ... )
if err != nil {
return nil , err
}
list = make ( map [ int ] * User )
for rows . Next ( ) {
2017-09-03 04:50:31 +00:00
user := & User { Loggedin : true }
err := rows . Scan ( & user . ID , & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-08-06 15:22:18 +00:00
if err != nil {
return nil , err
}
// Initialise the user
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-08-06 15:22:18 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , user . ID )
2017-08-06 15:22:18 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( user )
2017-08-06 15:22:18 +00:00
// Add it to the list to be returned
list [ user . ID ] = user
}
return list , nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) BypassGet ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := User { ID : id , Loggedin : true }
err := sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
if user . Avatar != "" {
if user . Avatar [ 0 ] == '.' {
user . Avatar = "/uploads/avatar_" + strconv . Itoa ( user . ID ) + user . Avatar
}
} else {
2017-09-03 04:50:31 +00:00
user . Avatar = strings . Replace ( config . Noavatar , "{id}" , strconv . Itoa ( user . ID ) , 1 )
2017-06-13 07:12:58 +00:00
}
2017-09-03 04:50:31 +00:00
user . Link = buildProfileURL ( nameToSlug ( user . Name ) , id )
2017-06-13 07:12:58 +00:00
user . Tag = groups [ user . Group ] . Tag
2017-09-03 04:50:31 +00:00
initUserPerms ( & user )
2017-06-13 07:12:58 +00:00
return & user , err
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) Load ( id int ) error {
2017-09-03 04:50:31 +00:00
user := & User { ID : id }
2017-08-27 09:33:45 +00:00
// Simplify this into a quick check to see whether the user exists. Add an Exists method to facilitate this?
2017-09-03 04:50:31 +00:00
return sus . get . QueryRow ( id ) . Scan ( & user . Name , & user . Group , & user . IsSuperAdmin , & user . Session , & user . Email , & user . Avatar , & user . Message , & user . URLPrefix , & user . URLName , & user . Level , & user . Score , & user . LastIP , & user . TempGroup )
2017-06-13 07:12:58 +00:00
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) CreateUser ( username string , password string , email string , group int , active int ) ( int , error ) {
2017-06-25 09:56:39 +00:00
// Is this username already taken..?
2017-09-03 04:50:31 +00:00
err := sus . usernameExists . QueryRow ( username ) . Scan ( & username )
2017-06-28 12:05:26 +00:00
if err != ErrNoRows {
2017-09-03 04:50:31 +00:00
return 0 , errAccountExists
2017-06-25 09:56:39 +00:00
}
salt , err := GenerateSafeString ( saltLength )
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
hashedPassword , err := bcrypt . GenerateFromPassword ( [ ] byte ( password + salt ) , bcrypt . DefaultCost )
2017-06-25 09:56:39 +00:00
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
res , err := sus . register . Exec ( username , email , string ( hashedPassword ) , salt , group , active )
2017-06-25 09:56:39 +00:00
if err != nil {
return 0 , err
}
2017-09-03 04:50:31 +00:00
lastID , err := res . LastInsertId ( )
return int ( lastID ) , err
2017-06-25 09:56:39 +00:00
}
2017-06-28 12:05:26 +00:00
// Placeholder methods, as we're not don't need to do any cache management with this implementation ofr the UserStore
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) Set ( item * User ) error {
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) Add ( item * User ) error {
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) AddUnsafe ( item * User ) error {
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) Remove ( id int ) error {
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) RemoveUnsafe ( id int ) error {
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) GetCapacity ( ) int {
2017-06-13 07:12:58 +00:00
return 0
}
2017-08-15 13:47:56 +00:00
// Return the total number of users registered on the forums
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) GetLength ( ) int {
2017-08-15 13:47:56 +00:00
var ucount int
2017-09-03 04:50:31 +00:00
err := sus . userCount . QueryRow ( ) . Scan ( & ucount )
2017-08-15 13:47:56 +00:00
if err != nil {
LogError ( err )
}
return ucount
}
2017-09-10 16:57:22 +00:00
func ( sus * SQLUserStore ) GetGlobalCount ( ) int {
2017-08-15 13:47:56 +00:00
var ucount int
2017-09-03 04:50:31 +00:00
err := sus . userCount . QueryRow ( ) . Scan ( & ucount )
2017-08-15 13:47:56 +00:00
if err != nil {
LogError ( err )
}
return ucount
2017-06-13 07:12:58 +00:00
}