Rename group package and defaults to halls

This commit is contained in:
Storm Dragon
2026-05-17 22:37:38 -04:00
parent 55528b8e62
commit a8ada950d5
26 changed files with 459 additions and 459 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
/skald /skald
/skaldctl/skaldctl /skaldctl/skaldctl
/data /data
/groups/**/*.json /halls/**/*.json
/static/**/*.d.ts /static/**/*.d.ts
/static/third-party/tasks-vision /static/third-party/tasks-vision
+2 -2
View File
@@ -10,8 +10,8 @@ hall conferencing server. The current development repository is
git clone https://git.stormux.org/storm/skald git clone https://git.stormux.org/storm/skald
cd skald cd skald
CGO_ENABLED=0 go build -ldflags='-s -w' CGO_ENABLED=0 go build -ldflags='-s -w'
mkdir groups mkdir halls
echo '{"users": {"vimes": {"password":"sybil", "permissions":"op"}}}' > groups/night-watch.json echo '{"users": {"vimes": {"password":"sybil", "permissions":"op"}}}' > halls/night-watch.json
./skald & ./skald &
``` ```
+16 -16
View File
@@ -24,7 +24,7 @@ import (
gcodecs "git.stormux.org/storm/skald/codecs" gcodecs "git.stormux.org/storm/skald/codecs"
"git.stormux.org/storm/skald/conn" "git.stormux.org/storm/skald/conn"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/rtptime" "git.stormux.org/storm/skald/rtptime"
) )
@@ -36,8 +36,8 @@ const (
var Directory string var Directory string
type Client struct { type Client struct {
group *group.Group hall *hall.Hall
id string id string
mu sync.Mutex mu sync.Mutex
down map[string]*diskConn down map[string]*diskConn
@@ -50,12 +50,12 @@ func newId() string {
return hex.EncodeToString(b) return hex.EncodeToString(b)
} }
func New(g *group.Group) *Client { func New(g *hall.Hall) *Client {
return &Client{group: g, id: newId()} return &Client{hall: g, id: newId()}
} }
func (client *Client) Group() *group.Group { func (client *Client) Hall() *hall.Hall {
return client.group return client.hall
} }
func (client *Client) Id() string { func (client *Client) Id() string {
@@ -86,7 +86,7 @@ func (client *Client) PushClient(group, kind, id, username string, perms []strin
return nil return nil
} }
func (client *Client) RequestConns(target group.Client, g *group.Group, id string) error { func (client *Client) RequestConns(target hall.Client, g *hall.Hall, id string) error {
return nil return nil
} }
@@ -104,7 +104,7 @@ func (client *Client) Close() error {
func (client *Client) Kick(id string, user *string, message string) error { func (client *Client) Kick(id string, user *string, message string) error {
err := client.Close() err := client.Close()
group.DelClient(client) hall.DelClient(client)
return err return err
} }
@@ -116,8 +116,8 @@ func (client *Client) Joined(group, kind string) error {
return nil return nil
} }
func (client *Client) PushConn(g *group.Group, id string, up conn.Up, tracks []conn.UpTrack, replace string) error { func (client *Client) PushConn(g *hall.Hall, id string, up conn.Up, tracks []conn.UpTrack, replace string) error {
if client.group != g { if client.hall != g {
return nil return nil
} }
@@ -148,7 +148,7 @@ func (client *Client) PushConn(g *group.Group, id string, up conn.Up, tracks []c
return nil return nil
} }
directory := filepath.Join(Directory, client.group.Name()) directory := filepath.Join(Directory, client.hall.Name())
err := os.MkdirAll(directory, 0700) err := os.MkdirAll(directory, 0700)
if err != nil { if err != nil {
g.WallOps("Write to disk: " + err.Error()) g.WallOps("Write to disk: " + err.Error())
@@ -192,7 +192,7 @@ func (conn *diskConn) warn(message string) {
return return
} }
log.Println(message) log.Println(message)
conn.client.group.WallOps(message) conn.client.hall.WallOps(message)
conn.lastWarning = now conn.lastWarning = now
} }
@@ -330,7 +330,7 @@ func newDiskConn(client *Client, directory string, up conn.Up, remoteTracks []co
if audio == nil { if audio == nil {
audio = remote audio = remote
} else { } else {
client.group.WallOps("Multiple audio tracks, recording just one") client.hall.WallOps("Multiple audio tracks, recording just one")
} }
} else if strings.EqualFold(codec, "video/vp8") || } else if strings.EqualFold(codec, "video/vp8") ||
strings.EqualFold(codec, "video/vp9") || strings.EqualFold(codec, "video/vp9") ||
@@ -338,10 +338,10 @@ func newDiskConn(client *Client, directory string, up conn.Up, remoteTracks []co
if video == nil || video.Label() == "l" { if video == nil || video.Label() == "l" {
video = remote video = remote
} else if remote.Label() != "l" { } else if remote.Label() != "l" {
client.group.WallOps("Multiple video tracks, recording just one") client.hall.WallOps("Multiple video tracks, recording just one")
} }
} else { } else {
client.group.WallOps("Unknown codec, " + codec + ", not recording") client.hall.WallOps("Unknown codec, " + codec + ", not recording")
} }
} }
+6 -6
View File
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"bytes" "bytes"
@@ -134,7 +134,7 @@ type ClientCredentials struct {
} }
type Client interface { type Client interface {
Group() *Group Hall() *Hall
Addr() net.Addr Addr() net.Addr
Id() string Id() string
Username() string Username() string
@@ -142,9 +142,9 @@ type Client interface {
Permissions() []string Permissions() []string
SetPermissions([]string) SetPermissions([]string)
Data() map[string]interface{} Data() map[string]interface{}
PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error PushConn(g *Hall, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error
RequestConns(target Client, g *Group, id string) error RequestConns(target Client, g *Hall, id string) error
Joined(group, kind string) error Joined(hall, kind string) error
PushClient(group, kind, id, username string, perms []string, data map[string]interface{}) error PushClient(hall, kind, id, username string, perms []string, data map[string]interface{}) error
Kick(id string, user *string, message string) error Kick(id string, user *string, message string) error
} }
+1 -1
View File
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"bytes" "bytes"
+39 -39
View File
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"encoding/json" "encoding/json"
@@ -147,11 +147,11 @@ func (u UserDescription) MarshalJSON() ([]byte, error) {
return json.Marshal(uu) return json.Marshal(uu)
} }
// Description represents a group description together with some metadata // Description represents a hall description together with some metadata
// about the JSON file it was deserialised from. // about the JSON file it was deserialised from.
type Description struct { type Description struct {
// The file this was deserialised from. This is not necessarily // The file this was deserialised from. This is not necessarily
// the name of the group, for example in case of a subgroup. // the name of the hall, for example in case of a subgroup.
FileName string `json:"-"` FileName string `json:"-"`
// The modtime and size of the file. These are used to detect // The modtime and size of the file. These are used to detect
@@ -162,10 +162,10 @@ type Description struct {
// Whether this is an automatically generated subgroup // Whether this is an automatically generated subgroup
isSubgroup bool `json:"-"` isSubgroup bool `json:"-"`
// The user-friendly group name // The user-friendly hall name
DisplayName string `json:"displayName,omitempty"` DisplayName string `json:"displayName,omitempty"`
// A user-readable description of the group. // A user-readable description of the hall.
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
// A user-readable contact, typically an e-mail address. // A user-readable contact, typically an e-mail address.
@@ -174,10 +174,10 @@ type Description struct {
// A user-readable comment. Ignored by the server. // A user-readable comment. Ignored by the server.
Comment string `json:"comment,omitempty"` Comment string `json:"comment,omitempty"`
// Whether to display the group on the landing page. // Whether to display the hall on the landing page.
Public bool `json:"public,omitempty"` Public bool `json:"public,omitempty"`
// A URL to redirect the group to. If this is not empty, most // A URL to redirect the hall to. If this is not empty, most
// other fields are ignored. // other fields are ignored.
Redirect string `json:"redirect,omitempty"` Redirect string `json:"redirect,omitempty"`
@@ -202,7 +202,7 @@ type Description struct {
// Whether subgroups are created on the fly. // Whether subgroups are created on the fly.
AutoSubgroups bool `json:"auto-subgroups,omitempty"` AutoSubgroups bool `json:"auto-subgroups,omitempty"`
// Whether to lock the group when the last op logs out. // Whether to lock the hall when the last op logs out.
Autolock bool `json:"autolock,omitempty"` Autolock bool `json:"autolock,omitempty"`
// Whether to kick all users when the last op logs out. // Whether to kick all users when the last op logs out.
@@ -278,7 +278,7 @@ func descriptionMatch(d1, d2 *Description) bool {
return true return true
} }
// descriptionUnchanged returns true if a group's description hasn't // descriptionUnchanged returns true if a hall's description hasn't
// changed since it was last read. // changed since it was last read.
func descriptionUnchanged(name string, desc *Description) bool { func descriptionUnchanged(name string, desc *Description) bool {
fi, fileName, _, err := getDescriptionFile(name, true, os.Stat) fi, fileName, _, err := getDescriptionFile(name, true, os.Stat)
@@ -292,7 +292,7 @@ func descriptionUnchanged(name string, desc *Description) bool {
return true return true
} }
// GetDescription gets a group description, either from cache or from disk // GetDescription gets a hall description, either from cache or from disk
func GetDescription(name string) (*Description, error) { func GetDescription(name string) (*Description, error) {
g := Get(name) g := Get(name)
if g != nil { if g != nil {
@@ -336,10 +336,10 @@ func makeETag(fileSize int64, modTime time.Time) string {
} }
// DeleteDescription deletes a description (and therefore persistently // DeleteDescription deletes a description (and therefore persistently
// deletes a group) but only if it matches a given ETag. // deletes a hall) but only if it matches a given ETag.
func DeleteDescription(name, etag string) error { func DeleteDescription(name, etag string) error {
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
fi, fileName, _, err := getDescriptionFile(name, false, os.Stat) fi, fileName, _, err := getDescriptionFile(name, false, os.Stat)
if err != nil { if err != nil {
@@ -352,14 +352,14 @@ func DeleteDescription(name, etag string) error {
} }
// UpdateDescription overwrites a description if it matches a given ETag. // UpdateDescription overwrites a description if it matches a given ETag.
// In order to create a new group, pass an empty ETag. // In order to create a new hall, pass an empty ETag.
func UpdateDescription(name, etag string, desc *Description) error { func UpdateDescription(name, etag string, desc *Description) error {
if desc.Users != nil || desc.WildcardUser != nil || desc.AuthKeys != nil { if desc.Users != nil || desc.WildcardUser != nil || desc.AuthKeys != nil {
return errors.New("description is not sanitised") return errors.New("description is not sanitised")
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
oldetag := "" oldetag := ""
var filename string var filename string
@@ -438,7 +438,7 @@ func rewriteDescriptionFile(filename string, desc *Description) error {
} }
// readDescription reads a group's description from disk // readDescription reads a hall's description from disk
func readDescription(name string, allowSubgroups bool) (*Description, error) { func readDescription(name string, allowSubgroups bool) (*Description, error) {
r, fileName, isSubgroup, err := r, fileName, isSubgroup, err :=
getDescriptionFile(name, allowSubgroups, os.Open) getDescriptionFile(name, allowSubgroups, os.Open)
@@ -585,7 +585,7 @@ func GetDescriptionNames() ([]string, error) {
return names, err return names, err
} }
func SetKeys(group string, keys []map[string]any) error { func SetKeys(hall string, keys []map[string]any) error {
if keys != nil { if keys != nil {
_, err := token.ParseKeys(keys, "", "") _, err := token.ParseKeys(keys, "", "")
if err != nil { if err != nil {
@@ -593,10 +593,10 @@ func SetKeys(group string, keys []map[string]any) error {
} }
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
desc, err := readDescription(group, false) desc, err := readDescription(hall, false)
if err != nil { if err != nil {
return err return err
} }
@@ -604,8 +604,8 @@ func SetKeys(group string, keys []map[string]any) error {
return rewriteDescriptionFile(desc.FileName, desc) return rewriteDescriptionFile(desc.FileName, desc)
} }
func GetUsers(group string) ([]string, string, error) { func GetUsers(hall string) ([]string, string, error) {
desc, err := GetDescription(group) desc, err := GetDescription(hall)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@@ -618,13 +618,13 @@ func GetUsers(group string) ([]string, string, error) {
return users, makeETag(desc.fileSize, desc.modTime), nil return users, makeETag(desc.fileSize, desc.modTime), nil
} }
func GetSanitisedUser(group, username string, wildcard bool) (UserDescription, string, error) { func GetSanitisedUser(hall, username string, wildcard bool) (UserDescription, string, error) {
if wildcard && username != "" { if wildcard && username != "" {
return UserDescription{}, "", return UserDescription{}, "",
errors.New("wildcard with username") errors.New("wildcard with username")
} }
desc, err := GetDescription(group) desc, err := GetDescription(hall)
if err != nil { if err != nil {
return UserDescription{}, "", err return UserDescription{}, "", err
} }
@@ -651,20 +651,20 @@ func GetSanitisedUser(group, username string, wildcard bool) (UserDescription, s
return u, makeETag(desc.fileSize, desc.modTime), nil return u, makeETag(desc.fileSize, desc.modTime), nil
} }
func GetUserTag(group, username string, wildcard bool) (string, error) { func GetUserTag(hall, username string, wildcard bool) (string, error) {
_, etag, err := GetSanitisedUser(group, username, wildcard) _, etag, err := GetSanitisedUser(hall, username, wildcard)
return etag, err return etag, err
} }
func DeleteUser(group, username string, wildcard bool, etag string) error { func DeleteUser(hall, username string, wildcard bool, etag string) error {
if wildcard && username != "" { if wildcard && username != "" {
return errors.New("wildcard with username") return errors.New("wildcard with username")
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
desc, err := readDescription(group, false) desc, err := readDescription(hall, false)
if err != nil { if err != nil {
return err return err
} }
@@ -697,7 +697,7 @@ func DeleteUser(group, username string, wildcard bool, etag string) error {
return rewriteDescriptionFile(desc.FileName, desc) return rewriteDescriptionFile(desc.FileName, desc)
} }
func UpdateUser(group, username string, wildcard bool, etag string, user *UserDescription) error { func UpdateUser(hall, username string, wildcard bool, etag string, user *UserDescription) error {
if wildcard && username != "" { if wildcard && username != "" {
return errors.New("wildcard with username") return errors.New("wildcard with username")
} }
@@ -705,10 +705,10 @@ func UpdateUser(group, username string, wildcard bool, etag string, user *UserDe
return errors.New("user description is not sanitised") return errors.New("user description is not sanitised")
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
desc, err := readDescription(group, false) desc, err := readDescription(hall, false)
if err != nil { if err != nil {
return err return err
} }
@@ -749,15 +749,15 @@ func UpdateUser(group, username string, wildcard bool, etag string, user *UserDe
return rewriteDescriptionFile(desc.FileName, desc) return rewriteDescriptionFile(desc.FileName, desc)
} }
func SetUserPassword(group, username string, wildcard bool, pw Password) error { func SetUserPassword(hall, username string, wildcard bool, pw Password) error {
if wildcard && username != "" { if wildcard && username != "" {
return errors.New("wildcard with username") return errors.New("wildcard with username")
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
desc, err := readDescription(group, false) desc, err := readDescription(hall, false)
if err != nil { if err != nil {
return err return err
} }
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"encoding/json" "encoding/json"
+89 -89
View File
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"encoding/json" "encoding/json"
@@ -98,7 +98,7 @@ const (
MaxBitrate = 1024 * 1024 * 1024 MaxBitrate = 1024 * 1024 * 1024
) )
type Group struct { type Hall struct {
name string name string
mu sync.Mutex mu sync.Mutex
@@ -110,11 +110,11 @@ type Group struct {
data map[string]interface{} data map[string]interface{}
} }
func (g *Group) Name() string { func (g *Hall) Name() string {
return g.name return g.name
} }
func (g *Group) Locked() (bool, string) { func (g *Hall) Locked() (bool, string) {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
if g.locked != nil { if g.locked != nil {
@@ -124,7 +124,7 @@ func (g *Group) Locked() (bool, string) {
} }
} }
func (g *Group) SetLocked(locked bool, message string) { func (g *Hall) SetLocked(locked bool, message string) {
g.mu.Lock() g.mu.Lock()
if locked { if locked {
g.locked = &message g.locked = &message
@@ -139,13 +139,13 @@ func (g *Group) SetLocked(locked bool, message string) {
} }
} }
func (g *Group) Data() map[string]interface{} { func (g *Hall) Data() map[string]interface{} {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return maps.Clone(g.data) return maps.Clone(g.data)
} }
func (g *Group) UpdateData(d map[string]interface{}) { func (g *Hall) UpdateData(d map[string]interface{}) {
g.mu.Lock() g.mu.Lock()
if g.data == nil { if g.data == nil {
g.data = make(map[string]interface{}) g.data = make(map[string]interface{})
@@ -165,19 +165,19 @@ func (g *Group) UpdateData(d map[string]interface{}) {
} }
} }
func (g *Group) Description() *Description { func (g *Hall) Description() *Description {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return g.description return g.description
} }
func (g *Group) ClientCount() int { func (g *Hall) ClientCount() int {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return len(g.clients) return len(g.clients)
} }
func (g *Group) mayExpire() bool { func (g *Hall) mayExpire() bool {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
@@ -190,12 +190,12 @@ func (g *Group) mayExpire() bool {
return time.Since(g.timestamp) > maxHistoryAge(g.description) return time.Since(g.timestamp) > maxHistoryAge(g.description)
} }
var groups struct { var halls struct {
mu sync.Mutex mu sync.Mutex
groups map[string]*Group halls map[string]*Hall
} }
func (g *Group) API() (*webrtc.API, error) { func (g *Hall) API() (*webrtc.API, error) {
g.mu.Lock() g.mu.Lock()
codecs := g.description.Codecs codecs := g.description.Codecs
g.mu.Unlock() g.mu.Unlock()
@@ -432,7 +432,7 @@ func APIFromNames(names []string) (*webrtc.API, error) {
return APIFromCodecs(codecs) return APIFromCodecs(codecs)
} }
func Add(name string, desc *Description) (*Group, error) { func Add(name string, desc *Description) (*Hall, error) {
g, notify, err := add(name, desc) g, notify, err := add(name, desc)
for _, c := range notify { for _, c := range notify {
c.Joined(g.Name(), "change") c.Joined(g.Name(), "change")
@@ -440,7 +440,7 @@ func Add(name string, desc *Description) (*Group, error) {
return g, err return g, err
} }
func validGroupName(name string) bool { func validHallName(name string) bool {
if strings.ContainsRune(name, '\\') { if strings.ContainsRune(name, '\\') {
return false return false
} }
@@ -458,21 +458,21 @@ func validGroupName(name string) bool {
return s == "/"+name return s == "/"+name
} }
func add(name string, desc *Description) (*Group, []Client, error) { func add(name string, desc *Description) (*Hall, []Client, error) {
if !validGroupName(name) { if !validHallName(name) {
return nil, nil, UserError("illegal group name") return nil, nil, UserError("illegal hall name")
} }
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
if groups.groups == nil { if halls.halls == nil {
groups.groups = make(map[string]*Group) halls.halls = make(map[string]*Hall)
} }
var err error var err error
g := groups.groups[name] g := halls.halls[name]
if g == nil { if g == nil {
if desc == nil { if desc == nil {
desc, err = readDescription(name, true) desc, err = readDescription(name, true)
@@ -481,13 +481,13 @@ func add(name string, desc *Description) (*Group, []Client, error) {
} }
} }
g = &Group{ g = &Hall{
name: name, name: name,
description: desc, description: desc,
clients: make(map[string]Client), clients: make(map[string]Client),
timestamp: time.Now(), timestamp: time.Now(),
} }
groups.groups[name] = g halls.halls[name] = g
} }
g.mu.Lock() g.mu.Lock()
@@ -503,7 +503,7 @@ func add(name string, desc *Description) (*Group, []Client, error) {
desc, err = readDescription(name, true) desc, err = readDescription(name, true)
if err != nil { if err != nil {
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
log.Printf("Reading group %v: %v", name, err) log.Printf("Reading hall %v: %v", name, err)
} }
deleteUnlocked(g) deleteUnlocked(g)
return nil, nil, err return nil, nil, err
@@ -521,11 +521,11 @@ func add(name string, desc *Description) (*Group, []Client, error) {
return g, clients, nil return g, clients, nil
} }
func Range(f func(g *Group) bool) { func Range(f func(g *Hall) bool) {
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
for _, g := range groups.groups { for _, g := range halls.halls {
ok := f(g) ok := f(g)
if !ok { if !ok {
break break
@@ -536,7 +536,7 @@ func Range(f func(g *Group) bool) {
func GetNames() []string { func GetNames() []string {
names := make([]string, 0) names := make([]string, 0)
Range(func(g *Group) bool { Range(func(g *Hall) bool {
names = append(names, g.name) names = append(names, g.name)
return true return true
}) })
@@ -552,7 +552,7 @@ func GetSubGroups(parent string) []SubGroup {
prefix := parent + "/" prefix := parent + "/"
subgroups := make([]SubGroup, 0) subgroups := make([]SubGroup, 0)
Range(func(g *Group) bool { Range(func(g *Hall) bool {
if strings.HasPrefix(g.name, prefix) { if strings.HasPrefix(g.name, prefix) {
g.mu.Lock() g.mu.Lock()
count := len(g.clients) count := len(g.clients)
@@ -567,19 +567,19 @@ func GetSubGroups(parent string) []SubGroup {
return subgroups return subgroups
} }
func Get(name string) *Group { func Get(name string) *Hall {
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
if groups.groups == nil { if halls.halls == nil {
return nil return nil
} }
return groups.groups[name] return halls.halls[name]
} }
func Delete(name string) bool { func Delete(name string) bool {
groups.mu.Lock() halls.mu.Lock()
defer groups.mu.Unlock() defer halls.mu.Unlock()
g := groups.groups[name] g := halls.halls[name]
if g == nil { if g == nil {
return false return false
} }
@@ -589,13 +589,13 @@ func Delete(name string) bool {
return deleteUnlocked(g) return deleteUnlocked(g)
} }
// Called with both groups.mu and g.mu taken. // Called with both halls.mu and g.mu taken.
func deleteUnlocked(g *Group) bool { func deleteUnlocked(g *Hall) bool {
if len(g.clients) != 0 { if len(g.clients) != 0 {
return false return false
} }
delete(groups.groups, g.name) delete(halls.halls, g.name)
return true return true
} }
@@ -608,8 +608,8 @@ func member(v string, l []string) bool {
return false return false
} }
func AddClient(group string, c Client, creds ClientCredentials) (*Group, error) { func AddClient(hall string, c Client, creds ClientCredentials) (*Hall, error) {
g, err := Add(group, nil) g, err := Add(hall, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -632,7 +632,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
if g.locked != nil { if g.locked != nil {
m := *g.locked m := *g.locked
if m == "" { if m == "" {
m = "this group is locked" m = "this hall is locked"
} }
return nil, UserError(m) return nil, UserError(m)
} }
@@ -642,13 +642,13 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
if g.description.NotBefore != nil && if g.description.NotBefore != nil &&
g.description.NotBefore.After(now) { g.description.NotBefore.After(now) {
return nil, UserError( return nil, UserError(
"this group is not open yet", "this hall is not open yet",
) )
} }
if g.description.Expires != nil && if g.description.Expires != nil &&
g.description.Expires.Before(now) { g.description.Expires.Before(now) {
return nil, UserError( return nil, UserError(
"this group is closed", "this hall is closed",
) )
} }
} }
@@ -663,7 +663,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
if !ops { if !ops {
return nil, UserError( return nil, UserError(
"there are no operators " + "there are no operators " +
"in this group", "in this hall",
) )
} }
} }
@@ -702,7 +702,7 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
} }
// called locked // called locked
func autoLockKick(g *Group) { func autoLockKick(g *Hall) {
if !(g.description.Autolock && g.locked == nil) && if !(g.description.Autolock && g.locked == nil) &&
!g.description.Autokick { !g.description.Autokick {
return return
@@ -715,7 +715,7 @@ func autoLockKick(g *Group) {
} }
} }
if g.description.Autolock && g.locked == nil { if g.description.Autolock && g.locked == nil {
m := "this group is locked" m := "this hall is locked"
g.locked = &m g.locked = &m
for _, c := range clients { for _, c := range clients {
c.Joined(g.Name(), "change") c.Joined(g.Name(), "change")
@@ -723,14 +723,14 @@ func autoLockKick(g *Group) {
} }
if g.description.Autokick { if g.description.Autokick {
// we cannot call kickall, since it requires the group to // we cannot call kickall, since it requires the hall to
// be unlocked. And calling it asynchronously might // be unlocked. And calling it asynchronously might
// spuriously kick out an operator. // spuriously kick out an operator.
go func(clients []Client) { go func(clients []Client) {
for _, c := range clients { for _, c := range clients {
c.Kick( c.Kick(
"", nil, "", nil,
"there are no operators in this group", "there are no operators in this hall",
) )
} }
}(g.getClientsUnlocked(nil)) }(g.getClientsUnlocked(nil))
@@ -738,7 +738,7 @@ func autoLockKick(g *Group) {
} }
func DelClient(c Client) { func DelClient(c Client) {
g := c.Group() g := c.Hall()
if g == nil { if g == nil {
return return
} }
@@ -762,13 +762,13 @@ func DelClient(c Client) {
autoLockKick(g) autoLockKick(g)
} }
func (g *Group) GetClients(except Client) []Client { func (g *Hall) GetClients(except Client) []Client {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return g.getClientsUnlocked(except) return g.getClientsUnlocked(except)
} }
func (g *Group) getClientsUnlocked(except Client) []Client { func (g *Hall) getClientsUnlocked(except Client) []Client {
clients := make([]Client, 0, len(g.clients)) clients := make([]Client, 0, len(g.clients))
for _, c := range g.clients { for _, c := range g.clients {
if c != except { if c != except {
@@ -778,13 +778,13 @@ func (g *Group) getClientsUnlocked(except Client) []Client {
return clients return clients
} }
func (g *Group) GetClient(id string) Client { func (g *Hall) GetClient(id string) Client {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return g.getClientUnlocked(id) return g.getClientUnlocked(id)
} }
func (g *Group) getClientUnlocked(id string) Client { func (g *Hall) getClientUnlocked(id string) Client {
for idd, c := range g.clients { for idd, c := range g.clients {
if idd == id { if idd == id {
return c return c
@@ -793,7 +793,7 @@ func (g *Group) getClientUnlocked(id string) Client {
return nil return nil
} }
func (g *Group) Range(f func(c Client) bool) { func (g *Hall) Range(f func(c Client) bool) {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
for _, c := range g.clients { for _, c := range g.clients {
@@ -804,7 +804,7 @@ func (g *Group) Range(f func(c Client) bool) {
} }
} }
func kickall(g *Group, message string) { func kickall(g *Hall, message string) {
g.Range(func(c Client) bool { g.Range(func(c Client) bool {
c.Kick("", nil, message) c.Kick("", nil, message)
return true return true
@@ -812,7 +812,7 @@ func kickall(g *Group, message string) {
} }
func Shutdown(message string) { func Shutdown(message string) {
Range(func(g *Group) bool { Range(func(g *Hall) bool {
g.SetLocked(true, message) g.SetLocked(true, message)
kickall(g, message) kickall(g, message)
return true return true
@@ -823,7 +823,7 @@ type warner interface {
Warn(oponly bool, message string) error Warn(oponly bool, message string) error
} }
func (g *Group) WallOps(message string) { func (g *Hall) WallOps(message string) {
clients := g.GetClients(nil) clients := g.GetClients(nil)
for _, c := range clients { for _, c := range clients {
w, ok := c.(warner) w, ok := c.(warner)
@@ -865,7 +865,7 @@ func deleteFunc[S ~[]E, E any](s S, f func(E) bool) S {
return s[:i] return s[:i]
} }
func (g *Group) ClearChatHistory(id string, userId string) { func (g *Hall) ClearChatHistory(id string, userId string) {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
if id == "" && userId == "" { if id == "" && userId == "" {
@@ -877,7 +877,7 @@ func (g *Group) ClearChatHistory(id string, userId string) {
}) })
} }
func (g *Group) AddToChatHistory(id, source string, user *string, time time.Time, kind string, value interface{}) { func (g *Hall) AddToChatHistory(id, source string, user *string, time time.Time, kind string, value interface{}) {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
@@ -905,7 +905,7 @@ func discardObsoleteHistory(h []ChatHistoryEntry, duration time.Duration) []Chat
return h return h
} }
func (g *Group) GetChatHistory() []ChatHistoryEntry { func (g *Hall) GetChatHistory() []ChatHistoryEntry {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
@@ -993,7 +993,7 @@ func GetConfiguration() (*Configuration, error) {
} }
// called locked // called locked
func (g *Group) getPasswordPermission(creds ClientCredentials) (Permissions, error) { func (g *Hall) getPasswordPermission(creds ClientCredentials) (Permissions, error) {
desc := g.description desc := g.description
if creds.Username == nil { if creds.Username == nil {
@@ -1024,14 +1024,14 @@ func (g *Group) getPasswordPermission(creds ClientCredentials) (Permissions, err
// Return true if there is a user entry with the given username. // Return true if there is a user entry with the given username.
// Always return false for an empty username. // Always return false for an empty username.
func (g *Group) UserExists(username string) bool { func (g *Hall) UserExists(username string) bool {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return g.userExists(username) return g.userExists(username)
} }
// called locked // called locked
func (g *Group) userExists(username string) bool { func (g *Hall) userExists(username string) bool {
desc := g.description desc := g.description
if desc.Users == nil { if desc.Users == nil {
return false return false
@@ -1046,11 +1046,11 @@ func (g *Group) userExists(username string) bool {
// usernames might lead to security vulnaribilities. // usernames might lead to security vulnaribilities.
// For now, we just do the minimal validation that avoids path traversal. // For now, we just do the minimal validation that avoids path traversal.
func validUsername(username string) bool { func validUsername(username string) bool {
return username == "" || validGroupName(username) return username == "" || validHallName(username)
} }
// called locked // called locked
func (g *Group) getPermission(creds ClientCredentials) (string, []string, error) { func (g *Hall) getPermission(creds ClientCredentials) (string, []string, error) {
desc := g.description desc := g.description
var username string var username string
var perms []string var perms []string
@@ -1096,7 +1096,7 @@ func (g *Group) getPermission(creds ClientCredentials) (string, []string, error)
return username, perms, nil return username, perms, nil
} }
func (g *Group) GetPermission(creds ClientCredentials) (string, []string, error) { func (g *Hall) GetPermission(creds ClientCredentials) (string, []string, error) {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
return g.getPermission(creds) return g.getPermission(creds)
@@ -1116,10 +1116,10 @@ type Status struct {
CanChangePassword bool `json:"canChangePassword,omitempty"` CanChangePassword bool `json:"canChangePassword,omitempty"`
} }
// Status returns a group's status. // Status returns a hall's status.
// Base is the base URL for groups; if omitted, then both the Location and // Base is the base URL for halls; if omitted, then both the Location and
// Endpoint members are omitted from the result. // Endpoint members are omitted from the result.
func (g *Group) Status(authentified bool, base *url.URL) Status { func (g *Hall) Status(authentified bool, base *url.URL) Status {
desc := g.Description() desc := g.Description()
if desc.Redirect != "" { if desc.Redirect != "" {
@@ -1141,7 +1141,7 @@ func (g *Group) Status(authentified bool, base *url.URL) Status {
Scheme: base.Scheme, Scheme: base.Scheme,
Host: base.Host, Host: base.Host,
Path: path.Join( Path: path.Join(
path.Join(base.Path, "/group/"), path.Join(base.Path, "/hall/"),
g.Name()) + "/", g.Name()) + "/",
} }
location = l.String() location = l.String()
@@ -1181,7 +1181,7 @@ func (g *Group) Status(authentified bool, base *url.URL) Status {
func GetPublic(base *url.URL) []Status { func GetPublic(base *url.URL) []Status {
gs := make([]Status, 0) gs := make([]Status, 0)
Range(func(g *Group) bool { Range(func(g *Hall) bool {
if g.Description().Public { if g.Description().Public {
gs = append(gs, g.Status(false, base)) gs = append(gs, g.Status(false, base))
} }
@@ -1193,9 +1193,9 @@ func GetPublic(base *url.URL) []Status {
return gs return gs
} }
// Update checks that all in-memory groups are up-to-date and updates the // Update checks that all in-memory halls are up-to-date and updates the
// list of public groups. It also removes from memory any non-public // list of public halls. It also removes from memory any non-public
// groups that haven't been accessed in maxHistoryAge. // halls that haven't been accessed in maxHistoryAge.
func Update() { func Update() {
_, err := GetConfiguration() _, err := GetConfiguration()
if err != nil { if err != nil {
@@ -1214,11 +1214,11 @@ func Update() {
deleted := false deleted := false
if g.mayExpire() { if g.mayExpire() {
// Delete checks if the group is still empty // Delete checks if the hall is still empty
deleted = Delete(name) deleted = Delete(name)
} }
// update group description // update hall description
if !deleted { if !deleted {
Add(name, nil) Add(name, nil)
} }
@@ -1228,14 +1228,14 @@ func Update() {
Directory, Directory,
func(path string, d fs.DirEntry, err error) error { func(path string, d fs.DirEntry, err error) error {
if err != nil { if err != nil {
log.Printf("Group file %v: %v", path, err) log.Printf("Hall file %v: %v", path, err)
return nil return nil
} }
if d.IsDir() { if d.IsDir() {
base := filepath.Base(path) base := filepath.Base(path)
if base[0] == '.' { if base[0] == '.' {
log.Printf( log.Printf(
"Ignoring group directory %v", "Ignoring hall directory %v",
path, path,
) )
return fs.SkipDir return fs.SkipDir
@@ -1244,25 +1244,25 @@ func Update() {
} }
filename, err := filepath.Rel(Directory, path) filename, err := filepath.Rel(Directory, path)
if err != nil { if err != nil {
log.Printf("Group file %v: %v", path, err) log.Printf("Hall file %v: %v", path, err)
return nil return nil
} }
if !strings.HasSuffix(filename, ".json") { if !strings.HasSuffix(filename, ".json") {
log.Printf( log.Printf(
"Unexpected extension for group file %v", "Unexpected extension for hall file %v",
path, path,
) )
return nil return nil
} }
base := filepath.Base(filename) base := filepath.Base(filename)
if base[0] == '.' { if base[0] == '.' {
log.Printf("Ignoring group file %v", filename) log.Printf("Ignoring hall file %v", filename)
return nil return nil
} }
name := strings.TrimSuffix(filename, ".json") name := strings.TrimSuffix(filename, ".json")
desc, err := GetDescription(name) desc, err := GetDescription(name)
if err != nil { if err != nil {
log.Printf("Group file %v: %v", path, err) log.Printf("Hall file %v: %v", path, err)
return nil return nil
} }
if desc.Public { if desc.Public {
@@ -1273,6 +1273,6 @@ func Update() {
) )
if err != nil { if err != nil {
log.Printf("Couldn't read groups: %v", err) log.Printf("Couldn't read halls: %v", err)
} }
} }
+19 -19
View File
@@ -1,4 +1,4 @@
package group package hall
import ( import (
"encoding/json" "encoding/json"
@@ -35,21 +35,21 @@ func TestConstantTimeCompare(t *testing.T) {
} }
func TestGroup(t *testing.T) { func TestGroup(t *testing.T) {
groups.groups = nil halls.halls = nil
Add("group", &Description{}) Add("hall", &Description{})
Add("group/subgroup", &Description{Public: true}) Add("hall/subgroup", &Description{Public: true})
if len(groups.groups) != 2 { if len(halls.halls) != 2 {
t.Errorf("Expected 2, got %v", len(groups.groups)) t.Errorf("Expected 2, got %v", len(halls.halls))
} }
g := Get("group") g := Get("hall")
g2 := Get("group/subgroup") g2 := Get("hall/subgroup")
if g == nil { if g == nil {
t.Fatalf("Couldn't get group") t.Fatalf("Couldn't get hall")
} }
if g2 == nil { if g2 == nil {
t.Fatalf("Couldn't get group/subgroup") t.Fatalf("Couldn't get hall/subgroup")
} }
if name := g.Name(); name != "group" { if name := g.Name(); name != "hall" {
t.Errorf("Name: expected group1, got %v", name) t.Errorf("Name: expected group1, got %v", name)
} }
if locked, _ := g.Locked(); locked { if locked, _ := g.Locked(); locked {
@@ -64,17 +64,17 @@ func TestGroup(t *testing.T) {
t.Errorf("Expected 2, got %v", names) t.Errorf("Expected 2, got %v", names)
} }
if subs := GetSubGroups("group"); len(subs) != 0 { if subs := GetSubGroups("hall"); len(subs) != 0 {
t.Errorf("Expected [], got %v", subs) t.Errorf("Expected [], got %v", subs)
} }
if public := GetPublic(nil); len(public) != 1 || public[0].Name != "group/subgroup" { if public := GetPublic(nil); len(public) != 1 || public[0].Name != "hall/subgroup" {
t.Errorf("Expected group/subgroup, got %v", public) t.Errorf("Expected hall/subgroup, got %v", public)
} }
} }
func TestChatHistory(t *testing.T) { func TestChatHistory(t *testing.T) {
g := Group{ g := Hall{
description: &Description{}, description: &Description{},
} }
user := "user" user := "user"
@@ -213,7 +213,7 @@ var goodClients = []credPerm{
} }
func TestPermissions(t *testing.T) { func TestPermissions(t *testing.T) {
var g Group var g Hall
err := json.Unmarshal([]byte(desc2JSON), &g.description) err := json.Unmarshal([]byte(desc2JSON), &g.description)
if err != nil { if err != nil {
t.Fatalf("unmarshal: %v", err) t.Fatalf("unmarshal: %v", err)
@@ -299,7 +299,7 @@ func TestExtraPermissions(t *testing.T) {
} }
func TestUsernameTaken(t *testing.T) { func TestUsernameTaken(t *testing.T) {
var g Group var g Hall
err := json.Unmarshal([]byte(desc2JSON), &g.description) err := json.Unmarshal([]byte(desc2JSON), &g.description)
if err != nil { if err != nil {
t.Fatalf("unmarshal: %v", err) t.Fatalf("unmarshal: %v", err)
@@ -345,7 +345,7 @@ func TestFmtpValue(t *testing.T) {
} }
} }
func TestValidGroupName(t *testing.T) { func TestValidHallName(t *testing.T) {
type nameTest struct { type nameTest struct {
name string name string
result bool result bool
@@ -367,7 +367,7 @@ func TestValidGroupName(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
r := validGroupName(test.name) r := validHallName(test.name)
if r != test.result { if r != test.result {
t.Errorf("Valid %v: got %v, expected %v", t.Errorf("Valid %v: got %v, expected %v",
test.name, r, test.result) test.name, r, test.result)
+17 -17
View File
@@ -17,7 +17,7 @@ import (
"git.stormux.org/storm/skald/codecs" "git.stormux.org/storm/skald/codecs"
"git.stormux.org/storm/skald/conn" "git.stormux.org/storm/skald/conn"
"git.stormux.org/storm/skald/estimator" "git.stormux.org/storm/skald/estimator"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/ice" "git.stormux.org/storm/skald/ice"
"git.stormux.org/storm/skald/jitter" "git.stormux.org/storm/skald/jitter"
"git.stormux.org/storm/skald/packetcache" "git.stormux.org/storm/skald/packetcache"
@@ -193,8 +193,8 @@ func (down *rtpDownConnection) getTracks() []*rtpDownTrack {
return tracks return tracks
} }
func newDownConn(c group.Client, id string, remote conn.Up) (*rtpDownConnection, error) { func newDownConn(c hall.Client, id string, remote conn.Up) (*rtpDownConnection, error) {
api, err := c.Group().API() api, err := c.Hall().API()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -504,7 +504,7 @@ func (up *rtpUpTrack) hasRtcpFb(tpe, parameter string) bool {
type rtpUpConnection struct { type rtpUpConnection struct {
id string id string
client group.Client client hall.Client
label string label string
pc *webrtc.PeerConnection pc *webrtc.PeerConnection
iceCandidates []*webrtc.ICECandidateInit iceCandidates []*webrtc.ICECandidateInit
@@ -599,7 +599,7 @@ func (up *rtpUpConnection) flushICECandidates() error {
} }
// pushConnNow pushes a connection to all of the clients in a group // pushConnNow pushes a connection to all of the clients in a group
func pushConnNow(up *rtpUpConnection, g *group.Group, cs []group.Client) { func pushConnNow(up *rtpUpConnection, g *hall.Hall, cs []hall.Client) {
up.mu.Lock() up.mu.Lock()
up.pushed = true up.pushed = true
replace := up.replace replace := up.replace
@@ -616,12 +616,12 @@ func pushConnNow(up *rtpUpConnection, g *group.Group, cs []group.Client) {
} }
// pushConn schedules a call to pushConnNow // pushConn schedules a call to pushConnNow
func pushConn(up *rtpUpConnection, g *group.Group, cs []group.Client) { func pushConn(up *rtpUpConnection, g *hall.Hall, cs []hall.Client) {
up.mu.Lock() up.mu.Lock()
up.pushed = false up.pushed = false
up.mu.Unlock() up.mu.Unlock()
go func(g *group.Group, cs []group.Client) { go func(g *hall.Hall, cs []hall.Client) {
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
up.mu.Lock() up.mu.Lock()
pushed := up.pushed pushed := up.pushed
@@ -633,14 +633,14 @@ func pushConn(up *rtpUpConnection, g *group.Group, cs []group.Client) {
}(g, cs) }(g, cs)
} }
func newUpConn(c group.Client, id string, label string, offer string) (*rtpUpConnection, error) { func newUpConn(c hall.Client, id string, label string, offer string) (*rtpUpConnection, error) {
var o sdp.SessionDescription var o sdp.SessionDescription
err := o.Unmarshal([]byte(offer)) err := o.Unmarshal([]byte(offer))
if err != nil { if err != nil {
return nil, err return nil, err
} }
api, err := c.Group().API() api, err := c.Hall().API()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -686,10 +686,10 @@ func newUpConn(c group.Client, id string, label string, offer string) (*rtpUpCon
up.mu.Unlock() up.mu.Unlock()
pushConn(up, c.Group(), c.Group().GetClients(c)) pushConn(up, c.Hall(), c.Hall().GetClients(c))
}) })
pushConn(up, c.Group(), c.Group().GetClients(c)) pushConn(up, c.Hall(), c.Hall().GetClients(c))
go rtcpUpSender(up) go rtcpUpSender(up)
return up, nil return up, nil
@@ -890,7 +890,7 @@ func sadd(x, y uint64) uint64 {
func maxUpBitrate(t *rtpUpTrack) uint64 { func maxUpBitrate(t *rtpUpTrack) uint64 {
minrate := ^uint64(0) minrate := ^uint64(0)
maxrate := uint64(group.MinBitrate) maxrate := uint64(hall.MinBitrate)
maxsid := 0 maxsid := 0
maxtid := 0 maxtid := 0
local := t.getLocal() local := t.getLocal()
@@ -902,8 +902,8 @@ func maxUpBitrate(t *rtpUpTrack) uint64 {
if maxtid < tid { if maxtid < tid {
maxtid = tid maxtid = tid
} }
if r < group.MinBitrate { if r < hall.MinBitrate {
r = group.MinBitrate r = hall.MinBitrate
} }
if minrate > r { if minrate > r {
minrate = r minrate = r
@@ -997,14 +997,14 @@ func sendUpRTCP(up *rtpUpConnection) error {
if t.Kind() == webrtc.RTPCodecTypeAudio { if t.Kind() == webrtc.RTPCodecTypeAudio {
rate = sadd(rate, 100*1024) rate = sadd(rate, 100*1024)
} else if t.Label() == "l" { } else if t.Label() == "l" {
rate = sadd(rate, group.LowBitrate) rate = sadd(rate, hall.LowBitrate)
} else { } else {
rate = sadd(rate, maxUpBitrate(t)) rate = sadd(rate, maxUpBitrate(t))
} }
} }
if rate > group.MaxBitrate { if rate > hall.MaxBitrate {
rate = group.MaxBitrate rate = hall.MaxBitrate
} }
if len(ssrcs) > 0 { if len(ssrcs) > 0 {
packets = append(packets, packets = append(packets,
+103 -103
View File
@@ -20,7 +20,7 @@ import (
"git.stormux.org/storm/skald/conn" "git.stormux.org/storm/skald/conn"
"git.stormux.org/storm/skald/diskwriter" "git.stormux.org/storm/skald/diskwriter"
"git.stormux.org/storm/skald/estimator" "git.stormux.org/storm/skald/estimator"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/ice" "git.stormux.org/storm/skald/ice"
"git.stormux.org/storm/skald/token" "git.stormux.org/storm/skald/token"
"git.stormux.org/storm/skald/unbounded" "git.stormux.org/storm/skald/unbounded"
@@ -33,7 +33,7 @@ func errorToWSCloseMessage(id string, err error) (*clientMessage, []byte) {
switch e := err.(type) { switch e := err.(type) {
case *websocket.CloseError: case *websocket.CloseError:
code = websocket.CloseNormalClosure code = websocket.CloseNormalClosure
case group.ProtocolError: case hall.ProtocolError:
code = websocket.CloseProtocolError code = websocket.CloseProtocolError
m = &clientMessage{ m = &clientMessage{
Type: "usermessage", Type: "usermessage",
@@ -43,7 +43,7 @@ func errorToWSCloseMessage(id string, err error) (*clientMessage, []byte) {
Value: e.Error(), Value: e.Error(),
} }
text = e.Error() text = e.Error()
case group.UserError, group.KickError: case hall.UserError, hall.KickError:
code = websocket.CloseNormalClosure code = websocket.CloseNormalClosure
m = errorMessage(id, err) m = errorMessage(id, err)
text = e.Error() text = e.Error()
@@ -60,7 +60,7 @@ func isWSNormalError(err error) bool {
} }
type webClient struct { type webClient struct {
group *group.Group hall *hall.Hall
addr net.Addr addr net.Addr
id string id string
username string username string
@@ -77,8 +77,8 @@ type webClient struct {
up map[string]*rtpUpConnection up map[string]*rtpUpConnection
} }
func (c *webClient) Group() *group.Group { func (c *webClient) Hall() *hall.Hall {
return c.group return c.hall
} }
func (c *webClient) Addr() net.Addr { func (c *webClient) Addr() net.Addr {
@@ -130,7 +130,7 @@ type clientMessage struct {
Token string `json:"token,omitempty"` Token string `json:"token,omitempty"`
Privileged bool `json:"privileged,omitempty"` Privileged bool `json:"privileged,omitempty"`
Permissions []string `json:"permissions,omitempty"` Permissions []string `json:"permissions,omitempty"`
Status *group.Status `json:"status,omitempty"` Status *hall.Status `json:"status,omitempty"`
Data map[string]interface{} `json:"data,omitempty"` Data map[string]interface{} `json:"data,omitempty"`
Group string `json:"group,omitempty"` Group string `json:"group,omitempty"`
Value interface{} `json:"value,omitempty"` Value interface{} `json:"value,omitempty"`
@@ -229,7 +229,7 @@ func delUpConn(c *webClient, id string, userId string, push bool) error {
replace := conn.getReplace(false) replace := conn.getReplace(false)
delete(c.up, id) delete(c.up, id)
g := c.group g := c.hall
c.mu.Unlock() c.mu.Unlock()
conn.mu.Lock() conn.mu.Lock()
@@ -386,9 +386,9 @@ func addDownTrackUnlocked(conn *rtpDownConnection, remoteTrack *rtpUpTrack) erro
// replace the RTCP feedback types with the ones we understand // replace the RTCP feedback types with the ones we understand
remoteCodec := remoteTrack.Codec() remoteCodec := remoteTrack.Codec()
if strings.HasPrefix(strings.ToLower(remoteCodec.MimeType), "video/") { if strings.HasPrefix(strings.ToLower(remoteCodec.MimeType), "video/") {
remoteCodec.RTCPFeedback = group.VideoRTCPFeedback remoteCodec.RTCPFeedback = hall.VideoRTCPFeedback
} else { } else {
remoteCodec.RTCPFeedback = group.AudioRTCPFeedback remoteCodec.RTCPFeedback = hall.AudioRTCPFeedback
} }
local, err := webrtc.NewTrackLocalStaticRTP( local, err := webrtc.NewTrackLocalStaticRTP(
@@ -408,7 +408,7 @@ func addDownTrackUnlocked(conn *rtpDownConnection, remoteTrack *rtpUpTrack) erro
} }
codec := local.Codec() codec := local.Codec()
ptype, err := group.CodecPayloadType(local.Codec()) ptype, err := hall.CodecPayloadType(local.Codec())
if err != nil { if err != nil {
log.Printf("Couldn't determine ptype for codec %v: %v", log.Printf("Couldn't determine ptype for codec %v: %v",
codec.MimeType, err) codec.MimeType, err)
@@ -726,31 +726,31 @@ func parseRequested(r interface{}) (map[string][]string, error) {
} }
func (c *webClient) setRequested(requested map[string][]string) error { func (c *webClient) setRequested(requested map[string][]string) error {
if c.group == nil { if c.hall == nil {
return errors.New("attempted to request with no group joined") return errors.New("attempted to request with no group joined")
} }
c.requested = requested c.requested = requested
requestConns(c, c.group, "") requestConns(c, c.hall, "")
return nil return nil
} }
func (c *webClient) setRequestedStream(down *rtpDownConnection, requested []string) error { func (c *webClient) setRequestedStream(down *rtpDownConnection, requested []string) error {
var remoteClient group.Client var remoteClient hall.Client
remote, ok := down.remote.(*rtpUpConnection) remote, ok := down.remote.(*rtpUpConnection)
if ok { if ok {
remoteClient = remote.client remoteClient = remote.client
} }
down.requested = requested down.requested = requested
return remoteClient.RequestConns(c, c.group, remote.id) return remoteClient.RequestConns(c, c.hall, remote.id)
} }
func (c *webClient) RequestConns(target group.Client, g *group.Group, id string) error { func (c *webClient) RequestConns(target hall.Client, g *hall.Hall, id string) error {
c.action(requestConnsAction{g, target, id}) c.action(requestConnsAction{g, target, id})
return nil return nil
} }
func requestConns(target group.Client, g *group.Group, id string) { func requestConns(target hall.Client, g *hall.Hall, id string) {
clients := g.GetClients(target) clients := g.GetClients(target)
for _, c := range clients { for _, c := range clients {
c.RequestConns(target, g, id) c.RequestConns(target, g, id)
@@ -817,7 +817,7 @@ func requestedTracks(c *webClient, requested []string, tracks []conn.UpTrack) ([
return ts, limitSid return ts, limitSid
} }
func (c *webClient) PushConn(g *group.Group, id string, up conn.Up, tracks []conn.UpTrack, replace string) error { func (c *webClient) PushConn(g *hall.Hall, id string, up conn.Up, tracks []conn.UpTrack, replace string) error {
c.action(pushConnAction{g, id, up, tracks, replace}) c.action(pushConnAction{g, id, up, tracks, replace})
return nil return nil
} }
@@ -854,7 +854,7 @@ func StartClient(conn *websocket.Conn, addr net.Addr) (err error) {
), ),
) )
conn.Close() conn.Close()
err = group.ProtocolError("client didn't handshake") err = hall.ProtocolError("client didn't handshake")
return return
} }
@@ -883,7 +883,7 @@ func StartClient(conn *websocket.Conn, addr net.Addr) (err error) {
m, e := errorToWSCloseMessage(c.id, err) m, e := errorToWSCloseMessage(c.id, err)
if isWSNormalError(err) { if isWSNormalError(err) {
err = nil err = nil
} else if _, ok := err.(group.KickError); ok { } else if _, ok := err.(hall.KickError); ok {
err = nil err = nil
} }
if m != nil { if m != nil {
@@ -896,7 +896,7 @@ func StartClient(conn *websocket.Conn, addr net.Addr) (err error) {
} }
type pushConnAction struct { type pushConnAction struct {
group *group.Group hall *hall.Hall
id string id string
conn conn.Up conn conn.Up
tracks []conn.UpTrack tracks []conn.UpTrack
@@ -904,8 +904,8 @@ type pushConnAction struct {
} }
type requestConnsAction struct { type requestConnsAction struct {
group *group.Group hall *hall.Hall
target group.Client target hall.Client
id string id string
} }
@@ -939,7 +939,7 @@ type kickAction struct {
message string message string
} }
var errEmptyId = group.ProtocolError("empty id") var errEmptyId = hall.ProtocolError("empty id")
func member(v string, l []string) bool { func member(v string, l []string) bool {
for _, w := range l { for _, w := range l {
@@ -1108,14 +1108,14 @@ func pushDownConn(c *webClient, id string, up conn.Up, tracks []conn.UpTrack, re
func handleAction(c *webClient, a any) error { func handleAction(c *webClient, a any) error {
switch a := a.(type) { switch a := a.(type) {
case pushConnAction: case pushConnAction:
if c.group == nil || c.group != a.group { if c.hall == nil || c.hall != a.hall {
log.Printf("Got connectsions for wrong group") log.Printf("Got connectsions for wrong group")
return nil return nil
} }
return pushDownConn(c, a.id, a.conn, a.tracks, a.replace) return pushDownConn(c, a.id, a.conn, a.tracks, a.replace)
case requestConnsAction: case requestConnsAction:
g := c.group g := c.hall
if g == nil || a.group != g { if g == nil || a.hall != g {
log.Printf("Misdirected pushConns") log.Printf("Misdirected pushConns")
return nil return nil
} }
@@ -1148,7 +1148,7 @@ func handleAction(c *webClient, a any) error {
tracks[i] = t.remote tracks[i] = t.remote
} }
c.PushConn( c.PushConn(
c.group, c.hall,
down.remote.Id(), down.remote, down.remote.Id(), down.remote,
tracks, "", tracks, "",
) )
@@ -1163,8 +1163,8 @@ func handleAction(c *webClient, a any) error {
} }
case pushClientAction: case pushClientAction:
if a.group != c.group.Name() { if a.group != c.hall.Name() {
log.Printf("got client for wrong group") log.Printf("got client for wrong hall")
return nil return nil
} }
perms := append([]string(nil), a.permissions...) perms := append([]string(nil), a.permissions...)
@@ -1178,11 +1178,11 @@ func handleAction(c *webClient, a any) error {
Data: a.data, Data: a.data,
}) })
case joinedAction: case joinedAction:
var status *group.Status var status *hall.Status
var data map[string]interface{} var data map[string]interface{}
var g *group.Group var g *hall.Hall
if a.group != "" { if a.group != "" {
g = group.Get(a.group) g = hall.Get(a.group)
if g != nil { if g != nil {
s := g.Status(true, nil) s := g.Status(true, nil)
status = &s status = &s
@@ -1230,7 +1230,7 @@ func handleAction(c *webClient, a any) error {
switch a.kind { switch a.kind {
case "op": case "op":
c.permissions = addnew("op", c.permissions) c.permissions = addnew("op", c.permissions)
g := c.Group() g := c.Hall()
if g != nil && g.Description().AllowRecording { if g != nil && g.Description().AllowRecording {
c.permissions = addnew("record", c.permissions) c.permissions = addnew("record", c.permissions)
} }
@@ -1246,11 +1246,11 @@ func handleAction(c *webClient, a any) error {
case "unshutup": case "unshutup":
c.permissions = addnew("message", c.permissions) c.permissions = addnew("message", c.permissions)
default: default:
return group.UserError("unknown permission") return hall.UserError("unknown permission")
} }
c.action(permissionsChangedAction{}) c.action(permissionsChangedAction{})
case permissionsChangedAction: case permissionsChangedAction:
g := c.Group() g := c.Hall()
if g == nil { if g == nil {
return errors.New("Permissions changed in no group") return errors.New("Permissions changed in no group")
} }
@@ -1284,7 +1284,7 @@ func handleAction(c *webClient, a any) error {
user := c.Username() user := c.Username()
d := c.Data() d := c.Data()
clients := g.GetClients(nil) clients := g.GetClients(nil)
go func(clients []group.Client) { go func(clients []hall.Client) {
for _, cc := range clients { for _, cc := range clients {
cc.PushClient( cc.PushClient(
g.Name(), "change", id, user, perms, d, g.Name(), "change", id, user, perms, d,
@@ -1292,7 +1292,7 @@ func handleAction(c *webClient, a any) error {
} }
}(clients) }(clients)
case kickAction: case kickAction:
return group.KickError{ return hall.KickError{
a.id, a.username, a.message, a.id, a.username, a.message,
} }
default: default:
@@ -1313,7 +1313,7 @@ func failUpConnection(c *webClient, id string, message string) error {
} }
} }
if message != "" { if message != "" {
err := c.error(group.UserError(message)) err := c.error(hall.UserError(message))
if err != nil { if err != nil {
return err return err
} }
@@ -1322,7 +1322,7 @@ func failUpConnection(c *webClient, id string, message string) error {
} }
func leaveGroup(c *webClient) { func leaveGroup(c *webClient) {
if c.group == nil { if c.hall == nil {
return return
} }
@@ -1337,11 +1337,11 @@ func leaveGroup(c *webClient) {
} }
} }
group.DelClient(c) hall.DelClient(c)
c.permissions = nil c.permissions = nil
c.data = nil c.data = nil
c.requested = make(map[string][]string) c.requested = make(map[string][]string)
c.group = nil c.hall = nil
} }
func closeDownConn(c *webClient, id string, message string) error { func closeDownConn(c *webClient, id string, message string) error {
@@ -1357,7 +1357,7 @@ func closeDownConn(c *webClient, id string, message string) error {
return err return err
} }
if message != "" { if message != "" {
err := c.error(group.UserError(message)) err := c.error(hall.UserError(message))
if err != nil { if err != nil {
return err return err
} }
@@ -1375,10 +1375,10 @@ func (c *webClient) Joined(group, kind string) error {
return nil return nil
} }
func kickClient(g *group.Group, id string, user *string, dest string, message string) error { func kickClient(g *hall.Hall, id string, user *string, dest string, message string) error {
client := g.GetClient(dest) client := g.GetClient(dest)
if client == nil { if client == nil {
return group.UserError("no such user") return hall.UserError("no such user")
} }
return client.Kick(id, user, message) return client.Kick(id, user, message)
@@ -1387,14 +1387,14 @@ func kickClient(g *group.Group, id string, user *string, dest string, message st
func handleClientMessage(c *webClient, m clientMessage) error { func handleClientMessage(c *webClient, m clientMessage) error {
if m.Source != "" { if m.Source != "" {
if m.Source != c.Id() { if m.Source != c.Id() {
return group.ProtocolError("spoofed client id") return hall.ProtocolError("spoofed client id")
} }
} }
if m.Type != "join" { if m.Type != "join" {
if m.Username != nil { if m.Username != nil {
if *m.Username != c.Username() { if *m.Username != c.Username() {
return group.ProtocolError("spoofed username") return hall.ProtocolError("spoofed username")
} }
} }
} }
@@ -1402,25 +1402,25 @@ func handleClientMessage(c *webClient, m clientMessage) error {
switch m.Type { switch m.Type {
case "join": case "join":
if m.Kind == "leave" { if m.Kind == "leave" {
if c.group == nil || c.group.Name() != m.Group { if c.hall == nil || c.hall.Name() != m.Group {
return group.UserError("you are not joined") return hall.UserError("you are not joined")
} }
leaveGroup(c) leaveGroup(c)
return nil return nil
} }
if m.Kind != "join" { if m.Kind != "join" {
return group.ProtocolError("unknown kind") return hall.ProtocolError("unknown kind")
} }
if c.group != nil { if c.hall != nil {
return group.ProtocolError( return hall.ProtocolError(
"cannot join multiple groups", "cannot join multiple groups",
) )
} }
c.data = m.Data c.data = m.Data
g, err := group.AddClient(m.Group, c, g, err := hall.AddClient(m.Group, c,
group.ClientCredentials{ hall.ClientCredentials{
Username: m.Username, Username: m.Username,
Password: m.Password, Password: m.Password,
Token: m.Token, Token: m.Token,
@@ -1428,11 +1428,11 @@ func handleClientMessage(c *webClient, m clientMessage) error {
) )
if err != nil { if err != nil {
var e, s string var e, s string
var autherr *group.NotAuthorisedError var autherr *hall.NotAuthorisedError
if errors.Is(err, token.ErrUsernameRequired) { if errors.Is(err, token.ErrUsernameRequired) {
s = err.Error() s = err.Error()
e = "need-username" e = "need-username"
} else if errors.Is(err, group.ErrDuplicateUsername) { } else if errors.Is(err, hall.ErrDuplicateUsername) {
s = err.Error() s = err.Error()
e = "duplicate-username" e = "duplicate-username"
} else if errors.As(err, &autherr) { } else if errors.As(err, &autherr) {
@@ -1441,7 +1441,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
log.Printf("Join group: %v", err) log.Printf("Join group: %v", err)
} else if errors.Is(err, os.ErrNotExist) { } else if errors.Is(err, os.ErrNotExist) {
s = "group does not exist" s = "group does not exist"
} else if _, ok := err.(group.UserError); ok { } else if _, ok := err.(hall.UserError); ok {
s = err.Error() s = err.Error()
} else { } else {
s = "internal server error" s = "internal server error"
@@ -1469,7 +1469,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Value: redirect, Value: redirect,
}) })
} }
c.group = g c.hall = g
case "request": case "request":
requested, err := parseRequested(m.Request) requested, err := parseRequested(m.Request)
if err != nil { if err != nil {
@@ -1498,7 +1498,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Type: "abort", Type: "abort",
Id: m.Id, Id: m.Id,
}) })
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
err := gotOffer(c, m.Id, m.Label, m.SDP, m.Replace) err := gotOffer(c, m.Id, m.Label, m.SDP, m.Replace)
if err != nil { if err != nil {
@@ -1565,16 +1565,16 @@ func handleClientMessage(c *webClient, m clientMessage) error {
return errEmptyId return errEmptyId
} }
if m.Candidate == nil { if m.Candidate == nil {
return group.ProtocolError("null candidate") return hall.ProtocolError("null candidate")
} }
err := gotICE(c, m.Candidate, m.Id) err := gotICE(c, m.Candidate, m.Id)
if err != nil { if err != nil {
log.Printf("ICE: %v", err) log.Printf("ICE: %v", err)
} }
case "chat", "usermessage": case "chat", "usermessage":
g := c.group g := c.hall
if g == nil { if g == nil {
return c.error(group.UserError("join a group first")) return c.error(hall.UserError("join a group first"))
} }
required := "message" required := "message"
@@ -1582,7 +1582,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
required = "caption" required = "caption"
} }
if !member(required, c.permissions) { if !member(required, c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
id := m.Id id := m.Id
@@ -1614,7 +1614,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Value: m.Value, Value: m.Value,
} }
if m.Dest == "" { if m.Dest == "" {
var except group.Client var except hall.Client
if m.NoEcho { if m.NoEcho {
except = c except = c
} }
@@ -1625,38 +1625,38 @@ func handleClientMessage(c *webClient, m clientMessage) error {
} else { } else {
cc := g.GetClient(m.Dest) cc := g.GetClient(m.Dest)
if cc == nil { if cc == nil {
return c.error(group.UserError("user unknown")) return c.error(hall.UserError("user unknown"))
} }
ccc, ok := cc.(*webClient) ccc, ok := cc.(*webClient)
if !ok { if !ok {
return c.error(group.UserError( return c.error(hall.UserError(
"this user doesn't chat", "this user doesn't chat",
)) ))
} }
ccc.write(mm) ccc.write(mm)
} }
case "groupaction": case "groupaction":
g := c.group g := c.hall
if g == nil { if g == nil {
return c.error(group.UserError("join a group first")) return c.error(hall.UserError("join a group first"))
} }
switch m.Kind { switch m.Kind {
case "clearchat": case "clearchat":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
var id, userId string var id, userId string
if m.Value != nil { if m.Value != nil {
value, ok := m.Value.(map[string]any) value, ok := m.Value.(map[string]any)
if !ok { if !ok {
return c.error(group.UserError( return c.error(hall.UserError(
"bad value in clearchat", "bad value in clearchat",
)) ))
} }
id, _ = value["id"].(string) id, _ = value["id"].(string)
userId, _ = value["userId"].(string) userId, _ = value["userId"].(string)
if userId == "" && id != "" { if userId == "" && id != "" {
return c.error(group.UserError( return c.error(hall.UserError(
"bad value in clearchat", "bad value in clearchat",
)) ))
} }
@@ -1674,7 +1674,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
} }
case "lock", "unlock": case "lock", "unlock":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
message := "" message := ""
v, ok := m.Value.(string) v, ok := m.Value.(string)
@@ -1684,17 +1684,17 @@ func handleClientMessage(c *webClient, m clientMessage) error {
g.SetLocked(m.Kind == "lock", message) g.SetLocked(m.Kind == "lock", message)
case "record": case "record":
if !member("record", c.permissions) { if !member("record", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
for _, cc := range g.GetClients(c) { for _, cc := range g.GetClients(c) {
_, ok := cc.(*diskwriter.Client) _, ok := cc.(*diskwriter.Client)
if ok { if ok {
return c.error(group.UserError("already recording")) return c.error(hall.UserError("already recording"))
} }
} }
disk := diskwriter.New(g) disk := diskwriter.New(g)
_, err := group.AddClient(g.Name(), disk, _, err := hall.AddClient(g.Name(), disk,
group.ClientCredentials{ hall.ClientCredentials{
System: true, System: true,
}, },
) )
@@ -1702,24 +1702,24 @@ func handleClientMessage(c *webClient, m clientMessage) error {
disk.Close() disk.Close()
return c.error(err) return c.error(err)
} }
requestConns(disk, c.group, "") requestConns(disk, c.hall, "")
case "unrecord": case "unrecord":
if !member("record", c.permissions) { if !member("record", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
for _, cc := range g.GetClients(c) { for _, cc := range g.GetClients(c) {
disk, ok := cc.(*diskwriter.Client) disk, ok := cc.(*diskwriter.Client)
if ok { if ok {
disk.Close() disk.Close()
group.DelClient(disk) hall.DelClient(disk)
} }
} }
case "subgroups": case "subgroups":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
s := "" s := ""
for _, sg := range group.GetSubGroups(g.Name()) { for _, sg := range hall.GetSubGroups(g.Name()) {
plural := "" plural := ""
if sg.Clients > 1 { if sg.Clients > 1 {
plural = "s" plural = "s"
@@ -1737,11 +1737,11 @@ func handleClientMessage(c *webClient, m clientMessage) error {
}) })
case "setdata": case "setdata":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
data, ok := m.Value.(map[string]interface{}) data, ok := m.Value.(map[string]interface{})
if !ok { if !ok {
return c.error(group.UserError( return c.error(hall.UserError(
"Bad value in setdata", "Bad value in setdata",
)) ))
} }
@@ -1773,7 +1773,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
return terror("error", "client specified token") return terror("error", "client specified token")
} }
if tok.Group != c.group.Name() { if tok.Group != c.hall.Name() {
return terror("error", "wrong group in token") return terror("error", "wrong group in token")
} }
if tok.IncludeSubgroups { if tok.IncludeSubgroups {
@@ -1786,7 +1786,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
} }
if tok.Username != nil && if tok.Username != nil &&
c.group.UserExists(*tok.Username) { c.hall.UserExists(*tok.Username) {
return terror("error", "that username is taken") return terror("error", "that username is taken")
} }
@@ -1881,7 +1881,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
!member("token", c.permissions) { !member("token", c.permissions) {
return terror("not-authorised", "not authorised") return terror("not-authorised", "not authorised")
} }
tokens, _, err := token.List(c.group.Name()) tokens, _, err := token.List(c.hall.Name())
if err != nil { if err != nil {
return terror("error", err.Error()) return terror("error", err.Error())
} }
@@ -1892,37 +1892,37 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Value: tokens, Value: tokens,
}) })
default: default:
return group.UserError("unknown group action") return hall.UserError("unknown group action")
} }
case "useraction": case "useraction":
g := c.group g := c.hall
if g == nil { if g == nil {
return c.error(group.UserError("join a group first")) return c.error(hall.UserError("join a group first"))
} }
switch m.Kind { switch m.Kind {
case "op", "unop", "present", "unpresent", "shutup", "unshutup": case "op", "unop", "present", "unpresent", "shutup", "unshutup":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
t := g.GetClient(m.Dest) t := g.GetClient(m.Dest)
if t == nil { if t == nil {
return c.error(group.UserError("no such user")) return c.error(hall.UserError("no such user"))
} }
target, ok := t.(*webClient) target, ok := t.(*webClient)
if !ok { if !ok {
return c.error(group.UserError( return c.error(hall.UserError(
"this is not a real user", "this is not a real user",
)) ))
} }
target.action(changePermissionsAction{m.Kind}) target.action(changePermissionsAction{m.Kind})
case "identify": case "identify":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
d := g.GetClient(m.Dest) d := g.GetClient(m.Dest)
if d == nil { if d == nil {
return c.error( return c.error(
group.UserError("client not found"), hall.UserError("client not found"),
) )
} }
value := make(map[string]any) value := make(map[string]any)
@@ -1950,7 +1950,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
}) })
case "kick": case "kick":
if !member("op", c.permissions) { if !member("op", c.permissions) {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
message := "" message := ""
v, ok := m.Value.(string) v, ok := m.Value.(string)
@@ -1963,11 +1963,11 @@ func handleClientMessage(c *webClient, m clientMessage) error {
} }
case "setdata": case "setdata":
if m.Dest != c.Id() { if m.Dest != c.Id() {
return c.error(group.UserError("not authorised")) return c.error(hall.UserError("not authorised"))
} }
data, ok := m.Value.(map[string]interface{}) data, ok := m.Value.(map[string]interface{})
if !ok { if !ok {
return c.error(group.UserError( return c.error(hall.UserError(
"Bad value in setdata", "Bad value in setdata",
)) ))
} }
@@ -1985,7 +1985,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
user := c.Username() user := c.Username()
perms := c.Permissions() perms := c.Permissions()
data = c.Data() data = c.Data()
go func(clients []group.Client) { go func(clients []hall.Client) {
for _, cc := range clients { for _, cc := range clients {
cc.PushClient( cc.PushClient(
g.Name(), "change", g.Name(), "change",
@@ -1994,7 +1994,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
} }
}(g.GetClients(nil)) }(g.GetClients(nil))
default: default:
return group.UserError("unknown user action") return hall.UserError("unknown user action")
} }
case "pong": case "pong":
// nothing // nothing
@@ -2004,7 +2004,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
}) })
default: default:
log.Printf("unexpected message: %v", m.Type) log.Printf("unexpected message: %v", m.Type)
return group.ProtocolError("unexpected message") return hall.ProtocolError("unexpected message")
} }
return nil return nil
} }
@@ -2199,7 +2199,7 @@ func (c *webClient) write(m clientMessage) error {
} }
} }
func broadcast(cs []group.Client, m clientMessage) error { func broadcast(cs []hall.Client, m clientMessage) error {
b, err := json.Marshal(m) b, err := json.Marshal(m)
if err != nil { if err != nil {
return err return err
@@ -2228,7 +2228,7 @@ func (c *webClient) close(data []byte) error {
func errorMessage(id string, err error) *clientMessage { func errorMessage(id string, err error) *clientMessage {
switch e := err.(type) { switch e := err.(type) {
case group.UserError: case hall.UserError:
return &clientMessage{ return &clientMessage{
Type: "usermessage", Type: "usermessage",
Kind: "error", Kind: "error",
@@ -2236,7 +2236,7 @@ func errorMessage(id string, err error) *clientMessage {
Privileged: true, Privileged: true,
Value: e.Error(), Value: e.Error(),
} }
case group.KickError: case hall.KickError:
message := e.Message message := e.Message
if message == "" { if message == "" {
message = "you have been kicked out" message = "you have been kicked out"
+12 -12
View File
@@ -7,7 +7,7 @@ import (
"sync" "sync"
"git.stormux.org/storm/skald/conn" "git.stormux.org/storm/skald/conn"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/sdpfrag" "git.stormux.org/storm/skald/sdpfrag"
"github.com/pion/sdp/v3" "github.com/pion/sdp/v3"
@@ -15,7 +15,7 @@ import (
) )
type WhipClient struct { type WhipClient struct {
group *group.Group hall *hall.Hall
addr net.Addr addr net.Addr
id string id string
token string token string
@@ -27,12 +27,12 @@ type WhipClient struct {
etag string etag string
} }
func NewWhipClient(g *group.Group, id string, token string, addr net.Addr) *WhipClient { func NewWhipClient(g *hall.Hall, id string, token string, addr net.Addr) *WhipClient {
return &WhipClient{group: g, id: id, token: token, addr: addr} return &WhipClient{hall: g, id: id, token: token, addr: addr}
} }
func (c *WhipClient) Group() *group.Group { func (c *WhipClient) Hall() *hall.Hall {
return c.group return c.hall
} }
func (c *WhipClient) Addr() net.Addr { func (c *WhipClient) Addr() net.Addr {
@@ -83,12 +83,12 @@ func (c *WhipClient) SetETag(etag string) {
c.etag = etag c.etag = etag
} }
func (c *WhipClient) PushConn(g *group.Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error { func (c *WhipClient) PushConn(g *hall.Hall, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error {
return nil return nil
} }
func (c *WhipClient) RequestConns(target group.Client, g *group.Group, id string) error { func (c *WhipClient) RequestConns(target hall.Client, g *hall.Hall, id string) error {
if g != c.group { if g != c.hall {
return nil return nil
} }
@@ -122,7 +122,7 @@ func (c *WhipClient) Kick(id string, user *string, message string) error {
func (c *WhipClient) Close() error { func (c *WhipClient) Close() error {
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
g := c.group g := c.hall
if g == nil { if g == nil {
return nil return nil
} }
@@ -136,8 +136,8 @@ func (c *WhipClient) Close() error {
} }
c.connection = nil c.connection = nil
} }
group.DelClient(c) hall.DelClient(c)
c.group = nil c.hall = nil
return nil return nil
} }
+3 -3
View File
@@ -1,6 +1,6 @@
# Skald's administrative API # Skald's administrative API
Skald provides an HTTP-based API that can be used to create groups and Skald provides an HTTP-based API that can be used to create halls and
users. For example, in order to create a hall, a client may do users. For example, in order to create a hall, a client may do
PUT /skald-api/v0/.halls/hallname/ PUT /skald-api/v0/.halls/hallname/
@@ -39,11 +39,11 @@ Provides a number of statistics about the running server, in JSON. The
exact format is undocumented, and may change between versions. The only exact format is undocumented, and may change between versions. The only
allowed methods are HEAD and GET. allowed methods are HEAD and GET.
### List of groups ### List of halls
/skald-api/v0/.halls/ /skald-api/v0/.halls/
Returns a list of groups, as a JSON array. The only allowed methods are Returns a list of halls, as a JSON array. The only allowed methods are
HEAD and GET. HEAD and GET.
### Group definition ### Group definition
+4 -4
View File
@@ -56,17 +56,17 @@ The following instructions assume that your server is called
`skald.example.org` and that you have already created a dedicated user `skald.example.org` and that you have already created a dedicated user
called `skald`. called `skald`.
First, make sure that the `groups` and `data` directories exist: First, make sure that the `halls` and `data` directories exist:
```sh ```sh
mkdir groups data mkdir halls data
``` ```
Now copy the `skald` binary, and the directories `static`, `data` and Now copy the `skald` binary, and the directories `static`, `data` and
`groups` to the server: `halls` to the server:
```sh ```sh
rsync -a skald static data groups skald@skald.example.org: rsync -a skald static data halls skald@skald.example.org:
``` ```
If you don't have a TLS certificate, Skald will generate a self-signed If you don't have a TLS certificate, Skald will generate a self-signed
+14 -14
View File
@@ -15,7 +15,7 @@ import (
"time" "time"
"git.stormux.org/storm/skald/diskwriter" "git.stormux.org/storm/skald/diskwriter"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/ice" "git.stormux.org/storm/skald/ice"
"git.stormux.org/storm/skald/limit" "git.stormux.org/storm/skald/limit"
"git.stormux.org/storm/skald/token" "git.stormux.org/storm/skald/token"
@@ -32,10 +32,10 @@ func main() {
"web server root `directory`") "web server root `directory`")
flag.BoolVar(&webserver.Insecure, "insecure", false, flag.BoolVar(&webserver.Insecure, "insecure", false,
"act as an HTTP server rather than HTTPS") "act as an HTTP server rather than HTTPS")
flag.StringVar(&group.DataDirectory, "data", "./data/", flag.StringVar(&hall.DataDirectory, "data", "./data/",
"data `directory`") "data `directory`")
flag.StringVar(&group.Directory, "groups", "./groups/", flag.StringVar(&hall.Directory, "halls", "./halls/",
"group description `directory`") "hall description `directory`")
flag.StringVar(&diskwriter.Directory, "recordings", "./recordings/", flag.StringVar(&diskwriter.Directory, "recordings", "./recordings/",
"recordings `directory`") "recordings `directory`")
flag.StringVar(&cpuprofile, "cpuprofile", "", flag.StringVar(&cpuprofile, "cpuprofile", "",
@@ -46,7 +46,7 @@ func main() {
"store mutex profile in `file`") "store mutex profile in `file`")
flag.StringVar(&udpRange, "udp-range", "", flag.StringVar(&udpRange, "udp-range", "",
"UDP `port` (multiplexing) or port1-port2 (range)") "UDP `port` (multiplexing) or port1-port2 (range)")
flag.BoolVar(&group.UseMDNS, "mdns", false, "gather mDNS addresses") flag.BoolVar(&hall.UseMDNS, "mdns", false, "gather mDNS addresses")
flag.BoolVar(&ice.ICERelayOnly, "relay-only", false, flag.BoolVar(&ice.ICERelayOnly, "relay-only", false,
"require use of TURN relays for all media traffic") "require use of TURN relays for all media traffic")
flag.StringVar(&turnserver.Address, "turn", "auto", flag.StringVar(&turnserver.Address, "turn", "auto",
@@ -63,14 +63,14 @@ func main() {
if n != 2 || min <= 0 || max <= 0 || min > max { if n != 2 || min <= 0 || max <= 0 || min > max {
log.Fatalf("UDP range: bad range") log.Fatalf("UDP range: bad range")
} }
group.UDPMin = min hall.UDPMin = min
group.UDPMax = max hall.UDPMax = max
} else { } else {
port, err := strconv.Atoi(udpRange) port, err := strconv.Atoi(udpRange)
if err != nil { if err != nil {
log.Fatalf("UDP: %v", err) log.Fatalf("UDP: %v", err)
} }
err = group.SetUDPMux(port) err = hall.SetUDPMux(port)
if err != nil { if err != nil {
log.Fatalf("UDP: %v", err) log.Fatalf("UDP: %v", err)
} }
@@ -122,22 +122,22 @@ func main() {
log.Printf("File descriptor limit is %v, please increase it!", n) log.Printf("File descriptor limit is %v, please increase it!", n)
} }
ice.ICEFilename = filepath.Join(group.DataDirectory, "ice-servers.json") ice.ICEFilename = filepath.Join(hall.DataDirectory, "ice-servers.json")
token.SetStatefulFilename( token.SetStatefulFilename(
filepath.Join( filepath.Join(
filepath.Join(group.DataDirectory, "var"), filepath.Join(hall.DataDirectory, "var"),
"tokens.jsonl", "tokens.jsonl",
), ),
) )
// make sure the list of public groups is updated early // make sure the list of public halls is updated early
go group.Update() go hall.Update()
// causes the built-in server to start if required // causes the built-in server to start if required
ice.Update() ice.Update()
defer turnserver.Stop() defer turnserver.Stop()
err = webserver.Serve(httpAddr, group.DataDirectory) err = webserver.Serve(httpAddr, hall.DataDirectory)
if err != nil { if err != nil {
log.Fatalf("Server: %v", err) log.Fatalf("Server: %v", err)
} }
@@ -157,7 +157,7 @@ func main() {
select { select {
case <-ticker.C: case <-ticker.C:
go func() { go func() {
group.Update() hall.Update()
token.Expire() token.Expire()
}() }()
case <-slowTicker.C: case <-slowTicker.C:
+29 -29
View File
@@ -177,23 +177,23 @@ fields are as follows:
will be redirected to the canonical one. will be redirected to the canonical one.
## Group definitions ## Hall definitions
Groups are described by JSON files in the `groups/` directory. These Halls are described by JSON files in the `halls/` directory. These
files are normally administered using the `skaldctl` utility, but may files are normally administered using the `skaldctl` utility, but may
also be edited manually (there is no need to restart the server). also be edited manually (there is no need to restart the server).
### Managing groups using `skaldctl` ### Managing halls using `skaldctl`
#### Creating, modifying, and deleting groups #### Creating, modifying, and deleting halls
A group is created using `skaldctl create-hall`: A hall is created using `skaldctl create-hall`:
```sh ```sh
skaldctl create-hall -hall city-watch skaldctl create-hall -hall city-watch
``` ```
There are a number of options to customise the behaviour of the group, see There are a number of options to customise the behaviour of the hall, see
`skaldctl create-hall -help` for a full list. For example, in order to `skaldctl create-hall -help` for a full list. For example, in order to
create a hall that allows unrestricted creation of tokens, say: create a hall that allows unrestricted creation of tokens, say:
@@ -203,30 +203,30 @@ skaldctl create-hall -hall city-watch -unrestricted-tokens
For more advanced configuration, `skaldctl create-hall` can be invoked For more advanced configuration, `skaldctl create-hall` can be invoked
with the `-json` flag, in which case it takes a JSON template from standard with the `-json` flag, in which case it takes a JSON template from standard
input. The syntax of a JSON template is just like that of a group input. The syntax of a JSON template is just like that of a hall
definition file (see below), except that it must not contain the fields definition file (see below), except that it must not contain the fields
`users` and `wildcard-user`. For example, in order to create a redirect `users` and `wildcard-user`. For example, in order to create a redirect
(see the section *Group description reference* below): (see the section *Hall description reference* below):
```sh ```sh
echo '{"redirect": "https://skald.example.org:8443/hall/city-watch/"}' | skaldctl create-hall -hall amcw -json echo '{"redirect": "https://skald.example.org:8443/hall/city-watch/"}' | skaldctl create-hall -hall amcw -json
``` ```
Groups are modified using `skaldctl update-hall`: Halls are modified using `skaldctl update-hall`:
```sh ```sh
skaldctl update-hall -hall city-watch -unrestricted-tokens=false skaldctl update-hall -hall city-watch -unrestricted-tokens=false
``` ```
If a JSON template is provided to `skaldctl update-hall`, then it is If a JSON template is provided to `skaldctl update-hall`, then it is
merged with the existing group configuration. Entries may be deleted merged with the existing hall configuration. Entries may be deleted
by setting them to `null` in the template: by setting them to `null` in the template:
```sh ```sh
echo '{"redirect": null}' | skaldctl update-hall -hall amcw echo '{"redirect": null}' | skaldctl update-hall -hall amcw
``` ```
A group is deleted using `skaldctl delete-hall`: A hall is deleted using `skaldctl delete-hall`:
```sh ```sh
skaldctl delete-hall -hall amcw skaldctl delete-hall -hall amcw
@@ -266,7 +266,7 @@ skaldctl create-user -hall city-watch -wildcard
skaldctl set-password -hall city-watch -wildcard skaldctl set-password -hall city-watch -wildcard
``` ```
For open groups, where any user can login with any password, the wildcard For open halls, where any user can login with any password, the wildcard
user's password is set to the password of type `wildcard`: user's password is set to the password of type `wildcard`:
```sh ```sh
@@ -278,17 +278,17 @@ password types.
#### Automatic subgroups #### Automatic subgroups
It is sometimes necessary to create a large number of identical groups. It is sometimes necessary to create a large number of identical halls.
For example, the author has been using Skald to supervise computer For example, the author has been using Skald to supervise computer
science practicals, where up to 40 students are working in groups of two. science practicals, where up to 40 students are working in pairs.
While it is possible to automate the creation of groups by accessing While it is possible to automate the creation of halls by accessing
Skald's API, by scripting calls to skaldctl, or by generating files Skald's API, by scripting calls to skaldctl, or by generating files
directly under `groups/`, Skald provides a facility called *automatic directly under `halls/`, Skald provides a facility called *automatic
subgroups* that can be used to generate groups on demand. subgroups* that can be used to generate halls on demand.
Automatic subgroups are enabled by setting the `"auto-subgroups"` Automatic subgroups are enabled by setting the `"auto-subgroups"`
field in the group description: field in the hall description:
```sh ```sh
skaldctl create-hall unseen-university -auto-subgroups skaldctl create-hall unseen-university -auto-subgroups
@@ -297,7 +297,7 @@ skaldctl create-hall unseen-university -auto-subgroups
Whenever a user attempts to access a subgroup of `unseen-university`, for Whenever a user attempts to access a subgroup of `unseen-university`, for
example `unseen-university/hex`, the group is created in memory and example `unseen-university/hex`, the group is created in memory and
persists until it is empty and its chat history has expired. The main persists until it is empty and its chat history has expired. The main
group's operator can view the list of populated subgroups with the command hall's operator can view the list of populated subgroups with the command
`/subgroups`. `/subgroups`.
#### Managing tokens #### Managing tokens
@@ -313,26 +313,26 @@ skaldctl list-tokens -l -hall city-watch
``` ```
A token that is generated with the `-include-subgroups` flag applies to A token that is generated with the `-include-subgroups` flag applies to
the whole hierarchy rooted at the given group, including both ordinary the whole hierarchy rooted at the given hall, including both ordinary
groups and automatically generated subgroups. halls and automatically generated subgroups.
```sh ```sh
skaldctl create-token -hall city-watch -include-subgroups skaldctl create-token -hall city-watch -include-subgroups
``` ```
Such a token can be attached to the root of the group hierarchy, and Such a token can be attached to the root of the hall hierarchy, and
therefore be valid for any group on the server: therefore be valid for any hall on the server:
```sh ```sh
skaldctl create-token -hall '' -include-subgroups skaldctl create-token -hall '' -include-subgroups
``` ```
### Group description reference ### Hall description reference
The definition for the group called *hallname* is in the file The definition for the hall called *hallname* is in the file
`groups/hallname.json`; it does not contain the group name, which makes `halls/hallname.json`; it does not contain the hall name, which makes
it easy to copy or link hall definitions. You may use subdirectories: it easy to copy or link hall definitions. You may use subdirectories:
a file `groups/teaching/networking.json` defines a group called a file `halls/teaching/networking.json` defines a hall called
*teaching/networking*. *teaching/networking*.
Every hall definition file contains a single JSON dictionary. All fields Every hall definition file contains a single JSON dictionary. All fields
@@ -479,7 +479,7 @@ allows any username with any password.
### Hashed passwords ### Hashed passwords
For security reasons, passwords are usually hashed before being stored in For security reasons, passwords are usually hashed before being stored in
group descriptions (in fact, the `skaldctl` utility does not even support hall descriptions (in fact, the `skaldctl` utility does not even support
storing plaintext passwords). A hashed password is represented as a JSON storing plaintext passwords). A hashed password is represented as a JSON
dictionary with a field `type` and a number of type-specific fields. dictionary with a field `type` and a number of type-specific fields.
@@ -501,7 +501,7 @@ A user entry with a hashed password looks like this:
``` ```
Hashed passwords are normally generated transparently to the user by the Hashed passwords are normally generated transparently to the user by the
`skaldctl set-password` command. When editing group description files `skaldctl set-password` command. When editing hall description files
manually, hashed passwords can be generated with the `skaldctl hash-password` manually, hashed passwords can be generated with the `skaldctl hash-password`
utility. utility.
+15 -15
View File
@@ -26,7 +26,7 @@ import (
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"golang.org/x/term" "golang.org/x/term"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/token" "git.stormux.org/storm/skald/token"
) )
@@ -229,11 +229,11 @@ func readConfig(filename string) (configuration, error) {
return config, nil return config, nil
} }
func makePassword(pw string, algorithm string, iterations, length, saltlen, cost int) (group.Password, error) { func makePassword(pw string, algorithm string, iterations, length, saltlen, cost int) (hall.Password, error) {
salt := make([]byte, saltlen) salt := make([]byte, saltlen)
_, err := rand.Read(salt) _, err := rand.Read(salt)
if err != nil { if err != nil {
return group.Password{}, err return hall.Password{}, err
} }
switch algorithm { switch algorithm {
@@ -242,7 +242,7 @@ func makePassword(pw string, algorithm string, iterations, length, saltlen, cost
[]byte(pw), salt, iterations, length, sha256.New, []byte(pw), salt, iterations, length, sha256.New,
) )
encoded := hex.EncodeToString(key) encoded := hex.EncodeToString(key)
return group.Password{ return hall.Password{
Type: "pbkdf2", Type: "pbkdf2",
Hash: "sha-256", Hash: "sha-256",
Key: &encoded, Key: &encoded,
@@ -254,11 +254,11 @@ func makePassword(pw string, algorithm string, iterations, length, saltlen, cost
[]byte(pw), cost, []byte(pw), cost,
) )
if err != nil { if err != nil {
return group.Password{}, err return hall.Password{}, err
} }
k := string(key) k := string(key)
return group.Password{ return hall.Password{
Type: "bcrypt", Type: "bcrypt",
Key: &k, Key: &k,
}, nil }, nil
@@ -269,11 +269,11 @@ func makePassword(pw string, algorithm string, iterations, length, saltlen, cost
"must be the empty string", "must be the empty string",
) )
} }
return group.Password{ return hall.Password{
Type: "wildcard", Type: "wildcard",
}, nil }, nil
default: default:
return group.Password{}, errors.New("unknown password type") return hall.Password{}, errors.New("unknown password type")
} }
} }
@@ -392,18 +392,18 @@ func initialSetupCmd(cmdname string, args []string) {
defer skaldConfig.Close() defer skaldConfig.Close()
defer skaldctlConfig.Close() defer skaldctlConfig.Close()
var users map[string]group.UserDescription var users map[string]hall.UserDescription
if adminPassword != "" { if adminPassword != "" {
pw, err := makePassword(adminPassword, "bcrypt", 0, 0, 0, 12) pw, err := makePassword(adminPassword, "bcrypt", 0, 0, 0, 12)
if err != nil { if err != nil {
log.Fatalf("makePassword: %v", err) log.Fatalf("makePassword: %v", err)
} }
perms, err := group.NewPermissions("admin") perms, err := hall.NewPermissions("admin")
if err != nil { if err != nil {
log.Fatalf("NewPermissions: %v", err) log.Fatalf("NewPermissions: %v", err)
} }
users = map[string]group.UserDescription{ users = map[string]hall.UserDescription{
adminUsername: { adminUsername: {
Password: pw, Password: pw,
Permissions: perms, Permissions: perms,
@@ -411,7 +411,7 @@ func initialSetupCmd(cmdname string, args []string) {
} }
} }
config := group.Configuration{ config := hall.Configuration{
WritableGroups: true, WritableGroups: true,
Users: users, Users: users,
} }
@@ -963,7 +963,7 @@ func parsePermissions(p string, expand bool) (any, error) {
if !expand { if !expand {
return p, nil return p, nil
} }
pp, err := group.NewPermissions(p) pp, err := hall.NewPermissions(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -985,7 +985,7 @@ func formatRawPermissions(permissions []string) string {
return fmt.Sprintf("[%s]", perms) return fmt.Sprintf("[%s]", perms)
} }
func formatPermissions(permissions group.Permissions) string { func formatPermissions(permissions hall.Permissions) string {
s := permissions.String() s := permissions.String()
if len(s) > 0 && s[0] != '[' { if len(s) > 0 && s[0] != '[' {
return s return s
@@ -1056,7 +1056,7 @@ func listUsersCmd(cmdname string, args []string) {
fmt.Printf("%-12s (ERROR=%v)\n", user, err) fmt.Printf("%-12s (ERROR=%v)\n", user, err)
continue continue
} }
var d group.UserDescription var d hall.UserDescription
_, err = getJSON(uu, &d) _, err = getJSON(uu, &d)
if err != nil { if err != nil {
fmt.Printf("%-12s (ERROR=%v)\n", user, err) fmt.Printf("%-12s (ERROR=%v)\n", user, err)
+3 -3
View File
@@ -4,11 +4,11 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
) )
func TestMakePassword(t *testing.T) { func TestMakePassword(t *testing.T) {
doit := func(pw group.Password) { doit := func(pw hall.Password) {
ok, _ := pw.Match("secret") ok, _ := pw.Match("secret")
if !ok { if !ok {
t.Errorf("%v didn't match", pw) t.Errorf("%v didn't match", pw)
@@ -50,7 +50,7 @@ func TestFormatPermissions(t *testing.T) {
{`[]`, "[]", "[]"}, {`[]`, "[]", "[]"},
} }
for _, test := range tests { for _, test := range tests {
var p group.Permissions var p hall.Permissions
err := json.Unmarshal([]byte(test.j), &p) err := json.Unmarshal([]byte(test.j), &p)
if err != nil { if err != nil {
t.Errorf("Unmarshal %#v: %v", test.j, err) t.Errorf("Unmarshal %#v: %v", test.j, err)
+1 -1
View File
@@ -4712,7 +4712,7 @@ async function start() {
if(groupStatus.name) { if(groupStatus.name) {
group = groupStatus.name; group = groupStatus.name;
} else { } else {
console.warn("no group name in status"); console.warn("no hall name in status");
group = decodeURIComponent( group = decodeURIComponent(
location.pathname.replace(/^\/[a-z]*\//, '').replace(/\/$/, ''), location.pathname.replace(/^\/[a-z]*\//, '').replace(/\/$/, ''),
); );
+3 -3
View File
@@ -5,7 +5,7 @@ import (
"sort" "sort"
"time" "time"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
) )
type GroupStats struct { type GroupStats struct {
@@ -59,11 +59,11 @@ type Track struct {
} }
func GetGroups() []GroupStats { func GetGroups() []GroupStats {
names := group.GetNames() names := hall.GetNames()
gs := make([]GroupStats, 0, len(names)) gs := make([]GroupStats, 0, len(names))
for _, name := range names { for _, name := range names {
g := group.Get(name) g := hall.Get(name)
if g == nil { if g == nil {
continue continue
} }
+7 -7
View File
@@ -62,13 +62,13 @@ X Search for remaining Galene names and classify each survivor as attribution/hi
# PHASE 2: HALL TERMINOLOGY AND DATA MODEL # PHASE 2: HALL TERMINOLOGY AND DATA MODEL
# ============================================================================ # ============================================================================
- Rename Go package group/ to hall/ if the package-wide rename stays tractable X Rename Go package group/ to hall/ if the package-wide rename stays tractable
- Rename package declarations, types, variables, funcs, tests, and comments from group to hall X Rename package declarations, types, variables, funcs, tests, and comments from group to hall
- Rename Group type to Hall and update all compile errors intentionally X Rename Group type to Hall and update all compile errors intentionally
- Rename group description files/concepts to hall description files/concepts X Rename group description files/concepts to hall description files/concepts
- Rename default directory flag: -groups -> -halls X Rename default directory flag: -groups -> -halls
- Rename default data directory: groups/ -> halls/ X Rename default data directory: groups/ -> halls/
- Rename public listing concept from public groups to public halls X Rename public listing concept from public groups to public halls
- Rename subgroups to subhalls everywhere, including JSON fields and commands - Rename subgroups to subhalls everywhere, including JSON fields and commands
- Rename auto-subgroups -> auto-subhalls - Rename auto-subgroups -> auto-subhalls
- Rename allow-subgroups -> allow-subhalls - Rename allow-subgroups -> allow-subhalls
+24 -24
View File
@@ -13,7 +13,7 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/stats" "git.stormux.org/storm/skald/stats"
"git.stormux.org/storm/skald/token" "git.stormux.org/storm/skald/token"
) )
@@ -47,7 +47,7 @@ func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, hallname, user s
} }
} }
if ok && !wildcard && username == user { if ok && !wildcard && username == user {
desc, err := group.GetDescription(hallname) desc, err := hall.GetDescription(hallname)
if err != nil { if err != nil {
internalError(w, internalError(w,
"Get description for group %v: %v", "Get description for group %v: %v",
@@ -193,7 +193,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
methodNotAllowed(w, "HEAD, GET") methodNotAllowed(w, "HEAD, GET")
return return
} }
groups, err := group.GetDescriptionNames() groups, err := hall.GetDescriptionNames()
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -233,7 +233,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
} }
if r.Method == "HEAD" || r.Method == "GET" { if r.Method == "HEAD" || r.Method == "GET" {
desc, etag, err := group.GetSanitisedDescription(g) desc, etag, err := hall.GetSanitisedDescription(g)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -249,7 +249,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
sendJSON(w, r, desc) sendJSON(w, r, desc)
return return
} else if r.Method == "PUT" { } else if r.Method == "PUT" {
etag, err := group.GetDescriptionTag(g) etag, err := hall.GetDescriptionTag(g)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
err = nil err = nil
etag = "" etag = ""
@@ -263,12 +263,12 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
return return
} }
var newdesc group.Description var newdesc hall.Description
done = getJSON(w, r, &newdesc) done = getJSON(w, r, &newdesc)
if done { if done {
return return
} }
err = group.UpdateDescription(g, etag, &newdesc) err = hall.UpdateDescription(g, etag, &newdesc)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -280,7 +280,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
} }
return return
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
etag, err := group.GetDescriptionTag(g) etag, err := hall.GetDescriptionTag(g)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -290,7 +290,7 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
if done { if done {
return return
} }
err = group.DeleteDescription(g, etag) err = hall.DeleteDescription(g, etag)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -318,7 +318,7 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
methodNotAllowed(w, "HEAD, GET") methodNotAllowed(w, "HEAD, GET")
return return
} }
users, etag, err := group.GetUsers(g) users, etag, err := hall.GetUsers(g)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -371,7 +371,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
} }
if r.Method == "HEAD" || r.Method == "GET" { if r.Method == "HEAD" || r.Method == "GET" {
user, etag, err := group.GetSanitisedUser(g, user, wildcard) user, etag, err := hall.GetSanitisedUser(g, user, wildcard)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -384,7 +384,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
sendJSON(w, r, user) sendJSON(w, r, user)
return return
} else if r.Method == "PUT" { } else if r.Method == "PUT" {
etag, err := group.GetUserTag(g, user, wildcard) etag, err := hall.GetUserTag(g, user, wildcard)
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
etag = "" etag = ""
err = nil err = nil
@@ -398,12 +398,12 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
return return
} }
var newdesc group.UserDescription var newdesc hall.UserDescription
done = getJSON(w, r, &newdesc) done = getJSON(w, r, &newdesc)
if done { if done {
return return
} }
err = group.UpdateUser(g, user, wildcard, etag, &newdesc) err = hall.UpdateUser(g, user, wildcard, etag, &newdesc)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -415,7 +415,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
} }
return return
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
etag, err := group.GetUserTag(g, user, wildcard) etag, err := hall.GetUserTag(g, user, wildcard)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -426,7 +426,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcar
return return
} }
err = group.DeleteUser(g, user, wildcard, etag) err = hall.DeleteUser(g, user, wildcard, etag)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -447,12 +447,12 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wil
} }
if r.Method == "PUT" { if r.Method == "PUT" {
var pw group.Password var pw hall.Password
done := getJSON(w, r, &pw) done := getJSON(w, r, &pw)
if done { if done {
return return
} }
err := group.SetUserPassword(g, user, wildcard, pw) err := hall.SetUserPassword(g, user, wildcard, pw)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -471,11 +471,11 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wil
} }
k := string(key) k := string(key)
pw := group.Password{ pw := hall.Password{
Type: "bcrypt", Type: "bcrypt",
Key: &k, Key: &k,
} }
err = group.SetUserPassword(g, user, wildcard, pw) err = hall.SetUserPassword(g, user, wildcard, pw)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -483,7 +483,7 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wil
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
err := group.SetUserPassword(g, user, wildcard, group.Password{}) err := hall.SetUserPassword(g, user, wildcard, hall.Password{})
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -528,7 +528,7 @@ func keysHandler(w http.ResponseWriter, r *http.Request, g string) {
httpError(w, err) httpError(w, err)
return return
} }
err = group.SetKeys(g, keys.Keys) err = hall.SetKeys(g, keys.Keys)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -536,7 +536,7 @@ func keysHandler(w http.ResponseWriter, r *http.Request, g string) {
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return return
} else if r.Method == "DELETE" { } else if r.Method == "DELETE" {
err := group.SetKeys(g, nil) err := hall.SetKeys(g, nil)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -563,7 +563,7 @@ func tokensHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
if g != "" { if g != "" {
// check that the group exists // check that the group exists
_, err := group.GetDescription(g) _, err := hall.GetDescription(g)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
+12 -12
View File
@@ -15,7 +15,7 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/token" "git.stormux.org/storm/skald/token"
) )
@@ -34,8 +34,8 @@ func setup() {
func setupTest(dir, datadir string) error { func setupTest(dir, datadir string) error {
setup() setup()
group.Directory = dir hall.Directory = dir
group.DataDirectory = datadir hall.DataDirectory = datadir
config := `{ config := `{
"writableGroups": true, "writableGroups": true,
"users": { "users": {
@@ -45,7 +45,7 @@ func setupTest(dir, datadir string) error {
} }
} }
}` }`
f, err := os.Create(filepath.Join(group.DataDirectory, "config.json")) f, err := os.Create(filepath.Join(hall.DataDirectory, "config.json"))
if err != nil { if err != nil {
return err return err
} }
@@ -142,7 +142,7 @@ func TestApi(t *testing.T) {
t.Errorf("Create hall: %v %v", err, resp.StatusCode) t.Errorf("Create hall: %v %v", err, resp.StatusCode)
} }
var desc *group.Description var desc *hall.Description
err = getJSON("/skald-api/v0/.halls/test/", &desc) err = getJSON("/skald-api/v0/.halls/test/", &desc)
if err != nil || len(desc.Users) != 0 { if err != nil || len(desc.Users) != 0 {
t.Errorf("Get hall: %v", err) t.Errorf("Get hall: %v", err)
@@ -223,7 +223,7 @@ func TestApi(t *testing.T) {
t.Errorf("Set password (POST): %v %v", err, resp.StatusCode) t.Errorf("Set password (POST): %v %v", err, resp.StatusCode)
} }
var user group.UserDescription var user hall.UserDescription
err = getJSON("/skald-api/v0/.halls/test/.users/jch", &user) err = getJSON("/skald-api/v0/.halls/test/.users/jch", &user)
if err != nil { if err != nil {
t.Errorf("Get user: %v", err) t.Errorf("Get user: %v", err)
@@ -232,7 +232,7 @@ func TestApi(t *testing.T) {
t.Errorf("User not sanitised properly") t.Errorf("User not sanitised properly")
} }
desc, err = group.GetDescription("test") desc, err = hall.GetDescription("test")
if err != nil { if err != nil {
t.Errorf("GetDescription: %v", err) t.Errorf("GetDescription: %v", err)
} }
@@ -251,7 +251,7 @@ func TestApi(t *testing.T) {
t.Errorf("Delete hall: %v %v", err, resp.StatusCode) t.Errorf("Delete hall: %v %v", err, resp.StatusCode)
} }
desc, err = group.GetDescription("test") desc, err = hall.GetDescription("test")
if err != nil { if err != nil {
t.Errorf("GetDescription: %v", err) t.Errorf("GetDescription: %v", err)
} }
@@ -272,7 +272,7 @@ func TestApi(t *testing.T) {
t.Errorf("Get wildcard user: %v", err) t.Errorf("Get wildcard user: %v", err)
} }
desc, err = group.GetDescription("test") desc, err = hall.GetDescription("test")
if err != nil { if err != nil {
t.Errorf("GetDescription: %v", err) t.Errorf("GetDescription: %v", err)
} }
@@ -287,7 +287,7 @@ func TestApi(t *testing.T) {
t.Errorf("Delete wildcard user: %v %v", err, resp.StatusCode) t.Errorf("Delete wildcard user: %v %v", err, resp.StatusCode)
} }
desc, err = group.GetDescription("test") desc, err = hall.GetDescription("test")
if err != nil { if err != nil {
t.Errorf("GetDescription: %v", err) t.Errorf("GetDescription: %v", err)
} }
@@ -414,7 +414,7 @@ func TestApi(t *testing.T) {
t.Errorf("Delete hall: %v %v", err, resp.StatusCode) t.Errorf("Delete hall: %v %v", err, resp.StatusCode)
} }
_, err = group.GetDescription("test") _, err = hall.GetDescription("test")
if !errors.Is(err, os.ErrNotExist) { if !errors.Is(err, os.ErrNotExist) {
t.Errorf("Group exists after delete") t.Errorf("Group exists after delete")
} }
@@ -451,7 +451,7 @@ func TestApiBadAuth(t *testing.T) {
do("GET", "/skald-api/v0/.halls/") do("GET", "/skald-api/v0/.halls/")
do("PUT", "/skald-api/v0/.halls/test/") do("PUT", "/skald-api/v0/.halls/test/")
f, err := os.Create(filepath.Join(group.Directory, "test.json")) f, err := os.Create(filepath.Join(hall.Directory, "test.json"))
if err != nil { if err != nil {
t.Fatalf("Create(test.json): %v", err) t.Fatalf("Create(test.json): %v", err)
} }
+22 -22
View File
@@ -22,7 +22,7 @@ import (
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"git.stormux.org/storm/skald/diskwriter" "git.stormux.org/storm/skald/diskwriter"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/rtpconn" "git.stormux.org/storm/skald/rtpconn"
"github.com/jech/cert" "github.com/jech/cert"
) )
@@ -64,7 +64,7 @@ func Serve(address string, dataDir string) error {
} }
} }
s.RegisterOnShutdown(func() { s.RegisterOnShutdown(func() {
group.Shutdown("server is shutting down") hall.Shutdown("server is shutting down")
}) })
server = s server = s
@@ -130,11 +130,11 @@ func httpError(w http.ResponseWriter, err error) {
notFound(w) notFound(w)
return return
} }
if errors.Is(err, group.ErrUnknownPermission) { if errors.Is(err, hall.ErrUnknownPermission) {
http.Error(w, "unknown permission", http.StatusBadRequest) http.Error(w, "unknown permission", http.StatusBadRequest)
return return
} }
var autherr *group.NotAuthorisedError var autherr *hall.NotAuthorisedError
if errors.As(err, &autherr) { if errors.As(err, &autherr) {
log.Printf("HTTP server error: %v", err) log.Printf("HTTP server error: %v", err)
http.Error(w, "not authorised", http.StatusUnauthorized) http.Error(w, "not authorised", http.StatusUnauthorized)
@@ -160,7 +160,7 @@ const (
) )
func redirect(w http.ResponseWriter, r *http.Request) bool { func redirect(w http.ResponseWriter, r *http.Request) bool {
conf, err := group.GetConfiguration() conf, err := hall.GetConfiguration()
if err != nil || conf.CanonicalHost == "" { if err != nil || conf.CanonicalHost == "" {
return false return false
} }
@@ -285,7 +285,7 @@ func serveFile(w http.ResponseWriter, r *http.Request, p string) {
http.ServeContent(w, r, fi.Name(), fi.ModTime(), f) http.ServeContent(w, r, fi.Name(), fi.ModTime(), f)
} }
func parseGroupName(prefix string, p string) string { func parseHallName(prefix string, p string) string {
if !strings.HasPrefix(p, prefix) { if !strings.HasPrefix(p, prefix) {
return "" return ""
} }
@@ -346,13 +346,13 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
name := parseGroupName("/hall/", r.URL.Path) name := parseHallName("/hall/", r.URL.Path)
if name == "" { if name == "" {
notFound(w) notFound(w)
return return
} }
g, err := group.Add(name, nil) g, err := hall.Add(name, nil)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -375,7 +375,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
} }
func baseURL(r *http.Request) (*url.URL, error) { func baseURL(r *http.Request) (*url.URL, error) {
conf, err := group.GetConfiguration() conf, err := hall.GetConfiguration()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -415,13 +415,13 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
internalError(w, "groupStatusHandler: this shouldn't happen") internalError(w, "groupStatusHandler: this shouldn't happen")
return return
} }
name := parseGroupName("/hall/", pth) name := parseHallName("/hall/", pth)
if name == "" { if name == "" {
notFound(w) notFound(w)
return return
} }
g, err := group.Add(name, nil) g, err := hall.Add(name, nil)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -458,13 +458,13 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
g := group.GetPublic(base) g := hall.GetPublic(base)
e := json.NewEncoder(w) e := json.NewEncoder(w)
e.Encode(g) e.Encode(g)
} }
func adminMatch(username, password string) (bool, error) { func adminMatch(username, password string) (bool, error) {
conf, err := group.GetConfiguration() conf, err := hall.GetConfiguration()
if err != nil { if err != nil {
return false, err return false, err
} }
@@ -512,7 +512,7 @@ func CheckOrigin(w http.ResponseWriter, r *http.Request, admin bool) bool {
if err == nil && strings.EqualFold(o.Host, r.Host) { if err == nil && strings.EqualFold(o.Host, r.Host) {
ok = true ok = true
} else { } else {
conf, err := group.GetConfiguration() conf, err := hall.GetConfiguration()
if err != nil { if err != nil {
return false return false
} }
@@ -613,20 +613,20 @@ func recordingsHandler(w http.ResponseWriter, r *http.Request) {
for len(p) > 0 && p[len(p)-1] == '/' { for len(p) > 0 && p[len(p)-1] == '/' {
p = p[:len(p)-1] p = p[:len(p)-1]
} }
group = parseGroupName("/", p) group = parseHallName("/", p)
if group == "" { if group == "" {
http.Error(w, "Bad group name", http.StatusBadRequest) http.Error(w, "Bad hall name", http.StatusBadRequest)
return return
} }
} else { } else {
if p[len(p)-1] == '/' { if p[len(p)-1] == '/' {
http.Error(w, "Bad group name", http.StatusBadRequest) http.Error(w, "Bad hall name", http.StatusBadRequest)
return return
} }
group, filename = path.Split(p) group, filename = path.Split(p)
group = parseGroupName("/", group) group = parseHallName("/", group)
if group == "" { if group == "" {
http.Error(w, "Bad group name", http.StatusBadRequest) http.Error(w, "Bad hall name", http.StatusBadRequest)
return return
} }
} }
@@ -711,13 +711,13 @@ func checkGroupPermissions(w http.ResponseWriter, r *http.Request, hallname stri
return false return false
} }
g := group.Get(hallname) g := hall.Get(hallname)
if g == nil { if g == nil {
return false return false
} }
_, p, err := g.GetPermission( _, p, err := g.GetPermission(
group.ClientCredentials{ hall.ClientCredentials{
Username: &user, Username: &user,
Password: pass, Password: pass,
}, },
@@ -732,7 +732,7 @@ func checkGroupPermissions(w http.ResponseWriter, r *http.Request, hallname stri
} }
} }
if err != nil || !record { if err != nil || !record {
var autherr *group.NotAuthorisedError var autherr *hall.NotAuthorisedError
if errors.As(err, &autherr) { if errors.As(err, &autherr) {
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
} }
+6 -6
View File
@@ -10,10 +10,10 @@ import (
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
) )
func TestParseGroupName(t *testing.T) { func TestParseHallName(t *testing.T) {
a := []struct{ p, g string }{ a := []struct{ p, g string }{
{"", ""}, {"", ""},
{"/foo", ""}, {"/foo", ""},
@@ -29,7 +29,7 @@ func TestParseGroupName(t *testing.T) {
} }
for _, pg := range a { for _, pg := range a {
g := parseGroupName("/hall/", pg.p) g := parseHallName("/hall/", pg.p)
if g != pg.g { if g != pg.g {
t.Errorf("Path %v, got %v, expected %v", t.Errorf("Path %v, got %v, expected %v",
pg.p, g, pg.g) pg.p, g, pg.g)
@@ -58,10 +58,10 @@ func TestBase(t *testing.T) {
} }
dir := t.TempDir() dir := t.TempDir()
group.DataDirectory = dir hall.DataDirectory = dir
for _, v := range a { for _, v := range a {
conf := group.Configuration{ conf := hall.Configuration{
ProxyURL: v.p, ProxyURL: v.p,
} }
c, err := json.Marshal(conf) c, err := json.Marshal(conf)
@@ -200,7 +200,7 @@ func TestFormatICEServer(t *testing.T) {
func TestMatchAdmin(t *testing.T) { func TestMatchAdmin(t *testing.T) {
d := t.TempDir() d := t.TempDir()
group.DataDirectory = d hall.DataDirectory = d
filename := filepath.Join(d, "config.json") filename := filepath.Join(d, "config.json")
f, err := os.Create(filename) f, err := os.Create(filename)
+10 -10
View File
@@ -17,7 +17,7 @@ import (
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
"git.stormux.org/storm/skald/group" "git.stormux.org/storm/skald/hall"
"git.stormux.org/storm/skald/ice" "git.stormux.org/storm/skald/ice"
"git.stormux.org/storm/skald/rtpconn" "git.stormux.org/storm/skald/rtpconn"
"git.stormux.org/storm/skald/sdpfrag" "git.stormux.org/storm/skald/sdpfrag"
@@ -153,13 +153,13 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
name := parseGroupName("/hall/", pth) name := parseHallName("/hall/", pth)
if name == "" { if name == "" {
notFound(w) notFound(w)
return return
} }
g, err := group.Add(name, nil) g, err := hall.Add(name, nil)
if err != nil { if err != nil {
httpError(w, err) httpError(w, err)
return return
@@ -199,7 +199,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
token := parseBearerToken(r.Header.Get("Authorization")) token := parseBearerToken(r.Header.Get("Authorization"))
whip := "whip" whip := "whip"
creds := group.ClientCredentials{ creds := hall.ClientCredentials{
Username: &whip, Username: &whip,
Token: token, Token: token,
} }
@@ -221,7 +221,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
c := rtpconn.NewWhipClient(g, id, token, addr) c := rtpconn.NewWhipClient(g, id, token, addr)
_, err = group.AddClient(g.Name(), c, creds) _, err = hall.AddClient(g.Name(), c, creds)
if err != nil { if err != nil {
log.Printf("WHIP: %v", err) log.Printf("WHIP: %v", err)
httpError(w, err) httpError(w, err)
@@ -229,7 +229,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
} }
if !canPresent(c.Permissions()) { if !canPresent(c.Permissions()) {
group.DelClient(c) hall.DelClient(c)
http.Error(w, "Forbidden", http.StatusForbidden) http.Error(w, "Forbidden", http.StatusForbidden)
return return
} }
@@ -238,7 +238,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
answer, err := c.NewConnection(r.Context(), body) answer, err := c.NewConnection(r.Context(), body)
if err != nil { if err != nil {
group.DelClient(c) hall.DelClient(c)
log.Printf("WHIP offer: %v", err) log.Printf("WHIP offer: %v", err)
httpError(w, err) httpError(w, err)
return return
@@ -269,13 +269,13 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
name := parseGroupName("/hall/", pth) name := parseHallName("/hall/", pth)
if name == "" { if name == "" {
notFound(w) notFound(w)
return return
} }
g := group.Get(name) g := hall.Get(name)
if g == nil { if g == nil {
notFound(w) notFound(w)
return return
@@ -295,7 +295,7 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
if t := c.Token(); t != "" { if t := c.Token(); t != "" {
token := parseBearerToken(r.Header.Get("Authorization")) token := parseBearerToken(r.Header.Get("Authorization"))
if !group.ConstantTimeCompare(t, token) { if !hall.ConstantTimeCompare(t, token) {
http.Error(w, "Forbidden", http.StatusForbidden) http.Error(w, "Forbidden", http.StatusForbidden)
return return
} }