// NGnius 2020-02-12 package main import ( "crypto/rand" "crypto/sha512" "encoding/binary" "encoding/json" "math/big" "strconv" // test //"fmt" ) var ( randomizeTokens bool ) type Jsonable interface { Json() ([]byte, error) JsonPretty() ([]byte, error) Unjson([]byte) error JsonObject() (interface{}, error) } 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 Jsonable func (b *Board) Json() ([]byte, error) { var data []byte jsonObj, err := b.JsonObject() if err != nil { return data, err } return json.Marshal(jsonObj) } func (b *Board) JsonPretty() ([]byte, error) { var data []byte jsonObj, err := b.JsonObject() if err != nil { return data, err } return json.MarshalIndent(jsonObj, "", " ") } func (b *Board) JsonObject() (interface{}, error) { jsonObj := BoardJSON{ID: b.ID, Name: b.Name, Description: b.Description, Url: b.Url()} return jsonObj, nil } // 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 Jsonable func (p *Player) Json() ([]byte, error) { var data []byte jsonObj, err := p.JsonObject() if err != nil { return data, err } return json.Marshal(jsonObj) } func (p *Player) JsonPretty() ([]byte, error) { var data []byte jsonObj, err := p.JsonObject() if err != nil { return data, err } return json.MarshalIndent(jsonObj, "", " ") } func (p *Player) JsonObject() (interface{}, error) { jsonObj := PlayerJSON{ID: p.ID, Name: p.Name, Url: p.Url()} return jsonObj, nil } // 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 Jsonable func (e *Entry) Json() ([]byte, error) { var data []byte jsonObj, err := e.JsonObject() if err != nil { return data, err } return json.Marshal(jsonObj) } func (e *Entry) JsonPretty() ([]byte, error) { var data []byte jsonObj, err := e.JsonObject() if err != nil { return data, err } return json.MarshalIndent(jsonObj, "", " ") } func (e *Entry) JsonObject() (interface{}, error) { jsonObj := EntryJSON{ID: e.ID, Rank: e.Rank, Score: e.Score, PlayerID: e.Player, BoardID: e.Board} ePlayer := &Player{ID: e.Player} err := ePlayer.Load() if err != nil { return jsonObj, err } jsonObj.PlayerName = ePlayer.Name jsonObj.PlayerURL = ePlayer.Url() eBoard := &Board{ID: e.Board} err = eBoard.Load() if err != nil { return jsonObj, err } jsonObj.BoardName = eBoard.Name jsonObj.BoardURL = eBoard.Url() return jsonObj, nil } // 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 Jsonable func (k *Key) Json() ([]byte, error) { var data []byte jsonObj, err := k.JsonObject() if err != nil { return data, err } return json.Marshal(jsonObj) } func (k *Key) JsonPretty() ([]byte, error) { var data []byte jsonObj, err := k.JsonObject() if err != nil { return data, err } return json.MarshalIndent(jsonObj, "", " ") } func (k *Key) JsonObject() (interface{}, error) { jsonObj := KeyJSON{Token: k.Token, PlayerID: k.Player} return jsonObj, 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} }