tFix flag submission - scoreboard - Interactive scoreboard for CTF-like games
 (HTM) git clone git://git.z3bra.org/scoreboard.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
       ---
 (DIR) commit 307cde02b7be14d34c37e7f02eeaea907d1c0d84
 (DIR) parent cc20e9fc44c4a724943a4eb8d8a1edb741ecbb39
 (HTM) Author: Willy Goiffon <contact@z3bra.org>
       Date:   Thu, 26 Sep 2024 00:11:35 +0200
       
       Fix flag submission
       
       Diffstat:
         M db.go                               |      26 ++++++++++++++++++++++++++
         M main.go                             |      28 +++++++++++++++-------------
         M player.go                           |      46 ++++++++++++++++++++++---------
         M ui.go                               |       4 +---
       
       4 files changed, 75 insertions(+), 29 deletions(-)
       ---
 (DIR) diff --git a/db.go b/db.go
       t@@ -74,6 +74,32 @@ func db_count_players(db *sql.DB) (int, error) {
                return count, err
        }
        
       +func db_count_user_flags(db *sql.DB, name string) (int, error) {
       +        var count int
       +
       +        query := `SELECT count(*) FROM score WHERE name = ?;`
       +
       +        row := db.QueryRow(query, name)
       +        err := row.Scan(&count)
       +
       +        return count, err
       +}
       +
       +func db_calculate_user_score(db *sql.DB, name string) (int, error) {
       +        var count int
       +
       +        query := `SELECT
       +          sum(flag.score)
       +          FROM flag
       +          INNER JOIN score ON score.flag = flag.value
       +          WHERE score.name = ?;`
       +
       +        row := db.QueryRow(query, name)
       +        err := row.Scan(&count)
       +
       +        return count, err
       +}
       +
        func db_get_flags(db *sql.DB) ([]Flag, error) {
                query := `SELECT rowid,value,badge,score FROM flag ORDER BY score;`
        
 (DIR) diff --git a/main.go b/main.go
       t@@ -24,7 +24,6 @@ import (
                "database/sql"
                "github.com/gdamore/tcell/v2"
                "github.com/rivo/tview"
       -        "github.com/dustin/go-humanize"
        
                _ "modernc.org/sqlite"
        )
       t@@ -34,7 +33,7 @@ const (
                BOARD_HEIGHT int = 15
                HTML string = "score.html"
                DB string = "score.db"
       -        TOKEN_REMINDER string = `%s, use the token below to submit your %s flag.
       +        TOKEN_REMINDER string = `%s, use the token below to submit your next flag.
        Save it carefully, do not share it.
        
          🔑%s
       t@@ -49,7 +48,7 @@ type Flag struct {
        }
        
        type Application struct {
       -        flag int
       +        flag string
                flag_ref []Flag
                db *sql.DB
                app *tview.Application
       t@@ -101,22 +100,26 @@ func pageToken() tview.Primitive {
                                        return
                                }
        
       -                        if len(input.GetText()) != 24 {
       +                        token := input.GetText()
       +
       +                        if len(token) != 24 {
                                        scoreboard.Popup("ERROR", "Invalid token format")
                                        return
                                }
       -                        err := scoreboard.player.FromToken(input.GetText())
       +                        err := scoreboard.player.FromToken(token)
                                if err != nil {
                                        scoreboard.Fatal(err)
                                        return
                                }
        
       +                        scoreboard.player.token = token;
       +
                                err = scoreboard.player.Submit(scoreboard.flag)
                                if err != nil {
                                        scoreboard.Fatal(err)
                                        return
                                }
       -                        scoreboard.HighlightBoard(scoreboard.player.ScoreRank())
       +                        scoreboard.HighlightBoard(scoreboard.player.ScoreRank() + 1)
                                scoreboard.pages.SwitchToPage("board")
                                scoreboard.GenerateHTML()
                        })
       t@@ -163,7 +166,7 @@ func main() {
        
                scoreboard.flag_ref, err = db_get_flags(scoreboard.db)
        
       -        scoreboard.flag = 0
       +        scoreboard.flag = ""
                scoreboard.html = *html
                scoreboard.app = tview.NewApplication()
                scoreboard.pages = tview.NewPages()
       t@@ -196,13 +199,12 @@ func main() {
                                scoreboard.NewPlayer(rank + 1)
                                scoreboard.pages.SwitchToPage("board")
                        } else {
       -                        switch scoreboard.flag = flagid(args[0]) + 1; scoreboard.flag {
       -                        case 0:
       +                        if flagid(args[0]) < 0 {
                                        fmt.Println("Incorrect flag")
                                        return
       -                        default:
       -                                scoreboard.pages.SwitchToPage("token")
                                }
       +                        scoreboard.flag = args[0]
       +                        scoreboard.pages.SwitchToPage("token")
                        }
                } else {
                        scoreboard.pages.SwitchToPage("board")
       t@@ -214,7 +216,7 @@ func main() {
                        os.Exit(1)
                }
        
       -        if scoreboard.player.token != "" && scoreboard.flag < 7 {
       -                fmt.Printf(TOKEN_REMINDER, scoreboard.player.name, humanize.Ordinal(scoreboard.flag + 1), scoreboard.player.token)
       +        if scoreboard.player.token != "" {
       +                fmt.Printf(TOKEN_REMINDER, scoreboard.player.name, scoreboard.player.token)
                }
        }
 (DIR) diff --git a/player.go b/player.go
       t@@ -76,7 +76,7 @@ func (p *Player) Register() error {
                        return err
                }
        
       -        query := `INSERT INTO score(name,hash,ts) VALUES(?,?,?);`
       +        query := `INSERT INTO user(name,hash,score,flag,ts) VALUES(?,?,0,0,?);`
                _, err = p.db.Exec(query, p.name, hash, p.ts)
                if err != nil {
                        return err
       t@@ -104,11 +104,21 @@ func (p *Player) Fetch() error {
                return nil
        }
        
       -func (p *Player) Refresh(score int, flag int, ts int64) error {
       +func (p *Player) Refresh(ts int64) error {
                var err error
        
       +        p.flag, err = db_count_user_flags(p.db, p.name)
       +        if err != nil {
       +                return err
       +        }
       +
       +        p.score, err = db_calculate_user_score(p.db, p.name)
       +        if err != nil {
       +                return err
       +        }
       +
                query := `UPDATE user SET score = ?, flag = ?, ts = ? WHERE name = ?;`
       -        _, err = p.db.Exec(query, score, flag, ts, p.name)
       +        _, err = p.db.Exec(query, p.score, p.flag, ts, p.name)
                if err != nil {
                        return err
                }
       t@@ -161,20 +171,30 @@ func (p *Player) Exists() bool {
                return (count > 0)
        }
        
       -func (p *Player) Submit(flag int) error {
       +func (p *Player) HasFlag(flag string) bool {
       +        var count int
       +        query := `SELECT count(*) FROM score WHERE name = ? AND flag = ?;`
       +        row := p.db.QueryRow(query, p.name, flag)
       +        row.Scan(&count)
       +        return (count > 0)
       +}
       +
       +func (p *Player) Submit(flag string) error {
       +        var err error
                var ts int64
       -        var score int
       -        var flags int
        
       -        // TODO: check flag existence
       -        // TODO: check flag already submitted
       -        // TODO: retrieve flag score
       -        ts = time.Now().Unix()
       -        score = p.score // + flag_score
       -        flags = p.flag + 1
       +        if p.HasFlag(flag) {
       +                return errors.New("Flag already submitted")
       +        } else {
       +                query := `INSERT INTO score(name,flag) VALUES(?,?);`
       +                _, err = p.db.Exec(query, p.name, flag)
       +                if err != nil {
       +                        return err
       +                }
       +        }
        
                // update user status in database
       -        err := p.Refresh(score, flags, ts)
       +        err = p.Refresh(ts)
                if err != nil {
                        return err
                }
 (DIR) diff --git a/ui.go b/ui.go
       t@@ -107,9 +107,7 @@ func (a *Application) HighlightBoard(line int) {
                        AddItem(RankTable(0, -1, 0, true).Select(line - 1, 0), BOARD_HEIGHT, 1, true)
                a.app.SetFocus(a.board)
        
       -        if (a.flag < 7) {
       -                a.frame.AddText(fmt.Sprintf(" 🔑%s", a.player.token), false, tview.AlignCenter, 0)
       -        }
       +        a.frame.AddText(fmt.Sprintf(" 🔑%s", a.player.token), false, tview.AlignCenter, 0)
        }
        
        func (a *Application) NewPlayer(rank int) {