Commit 3ba2394b authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Implement user statuses.

The server now maintains a set of statuses for each user that are not
interpreted by the server but communicated to the other members of the
group using 'user' messages.
parent f0a39fca
......@@ -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.
......
......@@ -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
}
......
......@@ -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
}
......@@ -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)
......
......@@ -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")
}
......
......@@ -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.
*
......
......@@ -64,6 +64,7 @@ function newLocalId() {
* @typedef {Object} user
* @property {string} username
* @property {Object<string,boolean>} permissions
* @property {Object<string,any>} status
*/
/**
......@@ -205,6 +206,7 @@ function ServerConnection() {
* @property {string} [password]
* @property {boolean} [privileged]
* @property {Object<string,boolean>} [permissions]
* @property {Object<string,any>} [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({
......
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