From aac197ef547489042b6d9380ca9aa339b8963e03 Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Fri, 22 May 2026 02:10:50 -0400 Subject: [PATCH] lock and unlock should now broadcast to the hall. Users trying to enter if hall is locked should now get message indicating the hall is locked. Removed redundant start with options. Browser requests mic permissions, also there is a mute option in the ui itself, so this seemed superfluous --- distro-packages/Arch-Linux/skald-git/PKGBUILD | 2 +- hall/hall.go | 4 +- hall/hall_test.go | 79 +++++++++++++++++++ rtpconn/webclient.go | 17 ++++ static/skald.html | 13 --- static/skald.js | 19 +++-- 6 files changed, 111 insertions(+), 23 deletions(-) diff --git a/distro-packages/Arch-Linux/skald-git/PKGBUILD b/distro-packages/Arch-Linux/skald-git/PKGBUILD index 3440178..901eb0f 100644 --- a/distro-packages/Arch-Linux/skald-git/PKGBUILD +++ b/distro-packages/Arch-Linux/skald-git/PKGBUILD @@ -2,7 +2,7 @@ # shellcheck shell=bash disable=SC2034,SC2154 pkgname=skald-git -pkgver=0.0.0.r1526.ga0a1b79 +pkgver=0.0.0.r1527.g47d1308 pkgrel=1 pkgdesc='Audio-only hall-based conferencing server' arch=('x86_64' 'aarch64') diff --git a/hall/hall.go b/hall/hall.go index 04781df..61e826e 100644 --- a/hall/hall.go +++ b/hall/hall.go @@ -549,7 +549,7 @@ func AddClient(hall string, c Client, creds ClientCredentials) (*Hall, error) { if g.locked != nil { m := *g.locked if m == "" { - m = "this hall is locked" + m = "Hall is locked." } return nil, UserError(m) } @@ -632,7 +632,7 @@ func autoLockKick(g *Hall) { } } if g.description.Autolock && g.locked == nil { - m := "this hall is locked" + m := "Hall is locked." g.locked = &m for _, c := range clients { c.Joined(g.Name(), "change") diff --git a/hall/hall_test.go b/hall/hall_test.go index 041df3a..85cd10b 100644 --- a/hall/hall_test.go +++ b/hall/hall_test.go @@ -4,10 +4,14 @@ import ( "encoding/json" "errors" "fmt" + "net" + "os" + "path/filepath" "sort" "testing" "time" + "git.stormux.org/storm/skald/conn" "github.com/pion/webrtc/v4" ) @@ -121,6 +125,81 @@ func TestChatHistory(t *testing.T) { } } +type testClient struct { + id string + username string + permissions []string +} + +func (c *testClient) Hall() *Hall { return nil } +func (c *testClient) Addr() net.Addr { return nil } +func (c *testClient) Id() string { return c.id } +func (c *testClient) Username() string { return c.username } +func (c *testClient) SetUsername(username string) { c.username = username } +func (c *testClient) Permissions() []string { return c.permissions } +func (c *testClient) SetPermissions(permissions []string) { c.permissions = permissions } +func (c *testClient) Data() map[string]interface{} { return nil } +func (c *testClient) PushConn(*Hall, string, conn.Up, []conn.UpTrack, string) error { + return nil +} +func (c *testClient) RequestConns(Client, *Hall, string) error { return nil } +func (c *testClient) Joined(string, string) error { return nil } +func (c *testClient) PushClient(string, string, string, string, []string, map[string]interface{}) error { + return nil +} +func (c *testClient) Kick(string, *string, string) error { return nil } + +func TestLockedHallJoinMessage(t *testing.T) { + halls.halls = nil + oldDirectory := Directory + Directory = t.TempDir() + t.Cleanup(func() { + Directory = oldDirectory + halls.halls = nil + }) + + err := os.WriteFile( + filepath.Join(Directory, "locked-message-test.json"), + []byte(`{ + "users": { + "user1": {"password": "secret", "permissions": "present"}, + "admin": {"password": "secret", "permissions": "op"} + } + }`), + 0644, + ) + if err != nil { + t.Fatalf("WriteFile: %v", err) + } + + g, err := Add("locked-message-test", nil) + if err != nil { + t.Fatalf("Add: %v", err) + } + g.SetLocked(true, "") + + username := "user1" + _, err = AddClient(g.Name(), &testClient{id: "user1"}, ClientCredentials{ + Username: &username, + Password: "secret", + }) + if err == nil { + t.Fatalf("AddClient unexpectedly succeeded") + } + if got, want := err.Error(), "Hall is locked."; got != want { + t.Fatalf("locked join message: got %q, want %q", got, want) + } + + adminUsername := "admin" + _, err = AddClient(g.Name(), &testClient{id: "admin"}, ClientCredentials{ + Username: &adminUsername, + Password: "secret", + }) + if err != nil { + t.Fatalf("operator join should bypass lock: %v", err) + } +} + func permissionsEqual(a, b []string) bool { // nil case if len(a) == 0 && len(b) == 0 { diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index d7ab324..73a6069 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -70,6 +70,18 @@ func broadcastRecordingStatus(g *hall.Hall, message string) { } } +func broadcastHallInfo(g *hall.Hall, message string) { + err := broadcast(g.GetClients(nil), clientMessage{ + Type: "usermessage", + Kind: "info", + Privileged: true, + Value: message, + }) + if err != nil { + log.Printf("broadcast(hall info): %v", err) + } +} + type webClient struct { hall *hall.Hall addr net.Addr @@ -1658,6 +1670,11 @@ func handleClientMessage(c *webClient, m clientMessage) error { message = v } g.SetLocked(m.Kind == "lock", message) + if m.Kind == "lock" { + broadcastHallInfo(g, "Hall locked") + } else { + broadcastHallInfo(g, "Hall unlocked") + } case "record": if !member("record", c.permissions) { return c.error(hall.UserError("not authorised")) diff --git a/static/skald.html b/static/skald.html index 2c5d672..0563446 100644 --- a/static/skald.html +++ b/static/skald.html @@ -103,19 +103,6 @@ -
- Enable at start: -
-

- - -

-

- - -

-
-
diff --git a/static/skald.js b/static/skald.js index b8ac96d..77854bc 100644 --- a/static/skald.js +++ b/static/skald.js @@ -2278,8 +2278,7 @@ function gotUserMessage(id, dest, username, time, privileged, kind, error, messa switch(kind) { case 'kicked': case 'error': - case 'warning': - case 'info': { + case 'warning': { if(!privileged) { console.error(`Got unprivileged message of kind ${kind}`); return; @@ -2288,6 +2287,16 @@ function gotUserMessage(id, dest, username, time, privileged, kind, error, messa displayError(`${from} said: ${message}`, kind); break; } + case 'info': { + if(!privileged) { + console.error(`Got unprivileged message of kind ${kind}`); + return; + } + let text = '' + message; + localMessage(text); + displayMessage(text); + break; + } case 'recording': { if(!privileged) { console.error(`Got unprivileged message of kind ${kind}`); @@ -3589,11 +3598,7 @@ document.getElementById('loginform').onsubmit = async function(e) { setVisibility('passwordform', true); - if(getInputElement('presentmike').checked) - presentRequested = 'mike'; - else - presentRequested = null; - getInputElement('presentmike').checked = true; + presentRequested = 'mike'; // Connect to the server, gotConnected will join. serverConnect();