From 24187430e85f29b951eabf1c703bc11947989dac Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Sat, 29 Jan 2022 22:54:44 +0100 Subject: [PATCH] Rename client status to data, add group data. We now distinguish between status, which is maintained by the server, and data, which is provided by the client. In addition to client data, we now support group data. --- README.FRONTEND | 2 +- README.PROTOCOL | 9 ++--- diskwriter/diskwriter.go | 4 +-- group/client.go | 4 +-- group/group.go | 35 ++++++++++++++++--- rtpconn/webclient.go | 75 ++++++++++++++++++++++------------------ static/galene.js | 11 +++--- static/protocol.js | 15 ++++---- webserver/webserver.go | 2 +- 9 files changed, 98 insertions(+), 59 deletions(-) diff --git a/README.FRONTEND b/README.FRONTEND index 35986d3..274f418 100644 --- a/README.FRONTEND +++ b/README.FRONTEND @@ -73,7 +73,7 @@ will trigger. There, you update your user interface and request incoming streams: ```javascript -serverConnection.onjoined = function(kind, group, perms, status, message) { +serverConnection.onjoined = function(kind, group, perms, status, data, message) { switch(kind) { case 'join': this.request({'':['audio','video']}); diff --git a/README.PROTOCOL b/README.PROTOCOL index 327f0e4..5fc8857 100644 --- a/README.PROTOCOL +++ b/README.PROTOCOL @@ -108,7 +108,7 @@ The `join` message requests that the sender join or leave a group: group: group, username: username, password: password, - status: status + data: data } ``` @@ -125,6 +125,7 @@ its permissions or in the recommended RTC configuration. username: username, permissions: permissions, status: status, + data: data, rtcConfiguration: RTCConfiguration } ``` @@ -337,7 +338,7 @@ A user action requests that the server act upon a user. } ``` Currently defined kinds include `op`, `unop`, `present`, `unpresent`, -`kick` and `setstatus`. +`kick` and `setdata`. Finally, a group action requests that the server act on the current group. @@ -352,5 +353,5 @@ Finally, a group action requests that the server act on the current group. ``` Currently defined kinds include `clearchat` (not to be confused with the -`clearchat` user message), `lock`, `unlock`, `record`, `unrecord` and -`subgroups`. +`clearchat` user message), `lock`, `unlock`, `record`, `unrecord`, +`subgroups` and `setdata`. diff --git a/diskwriter/diskwriter.go b/diskwriter/diskwriter.go index 2ad78ab..c585f11 100644 --- a/diskwriter/diskwriter.go +++ b/diskwriter/diskwriter.go @@ -69,11 +69,11 @@ func (client *Client) Permissions() group.ClientPermissions { } } -func (client *Client) Status() map[string]interface{} { +func (client *Client) Data() map[string]interface{} { return nil } -func (client *Client) PushClient(group, kind, id, username string, permissions group.ClientPermissions, status map[string]interface{}) error { +func (client *Client) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error { return nil } diff --git a/group/client.go b/group/client.go index 7b0d0d3..fdadc91 100644 --- a/group/client.go +++ b/group/client.go @@ -100,10 +100,10 @@ type Client interface { Username() string Permissions() ClientPermissions SetPermissions(ClientPermissions) - Status() map[string]interface{} + Data() map[string]interface{} PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error RequestConns(target Client, g *Group, id string) error Joined(group, kind string) error - PushClient(group, kind, id, username string, permissions ClientPermissions, status map[string]interface{}) error + PushClient(group, kind, id, username string, permissions ClientPermissions, data map[string]interface{}) error Kick(id, user, message string) error } diff --git a/group/group.go b/group/group.go index e54e1c5..fd01374 100644 --- a/group/group.go +++ b/group/group.go @@ -76,6 +76,7 @@ type Group struct { clients map[string]Client history []ChatHistoryEntry timestamp time.Time + data map[string]interface{} } func (g *Group) Name() string { @@ -107,6 +108,32 @@ func (g *Group) SetLocked(locked bool, message string) { } } +func (g *Group) Data() map[string]interface{} { + g.mu.Lock() + defer g.mu.Unlock() + return g.data +} + +func (g *Group) UpdateData(d map[string]interface{}) { + g.mu.Lock() + if g.data == nil { + g.data = make(map[string]interface{}) + } + for k, v := range d { + if v == nil { + delete(g.data, k) + } else { + g.data[k] = v + } + } + clients := g.getClientsUnlocked(nil) + g.mu.Unlock() + + for _, c := range clients { + c.Joined(g.Name(), "change") + } +} + func (g *Group) Description() *Description { g.mu.Lock() defer g.mu.Unlock() @@ -577,12 +604,12 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error) id := c.Id() u := c.Username() p := c.Permissions() - s := c.Status() + s := c.Data() c.PushClient(g.Name(), "add", c.Id(), u, p, s) for _, cc := range clients { pp := cc.Permissions() c.PushClient( - g.Name(), "add", cc.Id(), cc.Username(), pp, cc.Status(), + g.Name(), "add", cc.Id(), cc.Username(), pp, cc.Data(), ) cc.PushClient(g.Name(), "add", id, u, p, s) } @@ -1070,7 +1097,7 @@ type Status struct { ClientCount *int `json:"clientCount,omitempty"` } -func GetStatus(g *Group, authentified bool) Status { +func (g *Group) Status (authentified bool) Status { desc := g.Description() d := Status{ Name: g.name, @@ -1092,7 +1119,7 @@ func GetPublic() []Status { gs := make([]Status, 0) Range(func(g *Group) bool { if g.Description().Public { - gs = append(gs, GetStatus(g, false)) + gs = append(gs, g.Status(false)) } return true }) diff --git a/rtpconn/webclient.go b/rtpconn/webclient.go index 461b472..532cd7b 100644 --- a/rtpconn/webclient.go +++ b/rtpconn/webclient.go @@ -57,7 +57,7 @@ type webClient struct { id string username string permissions group.ClientPermissions - status map[string]interface{} + data map[string]interface{} requested map[string][]string done chan struct{} writeCh chan interface{} @@ -90,17 +90,17 @@ func (c *webClient) Permissions() group.ClientPermissions { return c.permissions } -func (c *webClient) Status() map[string]interface{} { - return c.status +func (c *webClient) Data() map[string]interface{} { + return c.data } func (c *webClient) SetPermissions(perms group.ClientPermissions) { c.permissions = perms } -func (c *webClient) PushClient(group, kind, id, username string, permissions group.ClientPermissions, status map[string]interface{}) error { +func (c *webClient) PushClient(group, kind, id, username string, permissions group.ClientPermissions, data map[string]interface{}) error { return c.action(pushClientAction{ - group, kind, id, username, permissions, status, + group, kind, id, username, permissions, data, }) } @@ -115,7 +115,8 @@ type clientMessage struct { Password string `json:"password,omitempty"` Privileged bool `json:"privileged,omitempty"` Permissions *group.ClientPermissions `json:"permissions,omitempty"` - Status interface{} `json:"status,omitempty"` + Status *group.Status `json:"status,omitempty"` + Data map[string]interface{} `json:"data,omitempty"` Group string `json:"group,omitempty"` Value interface{} `json:"value,omitempty"` NoEcho bool `json:"noecho,omitempty"` @@ -899,7 +900,7 @@ type pushClientAction struct { id string username string permissions group.ClientPermissions - status map[string]interface{} + data map[string]interface{} } type permissionsChangedAction struct{} @@ -1106,14 +1107,17 @@ func handleAction(c *webClient, a interface{}) error { Id: a.id, Username: a.username, Permissions: &a.permissions, - Status: a.status, + Data: a.data, }) case joinedAction: - var status interface{} + var status *group.Status + var data map[string]interface{} if a.group != "" { g := group.Get(a.group) if g != nil { - status = group.GetStatus(g, true) + s := g.Status(true) + status = &s + data = g.Data() } } perms := c.permissions @@ -1124,6 +1128,7 @@ func handleAction(c *webClient, a interface{}) error { Username: c.username, Permissions: &perms, Status: status, + Data: data, RTCConfiguration: ice.ICEConfiguration(), }) case permissionsChangedAction: @@ -1132,13 +1137,14 @@ func handleAction(c *webClient, a interface{}) error { return errors.New("Permissions changed in no group") } perms := c.permissions + status := g.Status(true) c.write(clientMessage{ Type: "joined", Kind: "change", Group: g.Name(), Username: c.username, Permissions: &perms, - Status: group.GetStatus(g, true), + Status: &status, RTCConfiguration: ice.ICEConfiguration(), }) if !c.permissions.Present { @@ -1157,12 +1163,12 @@ func handleAction(c *webClient, a interface{}) error { } id := c.Id() user := c.Username() - s := c.Status() + d := c.Data() clients := g.GetClients(nil) go func(clients []group.Client) { for _, cc := range clients { cc.PushClient( - g.Name(), "change", id, user, perms, s, + g.Name(), "change", id, user, perms, d, ) } }(clients) @@ -1214,7 +1220,7 @@ func leaveGroup(c *webClient) { group.DelClient(c) c.permissions = group.ClientPermissions{} - c.status = nil + c.data = nil c.requested = make(map[string][]string) c.group = nil } @@ -1321,15 +1327,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { ) } c.username = m.Username - if m.Status != nil { - s, ok := m.Status.(map[string]interface{}) - if !ok { - return group.ProtocolError( - "bad type for status", - ) - } - c.status = s - } + c.data = m.Data g, err := group.AddClient(m.Group, c, group.ClientCredentials{ Username: m.Username, @@ -1615,6 +1613,17 @@ func handleClientMessage(c *webClient, m clientMessage) error { Time: group.ToJSTime(time.Now()), Value: s, }) + case "setdata": + if !c.permissions.Op { + return c.error(group.UserError("not authorised")) + } + data, ok := m.Value.(map[string]interface{}) + if !ok { + return c.error(group.UserError( + "Bad value in setdata", + )) + } + g.UpdateData(data) default: return group.ProtocolError("unknown group action") } @@ -1645,35 +1654,35 @@ func handleClientMessage(c *webClient, m clientMessage) error { if err != nil { return c.error(err) } - case "setstatus": + case "setdata": if m.Dest != c.Id() { return c.error(group.UserError("not authorised")) } - s, ok := m.Value.(map[string]interface{}) + data, ok := m.Value.(map[string]interface{}) if !ok { return c.error(group.UserError( - "Bad value in setstatus", + "Bad value in setdata", )) } - if c.status == nil { - c.status = make(map[string]interface{}) + if c.data == nil { + c.data = make(map[string]interface{}) } - for k, v := range s { + for k, v := range data { if v == nil { - delete(c.status, k) + delete(c.data, k) } else { - c.status[k] = v + c.data[k] = v } } id := c.Id() user := c.Username() perms := c.Permissions() - status := c.Status() + data = c.Data() go func(clients []group.Client) { for _, cc := range clients { cc.PushClient( g.Name(), "change", - id, user, perms, status, + id, user, perms, data, ) } }(g.GetClients(nil)) diff --git a/static/galene.js b/static/galene.js index 192cee4..0f631f8 100644 --- a/static/galene.js +++ b/static/galene.js @@ -1969,7 +1969,7 @@ function addUser(id, userinfo) { user.id = 'user-' + id; user.classList.add("user-p"); user.textContent = userinfo.username ? userinfo.username : '(anon)'; - if (userinfo.status.raisehand) + if (userinfo.data.raisehand) user.classList.add('user-status-raisehand'); else user.classList.remove('user-status-raisehand'); @@ -2001,7 +2001,7 @@ function changeUser(id, userinfo) { return; } user.textContent = userinfo.username ? userinfo.username : '(anon)'; - if (userinfo.status.raisehand) + if (userinfo.data.raisehand) user.classList.add('user-status-raisehand'); else user.classList.remove('user-status-raisehand'); @@ -2084,9 +2084,10 @@ function setTitle(title) { * @param {string} group * @param {Object} perms * @param {Object} status + * @param {Object} data * @param {string} message */ -async function gotJoined(kind, group, perms, status, message) { +async function gotJoined(kind, group, perms, status, data, message) { let present = presentRequested; presentRequested = null; @@ -2698,7 +2699,7 @@ commands.raise = { description: 'raise hand', f: (c, r) => { serverConnection.userAction( - "setstatus", serverConnection.id, {"raisehand": true}, + "setdata", serverConnection.id, {"raisehand": true}, ); } } @@ -2707,7 +2708,7 @@ commands.unraise = { description: 'unraise hand', f: (c, r) => { serverConnection.userAction( - "setstatus", serverConnection.id, {"raisehand": null}, + "setdata", serverConnection.id, {"raisehand": null}, ); } } diff --git a/static/protocol.js b/static/protocol.js index 766be50..e8d65f2 100644 --- a/static/protocol.js +++ b/static/protocol.js @@ -64,7 +64,7 @@ function newLocalId() { * @typedef {Object} user * @property {string} username * @property {Object} permissions - * @property {Object} status + * @property {Object} data * @property {Object>} down */ @@ -164,7 +164,7 @@ function ServerConnection() { * * kind is one of 'join', 'fail', 'change' or 'leave'. * - * @type{(this: ServerConnection, kind: string, group: string, permissions: Object, status: Object, message: string) => void} + * @type{(this: ServerConnection, kind: string, group: string, permissions: Object, status: Object, data: Object, message: string) => void} */ this.onjoined = null; /** @@ -208,6 +208,7 @@ function ServerConnection() { * @property {boolean} [privileged] * @property {Object} [permissions] * @property {Object} [status] + * @property {Object} [data] * @property {string} [group] * @property {unknown} [value] * @property {boolean} [noecho] @@ -284,7 +285,7 @@ ServerConnection.prototype.connect = async function(url) { sc.onuser.call(sc, id, 'delete'); } if(sc.group && sc.onjoined) - sc.onjoined.call(sc, 'leave', sc.group, {}, {}, ''); + sc.onjoined.call(sc, 'leave', sc.group, {}, {}, {}, ''); sc.group = null; sc.username = null; if(sc.onclose) @@ -336,7 +337,7 @@ ServerConnection.prototype.connect = async function(url) { if(sc.onjoined) sc.onjoined.call(sc, m.kind, m.group, m.permissions || {}, - m.status, + m.status, m.data, m.value || null); break; case 'user': @@ -347,7 +348,7 @@ ServerConnection.prototype.connect = async function(url) { sc.users[m.id] = { username: m.username, permissions: m.permissions || {}, - status: m.status || {}, + data: m.data || {}, down: {}, }; break; @@ -357,13 +358,13 @@ ServerConnection.prototype.connect = async function(url) { sc.users[m.id] = { username: m.username, permissions: m.permissions || {}, - status: m.status || {}, + data: m.data || {}, down: {}, }; } else { sc.users[m.id].username = m.username; sc.users[m.id].permissions = m.permissions || {}; - sc.users[m.id].status = m.status || {}; + sc.users[m.id].data = m.data || {}; } break; case 'delete': diff --git a/webserver/webserver.go b/webserver/webserver.go index 537347b..a97ea62 100644 --- a/webserver/webserver.go +++ b/webserver/webserver.go @@ -339,7 +339,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) { return } - d := group.GetStatus(g, false) + d := g.Status(false) w.Header().Set("content-type", "application/json") w.Header().Set("cache-control", "no-cache")