mirror of
https://github.com/jech/galene.git
synced 2024-11-14 04:35:57 +01:00
Make client generic.
This commit is contained in:
parent
d8b984979b
commit
3240225dc3
2 changed files with 157 additions and 100 deletions
123
group.go
123
group.go
|
@ -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 {
|
||||||
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 {
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
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 {
|
||||||
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
|
||||||
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
|
||||||
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 {
|
||||||
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 {
|
||||||
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
|
||||||
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,8 +524,11 @@ func getGroupStats() []groupStats {
|
||||||
clients: make([]clientStats, 0, len(clients)),
|
clients: make([]clientStats, 0, len(clients)),
|
||||||
}
|
}
|
||||||
for _, c := range clients {
|
for _, c := range clients {
|
||||||
cs := getClientStats(c)
|
c, ok := c.(*webClient)
|
||||||
stats.clients = append(stats.clients, cs)
|
if ok {
|
||||||
|
cs := getClientStats(c)
|
||||||
|
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
|
||||||
|
|
134
webclient.go
134
webclient.go
|
@ -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,7 +271,10 @@ func startClient(conn *websocket.Conn) (err error) {
|
||||||
Username: c.username,
|
Username: c.username,
|
||||||
}
|
}
|
||||||
for _, c := range clients {
|
for _, c := range clients {
|
||||||
c.write(u)
|
c, ok := c.(*webClient)
|
||||||
|
if ok {
|
||||||
|
c.write(u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -271,7 +286,10 @@ func startClient(conn *websocket.Conn) (err error) {
|
||||||
Del: true,
|
Del: true,
|
||||||
}
|
}
|
||||||
for _, c := range clients {
|
for _, c := range clients {
|
||||||
c.write(u)
|
c, ok := c.(*webClient)
|
||||||
|
if ok {
|
||||||
|
c.write(u)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
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) (*
|
||||||
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 label != "" {
|
if err != nil {
|
||||||
c.action(addLabelAction{conn.id, conn.label})
|
return err
|
||||||
}
|
}
|
||||||
|
if 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 {
|
||||||
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 {
|
||||||
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 {
|
||||||
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.write(m)
|
cc, ok := cc.(*webClient)
|
||||||
|
if ok {
|
||||||
|
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.write(m)
|
cc, ok := cc.(*webClient)
|
||||||
|
if ok {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue