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-09-18 17:03:52 +00:00
"sync/atomic"
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-09-15 22:20:01 +00:00
// TODO: Add some sort of update method
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 {
Get ( id int ) ( * User , error )
2017-09-15 22:20:01 +00:00
Exists ( id int ) bool
//BulkGet(ids []int) ([]*User, error)
BulkGetMap ( ids [ ] int ) ( map [ int ] * User , error )
2017-06-13 07:12:58 +00:00
BypassGet ( id int ) ( * User , error )
2017-09-15 22:20:01 +00:00
Create ( username string , password string , email string , group int , active int ) ( int , error )
GetGlobalCount ( ) int
}
type UserCache interface {
CacheGet ( id int ) ( * User , error )
CacheGetUnsafe ( id int ) ( * User , error )
CacheSet ( item * User ) error
CacheAdd ( item * User ) error
CacheAddUnsafe ( item * User ) error
CacheRemove ( id int ) error
CacheRemoveUnsafe ( id int ) error
2017-09-18 17:03:52 +00:00
Flush ( )
2017-09-22 02:21:17 +00:00
Reload ( id int ) error
2017-06-13 07:12:58 +00:00
GetLength ( ) int
2017-09-15 22:20:01 +00:00
SetCapacity ( capacity int )
2017-06-13 07:12:58 +00:00
GetCapacity ( ) int
}
2017-06-15 11:40:35 +00:00
type MemoryUserStore struct {
2017-09-10 16:57:22 +00:00
items map [ int ] * User
2017-09-18 17:03:52 +00:00
length int64
2017-09-10 16:57:22 +00:00
capacity int
get * sql . Stmt
2017-09-15 22:20:01 +00:00
exists * sql . Stmt
2017-09-10 16:57:22 +00:00
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
2017-09-15 22:20:01 +00:00
existsStmt , err := qgen . Builder . SimpleSelect ( "users" , "uid" , "uid = ?" , "" , "" )
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 ,
2017-09-15 22:20:01 +00:00
exists : existsStmt ,
2017-09-10 16:57:22 +00:00
register : registerStmt ,
usernameExists : usernameExistsStmt ,
userCount : userCountStmt ,
2017-06-13 08:56:48 +00:00
}
2017-06-13 07:12:58 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheGet ( id int ) ( * User , error ) {
mus . RLock ( )
item , ok := mus . items [ id ]
mus . 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-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheGetUnsafe ( id int ) ( * User , error ) {
item , ok := mus . 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-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) Get ( id int ) ( * User , error ) {
mus . RLock ( )
user , ok := mus . items [ id ]
mus . 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 }
2017-09-18 17:03:52 +00:00
err := mus . 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
2017-06-13 07:12:58 +00:00
if err == nil {
2017-09-18 17:03:52 +00:00
mus . CacheSet ( 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
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) bulkGet ( ids [ ] int ) ( list [ ] * User ) {
2017-09-03 04:50:31 +00:00
list = make ( [ ] * User , len ( ids ) )
2017-09-18 17:03:52 +00:00
mus . RLock ( )
2017-08-06 15:22:18 +00:00
for i , id := range ids {
2017-09-18 17:03:52 +00:00
list [ i ] = mus . items [ id ]
2017-08-06 15:22:18 +00:00
}
2017-09-18 17:03:52 +00:00
mus . RUnlock ( )
2017-08-06 15:22:18 +00:00
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-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) BulkGetMap ( 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
2017-09-18 17:03:52 +00:00
sliceList := mus . bulkGet ( ids )
2017-09-03 04:50:31 +00:00
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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
2017-08-06 15:22:18 +00:00
// Add it to the cache...
2017-09-18 17:03:52 +00:00
_ = mus . CacheSet ( 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-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) BypassGet ( id int ) ( * User , error ) {
2017-09-03 04:50:31 +00:00
user := & User { ID : id , Loggedin : true }
2017-09-18 17:03:52 +00:00
err := mus . 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
2017-06-13 07:12:58 +00:00
return user , err
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) Reload ( id int ) error {
2017-09-03 04:50:31 +00:00
user := & User { ID : id , Loggedin : true }
2017-09-18 17:03:52 +00:00
err := mus . 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-09-18 17:03:52 +00:00
mus . CacheRemove ( 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
2017-09-18 17:03:52 +00:00
_ = mus . CacheSet ( user )
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) Exists ( id int ) bool {
return mus . exists . QueryRow ( id ) . Scan ( & id ) == nil
2017-09-15 22:20:01 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheSet ( item * User ) error {
mus . Lock ( )
user , ok := mus . items [ item . ID ]
2017-06-13 07:12:58 +00:00
if ok {
2017-09-18 17:03:52 +00:00
mus . Unlock ( )
2017-06-13 07:12:58 +00:00
* user = * item
2017-09-18 17:03:52 +00:00
} else if int ( mus . length ) >= mus . capacity {
mus . Unlock ( )
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
} else {
2017-09-18 17:03:52 +00:00
mus . items [ item . ID ] = item
mus . Unlock ( )
atomic . AddInt64 ( & mus . length , 1 )
2017-06-13 07:12:58 +00:00
}
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheAdd ( item * User ) error {
if int ( mus . length ) >= mus . capacity {
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
}
2017-09-18 17:03:52 +00:00
mus . Lock ( )
mus . items [ item . ID ] = item
mus . Unlock ( )
atomic . AddInt64 ( & mus . length , 1 )
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheAddUnsafe ( item * User ) error {
if int ( mus . length ) >= mus . capacity {
2017-06-13 07:12:58 +00:00
return ErrStoreCapacityOverflow
}
2017-09-18 17:03:52 +00:00
mus . items [ item . ID ] = item
atomic . AddInt64 ( & mus . length , 1 )
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheRemove ( id int ) error {
mus . Lock ( )
delete ( mus . items , id )
mus . Unlock ( )
atomic . AddInt64 ( & mus . length , - 1 )
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) CacheRemoveUnsafe ( id int ) error {
delete ( mus . items , id )
atomic . AddInt64 ( & mus . length , - 1 )
2017-06-13 07:12:58 +00:00
return nil
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) Create ( 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-18 17:03:52 +00:00
err := mus . 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-18 17:03:52 +00:00
res , err := mus . 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-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) Flush ( ) {
mus . Lock ( )
mus . items = make ( map [ int ] * User )
mus . length = 0
mus . Unlock ( )
2017-06-13 07:12:58 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) GetLength ( ) int {
return int ( mus . length )
2017-06-13 07:12:58 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) SetCapacity ( capacity int ) {
mus . capacity = capacity
}
func ( mus * MemoryUserStore ) GetCapacity ( ) int {
return mus . 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
2017-09-18 17:03:52 +00:00
func ( mus * MemoryUserStore ) GetGlobalCount ( ) int {
2017-08-15 13:47:56 +00:00
var ucount int
2017-09-18 17:03:52 +00:00
err := mus . 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
2017-09-15 22:20:01 +00:00
exists * sql . Stmt
2017-09-03 04:50:31 +00:00
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
2017-09-15 22:20:01 +00:00
existsStmt , err := qgen . Builder . SimpleSelect ( "users" , "uid" , "uid = ?" , "" , "" )
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 ,
2017-09-15 22:20:01 +00:00
exists : existsStmt ,
2017-09-03 04:50:31 +00:00
register : registerStmt ,
usernameExists : usernameExistsStmt ,
userCount : userCountStmt ,
2017-06-25 09:56:39 +00:00
}
2017-06-13 07:12:58 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * SQLUserStore ) Get ( id int ) ( * User , error ) {
2017-09-22 02:21:17 +00:00
user := & User { ID : id , Loggedin : true }
2017-09-18 17:03:52 +00:00
err := mus . 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
return user , err
2017-06-13 07:12:58 +00:00
}
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?
2017-09-18 17:03:52 +00:00
func ( mus * SQLUserStore ) BulkGetMap ( 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
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-18 17:03:52 +00:00
func ( mus * SQLUserStore ) BypassGet ( id int ) ( * User , error ) {
2017-09-22 02:21:17 +00:00
user := & User { ID : id , Loggedin : true }
2017-09-18 17:03:52 +00:00
err := mus . 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-09-15 22:20:01 +00:00
user . Tag = gstore . DirtyGet ( user . Group ) . Tag
2017-09-22 02:21:17 +00:00
user . initPerms ( )
return user , err
2017-09-15 22:20:01 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * SQLUserStore ) Exists ( id int ) bool {
return mus . exists . QueryRow ( id ) . Scan ( & id ) == nil
2017-06-13 07:12:58 +00:00
}
2017-09-18 17:03:52 +00:00
func ( mus * SQLUserStore ) Create ( 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-18 17:03:52 +00:00
err := mus . 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-18 17:03:52 +00:00
res , err := mus . 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-08-15 13:47:56 +00:00
// Return the total number of users registered on the forums
2017-09-18 17:03:52 +00:00
func ( mus * SQLUserStore ) GetGlobalCount ( ) int {
2017-08-15 13:47:56 +00:00
var ucount int
2017-09-18 17:03:52 +00:00
err := mus . 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
}