mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -08:00
Handle UserState and UserRemove (with a few exceptions!)
This commit is contained in:
parent
4903558e4a
commit
925e23b0f9
4 changed files with 235 additions and 28 deletions
37
client.go
37
client.go
|
|
@ -37,12 +37,21 @@ type Client struct {
|
||||||
udp bool
|
udp bool
|
||||||
|
|
||||||
// Personal
|
// Personal
|
||||||
UserId int
|
UserId int
|
||||||
Session uint32
|
Session uint32
|
||||||
Username string
|
Username string
|
||||||
Hash string
|
Hash string
|
||||||
Tokens []string
|
Tokens []string
|
||||||
Channel *Channel
|
Channel *Channel
|
||||||
|
SelfMute bool
|
||||||
|
SelfDeaf bool
|
||||||
|
Mute bool
|
||||||
|
Deaf bool
|
||||||
|
Suppress bool
|
||||||
|
PrioritySpeaker bool
|
||||||
|
Recording bool
|
||||||
|
PluginContext []byte
|
||||||
|
PluginIdentity string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Something invalid happened on the wire.
|
// Something invalid happened on the wire.
|
||||||
|
|
@ -51,18 +60,28 @@ func (client *Client) Panic(reason string) {
|
||||||
client.Disconnect()
|
client.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (client *Client) Disconnect() {
|
// Internal disconnect function
|
||||||
|
func (client *Client) disconnect(kicked bool) {
|
||||||
if !client.disconnected {
|
if !client.disconnected {
|
||||||
client.disconnected = true
|
client.disconnected = true
|
||||||
close(client.udprecv)
|
close(client.udprecv)
|
||||||
close(client.msgchan)
|
close(client.msgchan)
|
||||||
|
|
||||||
client.conn.Close()
|
client.conn.Close()
|
||||||
|
client.server.RemoveClient(client, kicked)
|
||||||
client.server.RemoveClient(client)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disconnect a client (client disconnected)
|
||||||
|
func (client *Client) Disconnect() {
|
||||||
|
client.disconnect(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disconnect a client (kick/ban)
|
||||||
|
func (client *Client) ForceDisconnect() {
|
||||||
|
client.disconnect(true)
|
||||||
|
}
|
||||||
|
|
||||||
// Read a protobuf message from a client
|
// Read a protobuf message from a client
|
||||||
func (client *Client) readProtoMessage() (msg *Message, err os.Error) {
|
func (client *Client) readProtoMessage() (msg *Message, err os.Error) {
|
||||||
var length uint32
|
var length uint32
|
||||||
|
|
|
||||||
148
message.go
148
message.go
|
|
@ -132,7 +132,48 @@ func (server *Server) handleChannelRemoveMessage(client *Client, msg *Message) {
|
||||||
func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
|
func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle a user remove packet. This can either be a client disconnecting, or a
|
||||||
|
// user kicking or kick-banning another player.
|
||||||
func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) {
|
func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) {
|
||||||
|
userremove := &mumbleproto.UserRemove{}
|
||||||
|
err := proto.Unmarshal(msg.buf, userremove)
|
||||||
|
if err != nil {
|
||||||
|
client.Panic(err.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the user to be removed.
|
||||||
|
user, ok := server.clients[*userremove.Session]
|
||||||
|
if !ok {
|
||||||
|
client.Panic("Invalid session in UserRemove message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ban := false
|
||||||
|
if userremove.Ban != nil {
|
||||||
|
ban = *userremove.Ban
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check user's permissions
|
||||||
|
perm := Permission(KickPermission)
|
||||||
|
if ban {
|
||||||
|
perm = Permission(BanPermission)
|
||||||
|
}
|
||||||
|
if user.UserId == 0 || !server.HasPermission(client, server.root, perm) {
|
||||||
|
client.sendPermissionDenied(client, server.root, perm)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ban {
|
||||||
|
log.Printf("handleUserRemove: Banning is not yet implemented.")
|
||||||
|
}
|
||||||
|
|
||||||
|
userremove.Actor = proto.Uint32(uint32(client.Session))
|
||||||
|
if err = server.broadcastProtoMessage(MessageUserRemove, userremove); err != nil {
|
||||||
|
log.Panic("Unable to broadcast UserRemove message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user.ForceDisconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
@ -143,13 +184,19 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
client.Panic(err.String())
|
client.Panic(err.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if userstate.Session == nil {
|
actor, ok := server.clients[client.Session]
|
||||||
log.Printf("UserState without session.")
|
if !ok {
|
||||||
|
log.Printf("handleUserState: !")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
user := actor
|
||||||
actor := server.clients[client.Session]
|
if userstate.Session != nil {
|
||||||
user := server.clients[*userstate.Session]
|
user, ok = server.clients[*userstate.Session]
|
||||||
|
if !ok {
|
||||||
|
log.Printf("Invalid session in UserState message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
log.Printf("actor = %v", actor)
|
log.Printf("actor = %v", actor)
|
||||||
log.Printf("user = %v", user)
|
log.Printf("user = %v", user)
|
||||||
|
|
@ -206,7 +253,6 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
// Comment set/clear
|
// Comment set/clear
|
||||||
if userstate.Comment != nil {
|
if userstate.Comment != nil {
|
||||||
comment := *userstate.Comment
|
comment := *userstate.Comment
|
||||||
log.Printf("comment = %v", comment)
|
|
||||||
|
|
||||||
// Clearing another user's comment.
|
// Clearing another user's comment.
|
||||||
if user != actor {
|
if user != actor {
|
||||||
|
|
@ -266,9 +312,99 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
|
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
|
||||||
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
|
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
|
||||||
userstate.Recording != nil) {
|
userstate.Recording != nil) {
|
||||||
|
client.Panic("Invalid UserState")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Printf("handleUserState: In the out-figuring state")
|
||||||
|
|
||||||
|
broadcast := false
|
||||||
|
if userstate.Texture != nil {
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.SelfDeaf != nil {
|
||||||
|
user.SelfDeaf = *userstate.SelfDeaf
|
||||||
|
if user.SelfDeaf {
|
||||||
|
userstate.SelfDeaf = proto.Bool(true)
|
||||||
|
user.SelfMute = true
|
||||||
|
}
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.SelfMute != nil {
|
||||||
|
user.SelfMute = *userstate.SelfMute
|
||||||
|
if !user.SelfMute {
|
||||||
|
userstate.SelfDeaf = proto.Bool(false)
|
||||||
|
user.SelfDeaf = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.PluginContext != nil {
|
||||||
|
user.PluginContext = userstate.PluginContext
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.PluginIdentity != nil {
|
||||||
|
user.PluginIdentity = *userstate.PluginIdentity
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.Comment != nil {
|
||||||
|
log.Printf("handleUserState: Comment unhandled")
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
|
||||||
|
if userstate.Deaf != nil {
|
||||||
|
user.Deaf = *userstate.Deaf
|
||||||
|
if user.Deaf {
|
||||||
|
userstate.Mute = proto.Bool(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if userstate.Mute != nil {
|
||||||
|
user.Mute = *userstate.Mute
|
||||||
|
if !user.Mute {
|
||||||
|
userstate.Deaf = proto.Bool(false)
|
||||||
|
user.Deaf = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if userstate.Suppress != nil {
|
||||||
|
user.Suppress = *userstate.Suppress
|
||||||
|
}
|
||||||
|
if userstate.PrioritySpeaker != nil {
|
||||||
|
user.PrioritySpeaker = *userstate.PrioritySpeaker
|
||||||
|
}
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.Recording != nil && *userstate.Recording != user.Recording {
|
||||||
|
user.Recording = *userstate.Recording
|
||||||
|
// fixme(mkrautz): Notify older clients of recording state change.
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.UserId != nil {
|
||||||
|
log.Printf("handleUserState: SelfRegister unhandled")
|
||||||
|
userstate.UserId = nil
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if userstate.ChannelId != nil {
|
||||||
|
channel, ok := server.channels[int(*userstate.ChannelId)]
|
||||||
|
if ok {
|
||||||
|
server.userEnterChannel(user, channel, userstate)
|
||||||
|
broadcast = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("broadcast = %v", broadcast)
|
||||||
|
if broadcast {
|
||||||
|
log.Printf("sending broadcast!")
|
||||||
|
err := server.broadcastProtoMessage(MessageUserState, userstate)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("handleUserState: failed to broadcast userstate")
|
||||||
|
log.Panic("Unable to broadcast UserState")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) handleBanListMessage(client *Client, msg *Message) {
|
func (server *Server) handleBanListMessage(client *Client, msg *Message) {
|
||||||
|
|
|
||||||
77
server.go
77
server.go
|
|
@ -7,11 +7,13 @@ package main
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"crypto/sha1"
|
||||||
"os"
|
"os"
|
||||||
"net"
|
"net"
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
"sync"
|
"sync"
|
||||||
"goprotobuf.googlecode.com/hg/proto"
|
"goprotobuf.googlecode.com/hg/proto"
|
||||||
"mumbleproto"
|
"mumbleproto"
|
||||||
|
|
@ -133,7 +135,7 @@ func (server *Server) NewClient(conn net.Conn) (err os.Error) {
|
||||||
|
|
||||||
// Remove a disconnected client from the server's
|
// Remove a disconnected client from the server's
|
||||||
// internal representation.
|
// internal representation.
|
||||||
func (server *Server) RemoveClient(client *Client) {
|
func (server *Server) RemoveClient(client *Client, kicked bool) {
|
||||||
server.hmutex.Lock()
|
server.hmutex.Lock()
|
||||||
if client.udpaddr != nil {
|
if client.udpaddr != nil {
|
||||||
host := client.udpaddr.IP.String()
|
host := client.udpaddr.IP.String()
|
||||||
|
|
@ -153,13 +155,20 @@ func (server *Server) RemoveClient(client *Client) {
|
||||||
|
|
||||||
// Remove client from channel
|
// Remove client from channel
|
||||||
channel := client.Channel
|
channel := client.Channel
|
||||||
channel.RemoveClient(client)
|
if channel != nil {
|
||||||
|
channel.RemoveClient(client)
|
||||||
|
}
|
||||||
|
|
||||||
err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{
|
// If the user was not kicked, broadcast a UserRemove message.
|
||||||
Session: proto.Uint32(client.Session),
|
// If the user is disconnect via a kick, the UserRemove message has already been sent
|
||||||
})
|
// at this point.
|
||||||
if err != nil {
|
if !kicked {
|
||||||
// server panic
|
err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{
|
||||||
|
Session: proto.Uint32(client.Session),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("Unable to broadcast UserRemove message for disconnected client.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,6 +244,21 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
|
||||||
|
|
||||||
client.Username = *auth.Username
|
client.Username = *auth.Username
|
||||||
|
|
||||||
|
// Extract certhash
|
||||||
|
tlsconn, ok := client.conn.(*tls.Conn)
|
||||||
|
if !ok {
|
||||||
|
client.Panic("Type assertion failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
certs := tlsconn.PeerCertificates()
|
||||||
|
if len(certs) > 0 {
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write(certs[0].Raw)
|
||||||
|
client.Hash = hex.EncodeToString(hash.Sum())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("hash=%s", client.Hash)
|
||||||
|
|
||||||
// Setup the cryptstate for the client.
|
// Setup the cryptstate for the client.
|
||||||
client.crypt, err = cryptstate.New()
|
client.crypt, err = cryptstate.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -289,9 +313,6 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
|
||||||
server.hclients[host] = append(server.hclients[host], client)
|
server.hclients[host] = append(server.hclients[host], client)
|
||||||
server.hmutex.Unlock()
|
server.hmutex.Unlock()
|
||||||
|
|
||||||
// Broadcast the the user entered a channel
|
|
||||||
server.root.AddClient(client)
|
|
||||||
|
|
||||||
if client.Username == "SuperUser" {
|
if client.Username == "SuperUser" {
|
||||||
client.UserId = 0
|
client.UserId = 0
|
||||||
}
|
}
|
||||||
|
|
@ -304,8 +325,9 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
|
||||||
if client.UserId >= 0 {
|
if client.UserId >= 0 {
|
||||||
userstate.UserId = proto.Uint32(uint32(client.UserId))
|
userstate.UserId = proto.Uint32(uint32(client.UserId))
|
||||||
}
|
}
|
||||||
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
|
server.userEnterChannel(client, server.root, userstate)
|
||||||
client.Panic(err.String())
|
if err := server.broadcastProtoMessage(MessageUserState, userstate); err != nil {
|
||||||
|
// Server panic?
|
||||||
}
|
}
|
||||||
|
|
||||||
server.sendUserList(client)
|
server.sendUserList(client)
|
||||||
|
|
@ -410,8 +432,9 @@ func (server *Server) sendUserList(client *Client) {
|
||||||
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
|
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||||
Session: proto.Uint32(user.Session),
|
Session: proto.Uint32(user.Session),
|
||||||
Name: proto.String(user.Username),
|
Name: proto.String(user.Username),
|
||||||
ChannelId: proto.Uint32(0),
|
ChannelId: proto.Uint32(uint32(user.Channel.Id)),
|
||||||
})
|
})
|
||||||
|
log.Printf("ChanId = %v", user.Channel.Id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Server panic?
|
// Server panic?
|
||||||
|
|
@ -618,6 +641,34 @@ func (s *Server) ClearACLCache() {
|
||||||
s.aclcache = NewACLCache()
|
s.aclcache = NewACLCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper method for users entering new channels
|
||||||
|
func (server *Server) userEnterChannel(client *Client, channel *Channel, userstate *mumbleproto.UserState) {
|
||||||
|
if client.Channel == channel {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
oldchan := client.Channel
|
||||||
|
if oldchan != nil {
|
||||||
|
oldchan.RemoveClient(client)
|
||||||
|
}
|
||||||
|
channel.AddClient(client)
|
||||||
|
|
||||||
|
server.ClearACLCache()
|
||||||
|
// fixme(mkrautz): Set LastChannel for user in datastore
|
||||||
|
// fixme(mkrautz): Remove channel if temporary
|
||||||
|
|
||||||
|
canspeak := server.HasPermission(client, channel, SpeakPermission)
|
||||||
|
if canspeak == client.Suppress {
|
||||||
|
client.Suppress = !canspeak
|
||||||
|
userstate.Suppress = proto.Bool(client.Suppress)
|
||||||
|
}
|
||||||
|
|
||||||
|
server.sendClientPermissions(client, channel)
|
||||||
|
if channel.parent != nil {
|
||||||
|
server.sendClientPermissions(client, channel.parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// The accept loop of the server.
|
// The accept loop of the server.
|
||||||
func (s *Server) ListenAndMurmur() {
|
func (s *Server) ListenAndMurmur() {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ func NewTLSListener(port int) (rl *tls.Listener) {
|
||||||
config.Certificates = make([]tls.Certificate, 1)
|
config.Certificates = make([]tls.Certificate, 1)
|
||||||
config.Certificates[0].Certificate = [][]byte{cert.Bytes}
|
config.Certificates[0].Certificate = [][]byte{cert.Bytes}
|
||||||
config.Certificates[0].PrivateKey = priv
|
config.Certificates[0].PrivateKey = priv
|
||||||
|
config.AuthenticateClient = true
|
||||||
|
|
||||||
l, err := net.ListenTCP("tcp", &net.TCPAddr{
|
l, err := net.ListenTCP("tcp", &net.TCPAddr{
|
||||||
net.ParseIP("0.0.0.0"),
|
net.ParseIP("0.0.0.0"),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue