tplayer.go - scoreboard - Interactive scoreboard for CTF-like games
(HTM) git clone git://git.z3bra.org/scoreboard.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
tplayer.go (4301B)
---
1 // Copyright 2016 The Tcell Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use file except in compliance with the License.
5 // You may obtain a copy of the license at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package main
16
17 import (
18 "crypto/rand"
19 "database/sql"
20 "encoding/base32"
21 "errors"
22 "fmt"
23 "strings"
24 "time"
25 "golang.org/x/crypto/scrypt"
26 "github.com/dustin/go-humanize"
27
28 _ "modernc.org/sqlite"
29 )
30
31 type Player struct {
32 db *sql.DB
33 id int
34 token string
35 name string
36 flags []Flag
37 score int
38 ts int64
39 }
40
41 /* Randomize a buffer of a given length */
42 func randbuf(length int64) []byte {
43 b := make([]byte, length)
44 _, err := rand.Read(b)
45 if err != nil {
46 panic(err)
47 }
48 return b
49 }
50
51 /* Generate a random base32 token using the provided input as a salt */
52 func mktoken(key []byte, salt string) (string, string, error) {
53 salt32 := base32.StdEncoding.EncodeToString([]byte(salt))
54
55 token := key
56 token = append(token, []byte(salt)...)
57
58 dk, err := scrypt.Key(key, []byte(salt32), 1<<15, 8, 1, 32)
59 if err != nil {
60 return "", "", err
61 }
62 hash32 := base32.StdEncoding.EncodeToString(dk)
63 token32 := base32.StdEncoding.EncodeToString(token)
64
65 return token32, hash32, nil
66 }
67
68 func hashtoken(token string) (string, error) {
69 var err error
70
71 blob, err := base32.StdEncoding.DecodeString(token)
72 if err != nil {
73 return "", err
74 }
75
76 key := blob[:12]
77 name := string(blob[12:])
78
79 salt := base32.StdEncoding.EncodeToString([]byte(name))
80
81 dk, err := scrypt.Key(key, []byte(salt), 1<<15, 8, 1, 32)
82 if err != nil {
83 return "", err
84 }
85 hash := base32.StdEncoding.EncodeToString(dk)
86
87 return hash, nil
88 }
89
90 /* Register a user in the database */
91 func (p *Player) Register() error {
92 var hash string
93 var err error
94
95 p.ts = time.Now().Unix()
96 p.token, hash, err = mktoken(randbuf(12), p.name)
97 if err != nil {
98 return err
99 }
100
101 return db_add_player(p.db, *p, hash)
102 }
103
104 func (p *Player) Fetch() error {
105 var err error
106 var hash string
107
108 if (p.token != "") {
109 hash, err = hashtoken(p.token)
110 }
111
112 /* Fill player struct with basic info */
113 p, err = db_get_user(p.db, p, hash)
114 if errors.Is(err, sql.ErrNoRows) {
115 return errors.New("Player not found")
116 }
117
118 return nil
119 }
120
121 func (p *Player) Rank() int {
122 rank, err := db_get_user_rank(p.db, p.name)
123 if err != nil {
124 return -1
125 }
126
127 return rank
128 }
129
130 func (p *Player) FlagStr() string {
131 return fmt.Sprintf("%2d/%d", len(p.flags), len(scoreboard.flag_ref))
132 }
133
134 func (p *Player) FlagsStr() string {
135 var flaglist strings.Builder
136
137 flaglist.WriteString("[-::-]")
138 for _, f := range p.flags {
139 flaglist.WriteString(fmt.Sprintf("%s %s\n", f.badge, f.value))
140 }
141
142 var hasflag = func(a []Flag, f Flag) bool {
143 for _, r := range a {
144 if f.value == r.value {
145 return true
146 }
147 }
148 return false
149 }
150 for _, r := range scoreboard.flag_ref {
151 if !hasflag(p.flags, r) {
152 flaglist.WriteString(fmt.Sprintf("[::d]%s %64s[::-]\n", r.badge, ""))
153 }
154 }
155
156 return flaglist.String();
157 }
158
159 func (p *Player) BadgeStr() string {
160 var badges strings.Builder
161
162 badges.WriteString("[-::-]")
163 for n, f := range p.flags {
164 if n > 0 && n % 8 == 0 {
165 badges.WriteString("\n")
166 }
167 badges.WriteString(fmt.Sprintf(`%s`, f.badge))
168 }
169
170 return badges.String();
171 }
172
173 func (p *Player) RankStr() string {
174 return humanize.Ordinal(p.Rank() + 1)
175 }
176
177 func (p *Player) Exists() bool {
178 forbidden := []string{"KKK"}
179 for i := 0; i<len(forbidden); i++ {
180 if p.name == forbidden[i] {
181 return true
182 }
183 }
184
185 _, err := db_get_user(p.db, p, "")
186
187 return !errors.Is(err, sql.ErrNoRows)
188 }
189
190 func (p *Player) HasFlag(flag Flag) bool {
191 for _, f := range p.flags {
192 if f.value == flag.value {
193 return true
194 }
195 }
196 return false
197 }
198
199 func (p *Player) Submit(flag Flag) error {
200 var err error
201
202 if p.HasFlag(flag) {
203 return errors.New("Flag already submitted")
204 } else {
205 p.ts = time.Now().Unix()
206 err = db_add_player_flag(p.db, *p, flag)
207 if err != nil {
208 return err
209 }
210 }
211
212 p.flags = append(p.flags, flag)
213
214 return nil
215 }
216