Commit 24187430 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

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.
parent 710cc3cc
...@@ -73,7 +73,7 @@ will trigger. There, you update your user interface and request incoming ...@@ -73,7 +73,7 @@ will trigger. There, you update your user interface and request incoming
streams: streams:
```javascript ```javascript
serverConnection.onjoined = function(kind, group, perms, status, message) { serverConnection.onjoined = function(kind, group, perms, status, data, message) {
switch(kind) { switch(kind) {
case 'join': case 'join':
this.request({'':['audio','video']}); this.request({'':['audio','video']});
......
...@@ -108,7 +108,7 @@ The `join` message requests that the sender join or leave a group: ...@@ -108,7 +108,7 @@ The `join` message requests that the sender join or leave a group:
group: group, group: group,
username: username, username: username,
password: password, password: password,
status: status data: data
} }
``` ```
...@@ -125,6 +125,7 @@ its permissions or in the recommended RTC configuration. ...@@ -125,6 +125,7 @@ its permissions or in the recommended RTC configuration.
username: username, username: username,
permissions: permissions, permissions: permissions,
status: status, status: status,
data: data,
rtcConfiguration: RTCConfiguration rtcConfiguration: RTCConfiguration
} }
``` ```
...@@ -337,7 +338,7 @@ A user action requests that the server act upon a user. ...@@ -337,7 +338,7 @@ A user action requests that the server act upon a user.
} }
``` ```
Currently defined kinds include `op`, `unop`, `present`, `unpresent`, 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. 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. ...@@ -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 Currently defined kinds include `clearchat` (not to be confused with the
`clearchat` user message), `lock`, `unlock`, `record`, `unrecord` and `clearchat` user message), `lock`, `unlock`, `record`, `unrecord`,
`subgroups`. `subgroups` and `setdata`.
...@@ -69,11 +69,11 @@ func (client *Client) Permissions() group.ClientPermissions { ...@@ -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 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 return nil
} }
......
...@@ -100,10 +100,10 @@ type Client interface { ...@@ -100,10 +100,10 @@ type Client interface {
Username() string Username() string
Permissions() ClientPermissions Permissions() ClientPermissions
SetPermissions(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 PushConn(g *Group, id string, conn conn.Up, tracks []conn.UpTrack, replace string) error
RequestConns(target Client, g *Group, id string) error RequestConns(target Client, g *Group, id string) error
Joined(group, kind 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 Kick(id, user, message string) error
} }
...@@ -76,6 +76,7 @@ type Group struct { ...@@ -76,6 +76,7 @@ type Group struct {
clients map[string]Client clients map[string]Client
history []ChatHistoryEntry history []ChatHistoryEntry
timestamp time.Time timestamp time.Time
data map[string]interface{}
} }
func (g *Group) Name() string { func (g *Group) Name() string {
...@@ -107,6 +108,32 @@ func (g *Group) SetLocked(locked bool, message 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 { func (g *Group) Description() *Description {
g.mu.Lock() g.mu.Lock()
defer g.mu.Unlock() defer g.mu.Unlock()
...@@ -577,12 +604,12 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error) ...@@ -577,12 +604,12 @@ func AddClient(group string, c Client, creds ClientCredentials) (*Group, error)
id := c.Id() id := c.Id()
u := c.Username() u := c.Username()
p := c.Permissions() p := c.Permissions()
s := c.Status() s := c.Data()
c.PushClient(g.Name(), "add", c.Id(), u, p, s) c.PushClient(g.Name(), "add", c.Id(), u, p, s)
for _, cc := range clients { for _, cc := range clients {
pp := cc.Permissions() pp := cc.Permissions()
c.PushClient( 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) cc.PushClient(g.Name(), "add", id, u, p, s)
} }
...@@ -1070,7 +1097,7 @@ type Status struct { ...@@ -1070,7 +1097,7 @@ type Status struct {
ClientCount *int `json:"clientCount,omitempty"` ClientCount *int `json:"clientCount,omitempty"`
} }
func GetStatus(g *Group, authentified bool) Status { func (g *Group) Status (authentified bool) Status {
desc := g.Description() desc := g.Description()
d := Status{ d := Status{
Name: g.name, Name: g.name,
...@@ -1092,7 +1119,7 @@ func GetPublic() []Status { ...@@ -1092,7 +1119,7 @@ func GetPublic() []Status {
gs := make([]Status, 0) gs := make([]Status, 0)
Range(func(g *Group) bool { Range(func(g *Group) bool {
if g.Description().Public { if g.Description().Public {
gs = append(gs, GetStatus(g, false)) gs = append(gs, g.Status(false))
} }
return true return true
}) })
......
...@@ -57,7 +57,7 @@ type webClient struct { ...@@ -57,7 +57,7 @@ type webClient struct {
id string id string
username string username string
permissions group.ClientPermissions permissions group.ClientPermissions
status map[string]interface{} data map[string]interface{}
requested map[string][]string requested map[string][]string
done chan struct{} done chan struct{}
writeCh chan interface{} writeCh chan interface{}
...@@ -90,17 +90,17 @@ func (c *webClient) Permissions() group.ClientPermissions { ...@@ -90,17 +90,17 @@ func (c *webClient) Permissions() group.ClientPermissions {
return c.permissions return c.permissions
} }
func (c *webClient) Status() map[string]interface{} { func (c *webClient) Data() map[string]interface{} {
return c.status return c.data
} }
func (c *webClient) SetPermissions(perms group.ClientPermissions) { func (c *webClient) SetPermissions(perms group.ClientPermissions) {
c.permissions = perms 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{ return c.action(pushClientAction{
group, kind, id, username, permissions, status, group, kind, id, username, permissions, data,
}) })
} }
...@@ -115,7 +115,8 @@ type clientMessage struct { ...@@ -115,7 +115,8 @@ type clientMessage struct {
Password string `json:"password,omitempty"` Password string `json:"password,omitempty"`
Privileged bool `json:"privileged,omitempty"` Privileged bool `json:"privileged,omitempty"`
Permissions *group.ClientPermissions `json:"permissions,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"` Group string `json:"group,omitempty"`
Value interface{} `json:"value,omitempty"` Value interface{} `json:"value,omitempty"`
NoEcho bool `json:"noecho,omitempty"` NoEcho bool `json:"noecho,omitempty"`
...@@ -899,7 +900,7 @@ type pushClientAction struct { ...@@ -899,7 +900,7 @@ type pushClientAction struct {
id string id string
username string username string
permissions group.ClientPermissions permissions group.ClientPermissions
status map[string]interface{} data map[string]interface{}
} }
type permissionsChangedAction struct{} type permissionsChangedAction struct{}
...@@ -1106,14 +1107,17 @@ func handleAction(c *webClient, a interface{}) error { ...@@ -1106,14 +1107,17 @@ func handleAction(c *webClient, a interface{}) error {
Id: a.id, Id: a.id,
Username: a.username, Username: a.username,
Permissions: &a.permissions, Permissions: &a.permissions,
Status: a.status, Data: a.data,
}) })
case joinedAction: case joinedAction:
var status interface{} var status *group.Status
var data map[string]interface{}
if a.group != "" { if a.group != "" {
g := group.Get(a.group) g := group.Get(a.group)
if g != nil { if g != nil {
status = group.GetStatus(g, true) s := g.Status(true)
status = &s
data = g.Data()
} }
} }
perms := c.permissions perms := c.permissions
...@@ -1124,6 +1128,7 @@ func handleAction(c *webClient, a interface{}) error { ...@@ -1124,6 +1128,7 @@ func handleAction(c *webClient, a interface{}) error {
Username: c.username, Username: c.username,
Permissions: &perms, Permissions: &perms,
Status: status, Status: status,
Data: data,
RTCConfiguration: ice.ICEConfiguration(), RTCConfiguration: ice.ICEConfiguration(),
}) })
case permissionsChangedAction: case permissionsChangedAction:
...@@ -1132,13 +1137,14 @@ func handleAction(c *webClient, a interface{}) error { ...@@ -1132,13 +1137,14 @@ func handleAction(c *webClient, a interface{}) error {
return errors.New("Permissions changed in no group") return errors.New("Permissions changed in no group")
} }
perms := c.permissions perms := c.permissions
status := g.Status(true)
c.write(clientMessage{ c.write(clientMessage{
Type: "joined", Type: "joined",
Kind: "change", Kind: "change",
Group: g.Name(), Group: g.Name(),
Username: c.username, Username: c.username,
Permissions: &perms, Permissions: &perms,
Status: group.GetStatus(g, true), Status: &status,
RTCConfiguration: ice.ICEConfiguration(), RTCConfiguration: ice.ICEConfiguration(),
}) })
if !c.permissions.Present { if !c.permissions.Present {
...@@ -1157,12 +1163,12 @@ func handleAction(c *webClient, a interface{}) error { ...@@ -1157,12 +1163,12 @@ func handleAction(c *webClient, a interface{}) error {
} }
id := c.Id() id := c.Id()
user := c.Username() user := c.Username()
s := c.Status() d := c.Data()
clients := g.GetClients(nil) clients := g.GetClients(nil)
go func(clients []group.Client) { go func(clients []group.Client) {
for _, cc := range clients { for _, cc := range clients {
cc.PushClient( cc.PushClient(
g.Name(), "change", id, user, perms, s, g.Name(), "change", id, user, perms, d,
) )
} }
}(clients) }(clients)
...@@ -1214,7 +1220,7 @@ func leaveGroup(c *webClient) { ...@@ -1214,7 +1220,7 @@ func leaveGroup(c *webClient) {
group.DelClient(c) group.DelClient(c)
c.permissions = group.ClientPermissions{} c.permissions = group.ClientPermissions{}
c.status = nil c.data = nil
c.requested = make(map[string][]string) c.requested = make(map[string][]string)
c.group = nil c.group = nil
} }
...@@ -1321,15 +1327,7 @@ func handleClientMessage(c *webClient, m clientMessage) error { ...@@ -1321,15 +1327,7 @@ func handleClientMessage(c *webClient, m clientMessage) error {
) )
} }
c.username = m.Username c.username = m.Username
if m.Status != nil { c.data = m.Data
s, ok := m.Status.(map[string]interface{})
if !ok {
return group.ProtocolError(
"bad type for status",
)
}
c.status = s
}
g, err := group.AddClient(m.Group, c, g, err := group.AddClient(m.Group, c,
group.ClientCredentials{ group.ClientCredentials{
Username: m.Username, Username: m.Username,
...@@ -1615,6 +1613,17 @@ func handleClientMessage(c *webClient, m clientMessage) error { ...@@ -1615,6 +1613,17 @@ func handleClientMessage(c *webClient, m clientMessage) error {
Time: group.ToJSTime(time.Now()), Time: group.ToJSTime(time.Now()),
Value: s, 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: default:
return group.ProtocolError("unknown group action") return group.ProtocolError("unknown group action")
} }
...@@ -1645,35 +1654,35 @@ func handleClientMessage(c *webClient, m clientMessage) error { ...@@ -1645,35 +1654,35 @@ func handleClientMessage(c *webClient, m clientMessage) error {
if err != nil { if err != nil {
return c.error(err) return c.error(err)
} }
case "setstatus": case "setdata":
if m.Dest != c.Id() { if m.Dest != c.Id() {
return c.error(group.UserError("not authorised")) return c.error(group.UserError("not authorised"))
} }
s, 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(group.UserError(
"Bad value in setstatus", "Bad value in setdata",
)) ))
} }
if c.status == nil { if c.data == nil {
c.status = make(map[string]interface{}) c.data = make(map[string]interface{})
} }
for k, v := range s { for k, v := range data {
if v == nil { if v == nil {
delete(c.status, k) delete(c.data, k)
} else { } else {
c.status[k] = v c.data[k] = v
} }
} }
id := c.Id() id := c.Id()
user := c.Username() user := c.Username()
perms := c.Permissions() perms := c.Permissions()
status := c.Status() data = c.Data()
go func(clients []group.Client) { go func(clients []group.Client) {
for _, cc := range clients { for _, cc := range clients {
cc.PushClient( cc.PushClient(
g.Name(), "change", g.Name(), "change",
id, user, perms, status, id, user, perms, data,
) )
} }
}(g.GetClients(nil)) }(g.GetClients(nil))
......
...@@ -1969,7 +1969,7 @@ function addUser(id, userinfo) { ...@@ -1969,7 +1969,7 @@ function addUser(id, userinfo) {
user.id = 'user-' + id; user.id = 'user-' + id;
user.classList.add("user-p"); user.classList.add("user-p");
user.textContent = userinfo.username ? userinfo.username : '(anon)'; user.textContent = userinfo.username ? userinfo.username : '(anon)';
if (userinfo.status.raisehand) if (userinfo.data.raisehand)
user.classList.add('user-status-raisehand'); user.classList.add('user-status-raisehand');
else else
user.classList.remove('user-status-raisehand'); user.classList.remove('user-status-raisehand');
...@@ -2001,7 +2001,7 @@ function changeUser(id, userinfo) { ...@@ -2001,7 +2001,7 @@ function changeUser(id, userinfo) {
return; return;
} }
user.textContent = userinfo.username ? userinfo.username : '(anon)'; user.textContent = userinfo.username ? userinfo.username : '(anon)';
if (userinfo.status.raisehand) if (userinfo.data.raisehand)
user.classList.add('user-status-raisehand'); user.classList.add('user-status-raisehand');
else else
user.classList.remove('user-status-raisehand'); user.classList.remove('user-status-raisehand');
...@@ -2084,9 +2084,10 @@ function setTitle(title) { ...@@ -2084,9 +2084,10 @@ function setTitle(title) {
* @param {string} group * @param {string} group
* @param {Object<string,boolean>} perms * @param {Object<string,boolean>} perms
* @param {Object<string,any>} status * @param {Object<string,any>} status
* @param {Object<string,any>} data
* @param {string} message * @param {string} message
*/ */
async function gotJoined(kind, group, perms, status, message) { async function gotJoined(kind, group, perms, status, data, message) {
let present = presentRequested; let present = presentRequested;
presentRequested = null; presentRequested = null;
...@@ -2698,7 +2699,7 @@ commands.raise = { ...@@ -2698,7 +2699,7 @@ commands.raise = {
description: 'raise hand', description: 'raise hand',
f: (c, r) => { f: (c, r) => {
serverConnection.userAction( serverConnection.userAction(
"setstatus", serverConnection.id, {"raisehand": true}, "setdata", serverConnection.id, {"raisehand": true},
); );
} }
} }
...@@ -2707,7 +2708,7 @@ commands.unraise = { ...@@ -2707,7 +2708,7 @@ commands.unraise = {
description: 'unraise hand', description: 'unraise hand',
f: (c, r) => { f: (c, r) => {
serverConnection.userAction( serverConnection.userAction(
"setstatus", serverConnection.id, {"raisehand": null}, "setdata", serverConnection.id, {"raisehand": null},
); );
} }
} }
......
...@@ -64,7 +64,7 @@ function newLocalId() { ...@@ -64,7 +64,7 @@ function newLocalId() {
* @typedef {Object} user * @typedef {Object} user
* @property {string} username * @property {string} username
* @property {Object<string,boolean>} permissions * @property {Object<string,boolean>} permissions
* @property {Object<string,any>} status * @property {Object<string,any>} data
* @property {Object<string,Object<string,boolean>>} down * @property {Object<string,Object<string,boolean>>} down
*/ */
...@@ -164,7 +164,7 @@ function ServerConnection() { ...@@ -164,7 +164,7 @@ function ServerConnection() {
* *
* kind is one of 'join', 'fail', 'change' or 'leave'. * kind is one of 'join', 'fail', 'change' or 'leave'.
* *
* @type{(this: ServerConnection, kind: string, group: string, permissions: Object<string,boolean>, status: Object<string,any>, message: string) => void} * @type{(this: ServerConnection, kind: string, group: string, permissions: Object<string,boolean>, status: Object<string,any>, data: Object<string,any>, message: string) => void}
*/ */
this.onjoined = null; this.onjoined = null;
/** /**
...@@ -208,6 +208,7 @@ function ServerConnection() { ...@@ -208,6 +208,7 @@ function ServerConnection() {
* @property {boolean} [privileged] * @property {boolean} [privileged]
* @property {Object<string,boolean>} [permissions] * @property {Object<string,boolean>} [permissions]
* @property {Object<string,any>} [status] * @property {Object<string,any>} [status]
* @property {Object<string,any>} [data]
* @property {string} [group] * @property {string} [group]
* @property {unknown} [value] * @property {unknown} [value]
* @property {boolean} [noecho] * @property {boolean} [noecho]
...@@ -284,7 +285,7 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -284,7 +285,7 @@ ServerConnection.prototype.connect = async function(url) {
sc.onuser.call(sc, id, 'delete'); sc.onuser.call(sc, id, 'delete');
} }
if(sc.group && sc.onjoined) if(sc.group && sc.onjoined)
sc.onjoined.call(sc, 'leave', sc.group, {}, {}, ''); sc.onjoined.call(sc, 'leave', sc.group, {}, {}, {}, '');
sc.group = null; sc.group = null;
sc.username = null; sc.username = null;
if(sc.onclose) if(sc.onclose)
...@@ -336,7 +337,7 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -336,7 +337,7 @@ ServerConnection.prototype.connect = async function(url) {
if(sc.onjoined) if(sc.onjoined)
sc.onjoined.call(sc, m.kind, m.group, sc.onjoined.call(sc, m.kind, m.group,
m.permissions || {}, m.permissions || {},
m.status, m.status, m.data,
m.value || null); m.value || null);
break; break;
case 'user': case 'user':
...@@ -347,7 +348,7 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -347,7 +348,7 @@ ServerConnection.prototype.connect = async function(url) {
sc.users[m.id] = { sc.users[m.id] = {
username: m.username, username: m.username,
permissions: m.permissions || {}, permissions: m.permissions || {},
status: m.status || {}, data: m.data || {},
down: {}, down: {},
}; };
break; break;
...@@ -357,13 +358,13 @@ ServerConnection.prototype.connect = async function(url) { ...@@ -357,13 +358,13 @@ ServerConnection.prototype.connect = async function(url) {
sc.users[m.id] = { sc.users[m.id] = {
username: m.username, username: m.username,
permissions: m.permissions || {}, permissions: m.permissions || {},
status: m.status || {}, data: m.data || {},
down: {}, down: {},
}; };
} else { } else {
sc.users[m.id].username = m.username; 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 || {}; sc.users[m.id].data = m.data || {};
} }
break; break;
case 'delete': case 'delete':
......
...@@ -339,7 +339,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) { ...@@ -339,7 +339,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
d := group.GetStatus(g, false) d := g.Status(false)
w.Header().Set("content-type", "application/json") w.Header().Set("content-type", "application/json")
w.Header().Set("cache-control", "no-cache") w.Header().Set("cache-control", "no-cache")
......
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