Commit 3240225d authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Make client generic.

parent d8b98497
...@@ -21,6 +21,13 @@ import ( ...@@ -21,6 +21,13 @@ import (
"github.com/pion/webrtc/v2" "github.com/pion/webrtc/v2"
) )
type client interface {
getGroup() *group
getId() string
getUsername() string
pushConn(conn *upConnection, tracks []*upTrack, label string) error
}
type chatHistoryEntry struct { type chatHistoryEntry struct {
id string id string
user string user string
...@@ -41,7 +48,7 @@ type group struct { ...@@ -41,7 +48,7 @@ type group struct {
locked uint32 locked uint32
mu sync.Mutex mu sync.Mutex
clients map[string]*webClient clients map[string]client
history []chatHistoryEntry history []chatHistoryEntry
} }
...@@ -64,7 +71,7 @@ type getUpAction struct { ...@@ -64,7 +71,7 @@ type getUpAction struct {
} }
type pushConnsAction struct { type pushConnsAction struct {
c *webClient c client
} }
type connectionFailedAction struct { type connectionFailedAction struct {
...@@ -122,7 +129,7 @@ func addGroup(name string, desc *groupDescription) (*group, error) { ...@@ -122,7 +129,7 @@ func addGroup(name string, desc *groupDescription) (*group, error) {
g = &group{ g = &group{
name: name, name: name,
description: desc, description: desc,
clients: make(map[string]*webClient), clients: make(map[string]client),
} }
groups.groups[name] = g groups.groups[name] = g
} else if desc != nil { } else if desc != nil {
...@@ -195,7 +202,7 @@ type userid struct { ...@@ -195,7 +202,7 @@ type userid struct {
username string username string
} }
func addClient(name string, client *webClient, user, pass string) (*group, []userid, error) { func addClient(name string, client client, user, pass string) (*group, []userid, error) {
g, err := addGroup(name, nil) g, err := addGroup(name, nil)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
...@@ -205,7 +212,10 @@ func addClient(name string, client *webClient, user, pass string) (*group, []use ...@@ -205,7 +212,10 @@ func addClient(name string, client *webClient, user, pass string) (*group, []use
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
client.permissions = perms c, ok := client.(*webClient)
if ok {
c.permissions = perms
}
if !perms.Op && atomic.LoadUint32(&g.locked) != 0 { if !perms.Op && atomic.LoadUint32(&g.locked) != 0 {
return nil, nil, userError("group is locked") return nil, nil, userError("group is locked")
...@@ -219,34 +229,34 @@ func addClient(name string, client *webClient, user, pass string) (*group, []use ...@@ -219,34 +229,34 @@ func addClient(name string, client *webClient, user, pass string) (*group, []use
return nil, nil, userError("too many users") return nil, nil, userError("too many users")
} }
} }
if g.clients[client.id] != nil { if g.clients[client.getId()] != nil {
return nil, nil, protocolError("duplicate client id") return nil, nil, protocolError("duplicate client id")
} }
var users []userid var users []userid
for _, c := range g.clients { for _, c := range g.clients {
users = append(users, userid{c.id, c.username}) users = append(users, userid{c.getId(), c.getUsername()})
} }
g.clients[client.id] = client g.clients[client.getId()] = client
return g, users, nil return g, users, nil
} }
func delClient(c *webClient) { func delClient(c client) {
c.group.mu.Lock() g := c.getGroup()
defer c.group.mu.Unlock() g.mu.Lock()
g := c.group defer g.mu.Unlock()
if g.clients[c.id] != c { if g.clients[c.getId()] != c {
log.Printf("Deleting unknown client") log.Printf("Deleting unknown client")
return return
} }
delete(g.clients, c.id) delete(g.clients, c.getId())
} }
func (g *group) getClients(except *webClient) []*webClient { func (g *group) getClients(except client) []client {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
clients := make([]*webClient, 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 {
clients = append(clients, c) clients = append(clients, c)
...@@ -255,16 +265,16 @@ func (g *group) getClients(except *webClient) []*webClient { ...@@ -255,16 +265,16 @@ func (g *group) getClients(except *webClient) []*webClient {
return clients return clients
} }
func (g *group) getClientUnlocked(id string) *webClient { func (g *group) getClientUnlocked(id string) client {
for _, c := range g.clients { for idd, c := range g.clients {
if c.id == id { if idd == id {
return c return c
} }
} }
return nil return nil
} }
func (g *group) Range(f func(c *webClient) bool) { func (g *group) 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 {
...@@ -311,42 +321,12 @@ func (err writerDeadError) Error() string { ...@@ -311,42 +321,12 @@ func (err writerDeadError) Error() string {
return "client writer died" return "client writer died"
} }
func (c *webClient) write(m clientMessage) error {
select {
case c.writeCh <- m:
return nil
case <-c.writerDone:
return writerDeadError(0)
}
}
func (c *webClient) error(err error) error {
switch e := err.(type) {
case userError:
return c.write(clientMessage{
Type: "error",
Value: string(e),
})
default:
return err
}
}
type clientDeadError int type clientDeadError int
func (err clientDeadError) Error() string { func (err clientDeadError) Error() string {
return "client dead" return "client dead"
} }
func (c *webClient) action(m interface{}) error {
select {
case c.actionCh <- m:
return nil
case <-c.done:
return clientDeadError(0)
}
}
type groupUser struct { type groupUser struct {
Username string `json:"username,omitempty"` Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
...@@ -453,42 +433,6 @@ func getPermission(desc *groupDescription, user, pass string) (userPermission, e ...@@ -453,42 +433,6 @@ func getPermission(desc *groupDescription, user, pass string) (userPermission, e
return p, userError("not authorised") return p, userError("not authorised")
} }
func setPermission(g *group, id string, perm string) error {
g.mu.Lock()
defer g.mu.Unlock()
c := g.getClientUnlocked(id)
if c == nil {
return userError("no such user")
}
switch perm {
case "op":
c.permissions.Op = true
case "unop":
c.permissions.Op = false
case "present":
c.permissions.Present = true
case "unpresent":
c.permissions.Present = false
default:
return userError("unknown permission")
}
return c.action(permissionsChangedAction{})
}
func kickClient(g *group, id string) error {
g.mu.Lock()
defer g.mu.Unlock()
c := g.getClientUnlocked(id)
if c == nil {
return userError("no such user")
}
return c.action(kickAction{})
}
type publicGroup struct { type publicGroup struct {
Name string `json:"name"` Name string `json:"name"`
ClientCount int `json:"clientCount"` ClientCount int `json:"clientCount"`
...@@ -580,9 +524,12 @@ func getGroupStats() []groupStats { ...@@ -580,9 +524,12 @@ func getGroupStats() []groupStats {
clients: make([]clientStats, 0, len(clients)), clients: make([]clientStats, 0, len(clients)),
} }
for _, c := range clients { for _, c := range clients {
c, ok := c.(*webClient)
if ok {
cs := getClientStats(c) cs := getClientStats(c)
stats.clients = append(stats.clients, cs) stats.clients = append(stats.clients, cs)
} }
}
sort.Slice(stats.clients, func(i, j int) bool { sort.Slice(stats.clients, func(i, j int) bool {
return stats.clients[i].id < stats.clients[j].id return stats.clients[i].id < stats.clients[j].id
}) })
......
...@@ -107,6 +107,18 @@ type webClient struct { ...@@ -107,6 +107,18 @@ type webClient struct {
up map[string]*upConnection up map[string]*upConnection
} }
func (c *webClient) getGroup() *group {
return c.group
}
func (c *webClient) getId() string {
return c.id
}
func (c *webClient) getUsername() string {
return c.username
}
type rateMap map[string]uint32 type rateMap map[string]uint32
func (v *rateMap) UnmarshalJSON(b []byte) error { func (v *rateMap) UnmarshalJSON(b []byte) error {
...@@ -259,8 +271,11 @@ func startClient(conn *websocket.Conn) (err error) { ...@@ -259,8 +271,11 @@ func startClient(conn *websocket.Conn) (err error) {
Username: c.username, Username: c.username,
} }
for _, c := range clients { for _, c := range clients {
c, ok := c.(*webClient)
if ok {
c.write(u) c.write(u)
} }
}
defer func() { defer func() {
clients := g.getClients(c) clients := g.getClients(c)
...@@ -271,8 +286,11 @@ func startClient(conn *websocket.Conn) (err error) { ...@@ -271,8 +286,11 @@ func startClient(conn *websocket.Conn) (err error) {
Del: true, Del: true,
} }
for _, c := range clients { for _, c := range clients {
c, ok := c.(*webClient)
if ok {
c.write(u) c.write(u)
} }
}
}() }()
return clientLoop(c, conn) return clientLoop(c, conn)
...@@ -403,7 +421,7 @@ func addUpConn(c *webClient, id string) (*upConnection, error) { ...@@ -403,7 +421,7 @@ func addUpConn(c *webClient, id string) (*upConnection, error) {
if tracks != nil { if tracks != nil {
clients := c.group.getClients(c) clients := c.group.getClients(c)
for _, cc := range clients { for _, cc := range clients {
pushConn(cc, u, tracks, u.label) cc.pushConn(u, tracks, u.label)
} }
} }
}) })
...@@ -1208,7 +1226,10 @@ func (c *webClient) setRequested(requested map[string]uint32) error { ...@@ -1208,7 +1226,10 @@ func (c *webClient) setRequested(requested map[string]uint32) error {
go func() { go func() {
clients := c.group.getClients(c) clients := c.group.getClients(c)
for _, cc := range clients { for _, cc := range clients {
cc.action(pushConnsAction{c}) ccc, ok := cc.(*webClient)
if ok {
ccc.action(pushConnsAction{c})
}
} }
}() }()
...@@ -1250,11 +1271,18 @@ func addDownConnTracks(c *webClient, remote *upConnection, tracks []*upTrack) (* ...@@ -1250,11 +1271,18 @@ func addDownConnTracks(c *webClient, remote *upConnection, tracks []*upTrack) (*
return down, nil return down, nil
} }
func pushConn(c *webClient, conn *upConnection, tracks []*upTrack, label string) { func (c *webClient) pushConn(conn *upConnection, tracks []*upTrack, label string) error {
c.action(addConnAction{conn, tracks}) err := c.action(addConnAction{conn, tracks})
if err != nil {
return err
}
if label != "" { if label != "" {
c.action(addLabelAction{conn.id, conn.label}) err := c.action(addLabelAction{conn.id, conn.label})
if err != nil {
return err
} }
}
return nil
} }
func clientLoop(c *webClient, conn *websocket.Conn) error { func clientLoop(c *webClient, conn *websocket.Conn) error {
...@@ -1354,7 +1382,7 @@ func clientLoop(c *webClient, conn *websocket.Conn) error { ...@@ -1354,7 +1382,7 @@ func clientLoop(c *webClient, conn *websocket.Conn) error {
for _, u := range c.up { for _, u := range c.up {
tracks := make([]*upTrack, len(u.tracks)) tracks := make([]*upTrack, len(u.tracks))
copy(tracks, u.tracks) copy(tracks, u.tracks)
go pushConn(a.c, u, tracks, u.label) go a.c.pushConn(u, tracks, u.label)
} }
case connectionFailedAction: case connectionFailedAction:
found := delUpConn(c, a.id) found := delUpConn(c, a.id)
...@@ -1428,6 +1456,52 @@ func failConnection(c *webClient, id string, message string) error { ...@@ -1428,6 +1456,52 @@ func failConnection(c *webClient, id string, message string) error {
return nil return nil
} }
func setPermissions(g *group, id string, perm string) error {
g.mu.Lock()
defer g.mu.Unlock()
client := g.getClientUnlocked(id)
if client == nil {
return userError("no such user")
}
c, ok := client.(*webClient)
if !ok {
return userError("this is not a real user")
}
switch perm {
case "op":
c.permissions.Op = true
case "unop":
c.permissions.Op = false
case "present":
c.permissions.Present = true
case "unpresent":
c.permissions.Present = false
default:
return userError("unknown permission")
}
return c.action(permissionsChangedAction{})
}
func kickClient(g *group, id string) error {
g.mu.Lock()
defer g.mu.Unlock()
client := g.getClientUnlocked(id)
if client == nil {
return userError("no such user")
}
c, ok := client.(*webClient)
if !ok {
return userError("this is not a real user")
}
return c.action(kickAction{})
}
func handleClientMessage(c *webClient, m clientMessage) error { func handleClientMessage(c *webClient, m clientMessage) error {
switch m.Type { switch m.Type {
case "request": case "request":
...@@ -1476,20 +1550,26 @@ func handleClientMessage(c *webClient, m clientMessage) error { ...@@ -1476,20 +1550,26 @@ func handleClientMessage(c *webClient, m clientMessage) error {
c.group.addToChatHistory(m.Id, m.Username, m.Value, m.Me) c.group.addToChatHistory(m.Id, m.Username, m.Value, m.Me)
clients := c.group.getClients(c) clients := c.group.getClients(c)
for _, cc := range clients { for _, cc := range clients {
cc, ok := cc.(*webClient)
if ok {
cc.write(m) cc.write(m)
} }
}
case "clearchat": case "clearchat":
c.group.clearChatHistory() c.group.clearChatHistory()
m := clientMessage{Type: "clearchat"} m := clientMessage{Type: "clearchat"}
clients := c.group.getClients(nil) clients := c.group.getClients(nil)
for _, cc := range clients { for _, cc := range clients {
cc, ok := cc.(*webClient)
if ok {
cc.write(m) cc.write(m)
} }
}
case "op", "unop", "present", "unpresent": case "op", "unop", "present", "unpresent":
if !c.permissions.Op { if !c.permissions.Op {
return c.error(userError("not authorised")) return c.error(userError("not authorised"))
} }
err := setPermission(c.group, m.Id, m.Type) err := setPermissions(c.group, m.Id, m.Type)
if err != nil { if err != nil {
return c.error(err) return c.error(err)
} }
...@@ -1619,3 +1699,33 @@ func clientWriter(conn *websocket.Conn, ch <-chan interface{}, done chan<- struc ...@@ -1619,3 +1699,33 @@ func clientWriter(conn *websocket.Conn, ch <-chan interface{}, done chan<- struc
} }
} }
} }
func (c *webClient) action(m interface{}) error {
select {
case c.actionCh <- m:
return nil
case <-c.done:
return clientDeadError(0)
}
}
func (c *webClient) write(m clientMessage) error {
select {
case c.writeCh <- m:
return nil
case <-c.writerDone:
return writerDeadError(0)
}
}
func (c *webClient) error(err error) error {
switch e := err.(type) {
case userError:
return c.write(clientMessage{
Type: "error",
Value: string(e),
})
default:
return err
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment