319 lines
6.4 KiB
Go
319 lines
6.4 KiB
Go
|
// NGnius 2020-02-12
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"crypto/rand"
|
||
|
"crypto/sha512"
|
||
|
"encoding/binary"
|
||
|
"math/big"
|
||
|
"strconv"
|
||
|
// test
|
||
|
//"fmt"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
randomizeTokens bool
|
||
|
)
|
||
|
|
||
|
type Rower interface {
|
||
|
Intake() []interface{}
|
||
|
Output() []interface{}
|
||
|
}
|
||
|
|
||
|
type Board struct {
|
||
|
ID int64
|
||
|
Name string
|
||
|
Description string
|
||
|
}
|
||
|
|
||
|
func LoadBoard(id int64) *Board {
|
||
|
b := &Board{ID: id}
|
||
|
loadErr := b.Load()
|
||
|
if loadErr != nil {
|
||
|
return nil
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (b *Board) Load() error {
|
||
|
return db.QueryRow(queryStrings[queryType][7], b.ID).Scan(b.Intake()...)
|
||
|
}
|
||
|
|
||
|
func (b *Board) Commit() error {
|
||
|
tx, _ := db.Begin()
|
||
|
statement, _ := tx.Prepare(queryStrings[queryType][8])
|
||
|
_, err := statement.Exec(b.Output()...)
|
||
|
if err != nil {
|
||
|
tx.Rollback()
|
||
|
return err
|
||
|
}
|
||
|
return tx.Commit()
|
||
|
}
|
||
|
|
||
|
func (b *Board) Entries() ([]*Entry, error) {
|
||
|
var entries []*Entry
|
||
|
rows, err := db.Query(queryStrings[queryType][9], b.ID)
|
||
|
if err != nil {
|
||
|
return entries, err
|
||
|
}
|
||
|
count := 0
|
||
|
for rows.Next() {
|
||
|
entries = append(entries, &Entry{})
|
||
|
scanErr := rows.Scan(entries[count].Intake()...)
|
||
|
if scanErr != nil {
|
||
|
return entries, scanErr
|
||
|
}
|
||
|
count++
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
|
||
|
func (b *Board) SomeEntries(start, end int64) ([]*Entry, error) {
|
||
|
var entries []*Entry
|
||
|
rows, err := db.Query(queryStrings[queryType][10], b.ID, start, end)
|
||
|
if err != nil {
|
||
|
return entries, err
|
||
|
}
|
||
|
count := 0
|
||
|
for rows.Next() {
|
||
|
entries = append(entries, &Entry{})
|
||
|
scanErr := rows.Scan(entries[count].Intake()...)
|
||
|
if scanErr != nil {
|
||
|
return entries, scanErr
|
||
|
}
|
||
|
count++
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
|
||
|
func (b *Board) Url() string {
|
||
|
return "/board?name=" + b.Name
|
||
|
}
|
||
|
|
||
|
// implementation of Rower
|
||
|
func (b *Board) Intake() []interface{} {
|
||
|
return []interface{}{&b.ID, &b.Name, &b.Description}
|
||
|
}
|
||
|
|
||
|
func (b *Board) Output() []interface{} {
|
||
|
return []interface{}{b.ID, b.Name, b.Description}
|
||
|
}
|
||
|
|
||
|
type Player struct {
|
||
|
ID int64
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
func LoadPlayer(id int64) *Player {
|
||
|
p := &Player{ID: id}
|
||
|
loadErr := p.Load()
|
||
|
if loadErr != nil {
|
||
|
return nil
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
func (p *Player) Load() error {
|
||
|
return db.QueryRow(queryStrings[queryType][11], p.ID).Scan(p.Intake()...)
|
||
|
}
|
||
|
|
||
|
func (p *Player) Commit() error {
|
||
|
tx, _ := db.Begin()
|
||
|
statement, _ := tx.Prepare(queryStrings[queryType][12])
|
||
|
_, err := statement.Exec(p.Output()...)
|
||
|
if err != nil {
|
||
|
tx.Rollback()
|
||
|
return err
|
||
|
}
|
||
|
return tx.Commit()
|
||
|
}
|
||
|
|
||
|
func (p *Player) Entries() ([]*Entry, error) {
|
||
|
var entries []*Entry
|
||
|
rows, err := db.Query(queryStrings[queryType][13], p.ID)
|
||
|
if err != nil {
|
||
|
return entries, err
|
||
|
}
|
||
|
count := 0
|
||
|
for rows.Next() {
|
||
|
entries = append(entries, &Entry{})
|
||
|
scanErr := rows.Scan(entries[count].Intake()...)
|
||
|
if scanErr != nil {
|
||
|
return entries, scanErr
|
||
|
}
|
||
|
count++
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
|
||
|
func (p *Player) SomeEntries(limit int64) ([]*Entry, error) {
|
||
|
var entries []*Entry
|
||
|
rows, err := db.Query(queryStrings[queryType][14], p.ID, limit)
|
||
|
if err != nil {
|
||
|
return entries, err
|
||
|
}
|
||
|
count := 0
|
||
|
for rows.Next() {
|
||
|
entries = append(entries, &Entry{})
|
||
|
scanErr := rows.Scan(entries[count].Intake()...)
|
||
|
if scanErr != nil {
|
||
|
return entries, scanErr
|
||
|
}
|
||
|
count++
|
||
|
}
|
||
|
return entries, nil
|
||
|
}
|
||
|
|
||
|
func (p *Player) Url() string {
|
||
|
return "/player?id=" + strconv.Itoa(int(p.ID))
|
||
|
}
|
||
|
|
||
|
// implementation of Rower
|
||
|
func (p *Player) Intake() []interface{} {
|
||
|
return []interface{}{&p.ID, &p.Name}
|
||
|
}
|
||
|
|
||
|
func (p *Player) Output() []interface{} {
|
||
|
return []interface{}{p.ID, p.Name}
|
||
|
}
|
||
|
|
||
|
type Entry struct {
|
||
|
ID int64
|
||
|
Rank int64
|
||
|
Score int64
|
||
|
Player int64
|
||
|
Board int64
|
||
|
Time int64 // Created time (seconds since Unix epoch)
|
||
|
Metadata []byte
|
||
|
}
|
||
|
|
||
|
func LoadEntry(id int64) *Entry {
|
||
|
e := &Entry{ID: id}
|
||
|
loadErr := e.Load()
|
||
|
if loadErr != nil {
|
||
|
return nil
|
||
|
}
|
||
|
return e
|
||
|
}
|
||
|
|
||
|
func (e *Entry) Load() error {
|
||
|
return db.QueryRow(queryStrings[queryType][15], e.ID).Scan(e.Intake()...)
|
||
|
}
|
||
|
|
||
|
func (e *Entry) Commit() error {
|
||
|
tx, _ := db.Begin()
|
||
|
statement, _ := tx.Prepare(queryStrings[queryType][16])
|
||
|
_, err := statement.Exec(e.Output()...)
|
||
|
if err != nil {
|
||
|
tx.Rollback()
|
||
|
return err
|
||
|
}
|
||
|
return tx.Commit()
|
||
|
}
|
||
|
|
||
|
// implementation of Rower
|
||
|
func (e *Entry) Intake() []interface{} {
|
||
|
return []interface{}{&e.ID, &e.Rank, &e.Score, &e.Player, &e.Board, &e.Time, &e.Metadata}
|
||
|
}
|
||
|
|
||
|
func (e *Entry) Output() []interface{} {
|
||
|
return []interface{}{e.ID, e.Rank, e.Score, e.Player, e.Board, e.Time, e.Metadata}
|
||
|
}
|
||
|
|
||
|
type Key struct {
|
||
|
ID int64
|
||
|
Token string
|
||
|
Player int64
|
||
|
Time int64 // Created time (seconds since Unix epoch)
|
||
|
Metadata []byte
|
||
|
}
|
||
|
|
||
|
func LoadKey(id int64) *Key {
|
||
|
k := &Key{ID: id}
|
||
|
loadErr := k.Load()
|
||
|
if loadErr != nil {
|
||
|
return nil
|
||
|
}
|
||
|
return k
|
||
|
}
|
||
|
|
||
|
func (k *Key) Load() error {
|
||
|
return db.QueryRow(queryStrings[queryType][17], k.ID).Scan(k.Intake()...)
|
||
|
}
|
||
|
|
||
|
func (k *Key) Commit() error {
|
||
|
tx, _ := db.Begin()
|
||
|
statement, _ := tx.Prepare(queryStrings[queryType][18])
|
||
|
_, err := statement.Exec(k.Output()...)
|
||
|
if err != nil {
|
||
|
tx.Rollback()
|
||
|
return err
|
||
|
}
|
||
|
return tx.Commit()
|
||
|
}
|
||
|
|
||
|
func (k *Key) IsEnabled() bool {
|
||
|
return (k.Metadata[0] & 0b00000001) == 0
|
||
|
}
|
||
|
|
||
|
func (k *Key) Disable() {
|
||
|
k.Metadata[0] = k.Metadata[0] | 0b00000001
|
||
|
}
|
||
|
|
||
|
func (k *Key) IsSuperuser() bool {
|
||
|
return (k.Metadata[0] & 0b00000100) == 0
|
||
|
}
|
||
|
|
||
|
func (k *Key) Promote() {
|
||
|
k.Metadata[0] = k.Metadata[0] | 0b00000100
|
||
|
}
|
||
|
|
||
|
func (k *Key) IsMultiuser() bool {
|
||
|
return (k.Metadata[0] & 0b00010000) == 0
|
||
|
}
|
||
|
|
||
|
func (k *Key) Develop() {
|
||
|
k.Metadata[0] = k.Metadata[0] | 0b00010000
|
||
|
}
|
||
|
|
||
|
func (k *Key) GenerateToken() error {
|
||
|
buf_int64 := make([]byte, 10) // 8 bytes = 64 bits
|
||
|
input := []byte{}
|
||
|
if randomizeTokens {
|
||
|
max := big.NewInt(2 ^ 16 - 1)
|
||
|
for i := 0; i < 512; i++ {
|
||
|
// generate randomness
|
||
|
num, _ := rand.Int(rand.Reader, max)
|
||
|
input = append(input, num.Bytes()...)
|
||
|
}
|
||
|
}
|
||
|
binary.PutVarint(buf_int64, k.ID)
|
||
|
input = append(input, buf_int64[0:8]...)
|
||
|
binary.PutVarint(buf_int64, k.Player)
|
||
|
input = append(input, buf_int64[0:8]...)
|
||
|
binary.PutVarint(buf_int64, k.Time)
|
||
|
input = append(input, buf_int64[0:8]...)
|
||
|
bToken := sha512.Sum512(input)
|
||
|
//k.Token = string(bToken)
|
||
|
k.Token = ""
|
||
|
for _, b := range bToken {
|
||
|
tmp_b := b & 0b01111111 // Valid 7-bit ASCII values only
|
||
|
for !((tmp_b > 47 && tmp_b < 58) || (tmp_b > 64 && tmp_b < 91) || (tmp_b > 96 && tmp_b < 123)) {
|
||
|
tmp_b = sha512.Sum512([]byte{tmp_b})[1]
|
||
|
tmp_b = tmp_b & 0b01111111
|
||
|
}
|
||
|
k.Token += string(tmp_b)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Implementation of Rower
|
||
|
func (k *Key) Intake() []interface{} {
|
||
|
return []interface{}{&k.ID, &k.Token, &k.Player, &k.Time, &k.Metadata}
|
||
|
}
|
||
|
|
||
|
func (k *Key) Output() []interface{} {
|
||
|
return []interface{}{k.ID, k.Token, k.Player, k.Time, k.Metadata}
|
||
|
}
|