diff --git a/README.PROTOCOL b/README.PROTOCOL index 7d40fab..d45ec11 100644 --- a/README.PROTOCOL +++ b/README.PROTOCOL @@ -118,7 +118,8 @@ users a `user` message: kind: 'add' or 'change' or 'delete', id: id, username: username, - permissions: permissions + permissions: permissions, + status: status } ``` @@ -277,8 +278,8 @@ A user action requests that the server act upon a user. value: value } ``` -Currently defined kinds include `op`, `unop`, `present`, `unpresent`, and -`kick`. +Currently defined kinds include `op`, `unop`, `present`, `unpresent`, +`kick` and `setstatus`. Finally, a group action requests that the server act on the current group. diff --git a/diskwriter/diskwriter.go b/diskwriter/diskwriter.go index cdd66e9..4847b89 100644 --- a/diskwriter/diskwriter.go +++ b/diskwriter/diskwriter.go @@ -71,7 +71,11 @@ func (client *Client) Permissions() group.ClientPermissions { return group.ClientPermissions{} } -func (client *Client) PushClient(id, username string, permissions group.ClientPermissions, kind string) error { +func (client *Client) Status() map[string]interface{} { + return nil +} + +func (client *Client) PushClient(id, username string, permissions group.ClientPermissions, status map[string]interface{}, kind string) error { return nil } diff --git a/group/client.go b/group/client.go index 0fc8714..1b703cf 100644 --- a/group/client.go +++ b/group/client.go @@ -98,8 +98,9 @@ type Client interface { Challengeable Permissions() ClientPermissions SetPermissions(ClientPermissions) + Status() map[string]interface{} OverridePermissions(*Group) bool PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error - PushClient(id, username string, permissions ClientPermissions, kind string) error + PushClient(id, username string, permissions ClientPermissions, status map[string]interface{}, kind string) error Kick(id, user, message string) error } diff --git a/group/group.go b/group/group.go index ed3abbb..6cd7d02 100644 --- a/group/group.go +++ b/group/group.go @@ -488,10 +488,14 @@ func AddClient(group string, c Client) (*Group, error) { id := c.Id() u := c.Username() p := c.Permissions() - c.PushClient(c.Id(), u, p, "add") + s := c.Status() + c.PushClient(c.Id(), u, p, s, "add") for _, cc := range clients { - c.PushClient(cc.Id(), cc.Username(), cc.Permissions(), "add") - cc.PushClient(id, u, p, "add") + c.PushClient( + cc.Id(), cc.Username(), cc.Permissions(), cc.Status(), + "add", + ) + cc.PushClient(id, u, p, s, "add") } return g, nil @@ -537,7 +541,7 @@ func DelClient(c Client) { go func(clients []Client) { for _, cc := range clients { - cc.PushClient(c.Id(), c.Username(), c.Permissions(), "delete") + cc.PushClient(c.Id(), c.Username(), c.Permissions(), c.Status(), "delete") } }(clients) diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index 3f732f6..43bdb5d 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -58,6 +58,7 @@ type webClient struct { username string password string permissions group.ClientPermissions + status map[string]interface{} requested map[string]uint32 done chan struct{} writeCh chan interface{} @@ -98,6 +99,10 @@ func (c *webClient) Permissions() group.ClientPermissions { return c.permissions } +func (c *webClient) Status() map[string]interface{} { + return c.status +} + func (c *webClient) SetPermissions(perms group.ClientPermissions) { c.permissions = perms } @@ -106,13 +111,14 @@ func (c *webClient) OverridePermissions(g *group.Group) bool { return false } -func (c *webClient) PushClient(id, username string, permissions group.ClientPermissions, kind string) error { +func (c *webClient) PushClient(id, username string, permissions group.ClientPermissions, status map[string]interface{}, kind string) error { return c.write(clientMessage{ Type: "user", Kind: kind, Id: id, Username: username, Permissions: &permissions, + Status: status, }) } @@ -174,6 +180,7 @@ type clientMessage struct { Password string `json:"password,omitempty"` Privileged bool `json:"privileged,omitempty"` Permissions *group.ClientPermissions `json:"permissions,omitempty"` + Status map[string]interface{} `json:"status,omitempty"` Group string `json:"group,omitempty"` Value interface{} `json:"value,omitempty"` NoEcho bool `json:"noecho,omitempty"` @@ -988,10 +995,11 @@ func handleAction(c *webClient, a interface{}) error { } id := c.Id() user := c.Username() + s := c.Status() clients := g.GetClients(nil) go func(clients []group.Client) { for _, cc := range clients { - cc.PushClient(id, user, perms, "change") + cc.PushClient(id, user, perms, s, "change") } }(clients) case kickAction: @@ -1042,6 +1050,7 @@ func leaveGroup(c *webClient) { group.DelClient(c) c.permissions = group.ClientPermissions{} + c.status = nil c.requested = map[string]uint32{} c.group = nil } @@ -1454,6 +1463,36 @@ func handleClientMessage(c *webClient, m clientMessage) error { if err != nil { return c.error(err) } + case "setstatus": + if m.Dest != c.Id() { + return c.error(group.UserError("not authorised")) + } + s, ok := m.Value.(map[string]interface{}) + if !ok { + return c.error(group.UserError( + "Bad value in setstatus", + )) + } + if c.status == nil { + c.status = make(map[string]interface{}) + } + for k, v := range s { + if v == nil { + delete(c.status, k) + } else { + c.status[k] = v + } + } + id := c.Id() + user := c.Username() + perms := c.Permissions() + status := c.Status() + go func(clients []group.Client) { + for _, cc := range clients { + cc.PushClient(id, user, perms, status, + "change") + } + }(g.GetClients(nil)) default: return group.ProtocolError("unknown user action") } diff --git a/static/galene.js b/static/galene.js index 065eb60..254f34b 100644 --- a/static/galene.js +++ b/static/galene.js @@ -2365,6 +2365,24 @@ commands.wall = { }, }; +commands.raise = { + description: 'raise hand', + f: (c, r) => { + serverConnection.userAction( + "setstatus", serverConnection.id, {"raisehand": true}, + ); + } +} + +commands.unraise = { + description: 'unraise hand', + f: (c, r) => { + serverConnection.userAction( + "setstatus", serverConnection.id, {"raisehand": null}, + ); + } +} + /** * Test loopback through a TURN relay. * diff --git a/static/protocol.js b/static/protocol.js index 80f5f73..a78f6b3 100644 --- a/static/protocol.js +++ b/static/protocol.js @@ -64,6 +64,7 @@ function newLocalId() { * @typedef {Object} user * @property {string} username * @property {Object} permissions + * @property {Object} status */ /** @@ -205,6 +206,7 @@ function ServerConnection() { * @property {string} [password] * @property {boolean} [privileged] * @property {Object} [permissions] + * @property {Object} [status] * @property {string} [group] * @property {unknown} [value] * @property {boolean} [noecho] @@ -342,7 +344,8 @@ ServerConnection.prototype.connect = async function(url) { console.warn(`Duplicate user ${m.id} ${m.username}`); sc.users[m.id] = { username: m.username, - permissions: m.permissions, + permissions: m.permissions || {}, + status: m.status || {}, }; break; case 'change': @@ -350,11 +353,13 @@ ServerConnection.prototype.connect = async function(url) { console.warn(`Unknown user ${m.id} ${m.username}`); sc.users[m.id] = { username: m.username, - permissions: m.permissions, + permissions: m.permissions || {}, + status: m.status || {}, }; } else { sc.users[m.id].username = m.username; - sc.users[m.id].permissions = m.permissions; + sc.users[m.id].permissions = m.permissions || {}; + sc.users[m.id].status = m.status || {}; } break; case 'delete': @@ -562,7 +567,7 @@ ServerConnection.prototype.chat = function(kind, dest, value) { * * @param {string} kind - One of "op", "unop", "kick", "present", "unpresent". * @param {string} dest - The id of the user to act upon. - * @param {string} [value] - An optional user-readable message. + * @param {any} [value] - An action-dependent parameter. */ ServerConnection.prototype.userAction = function(kind, dest, value) { this.send({