Handle UserState and UserRemove (with a few exceptions!)

This commit is contained in:
Mikkel Krautz 2010-11-27 14:22:47 +01:00
parent 4903558e4a
commit 925e23b0f9
4 changed files with 235 additions and 28 deletions

View file

@ -37,12 +37,21 @@ type Client struct {
udp bool
// Personal
UserId int
Session uint32
Username string
Hash string
Tokens []string
Channel *Channel
UserId int
Session uint32
Username string
Hash string
Tokens []string
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.
@ -51,18 +60,28 @@ func (client *Client) Panic(reason string) {
client.Disconnect()
}
func (client *Client) Disconnect() {
// Internal disconnect function
func (client *Client) disconnect(kicked bool) {
if !client.disconnected {
client.disconnected = true
close(client.udprecv)
close(client.msgchan)
client.conn.Close()
client.server.RemoveClient(client)
client.server.RemoveClient(client, kicked)
}
}
// 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
func (client *Client) readProtoMessage() (msg *Message, err os.Error) {
var length uint32

View file

@ -132,7 +132,48 @@ func (server *Server) handleChannelRemoveMessage(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) {
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) {
@ -143,13 +184,19 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
client.Panic(err.String())
}
if userstate.Session == nil {
log.Printf("UserState without session.")
actor, ok := server.clients[client.Session]
if !ok {
log.Printf("handleUserState: !")
return
}
actor := server.clients[client.Session]
user := server.clients[*userstate.Session]
user := actor
if userstate.Session != nil {
user, ok = server.clients[*userstate.Session]
if !ok {
log.Printf("Invalid session in UserState message")
return
}
}
log.Printf("actor = %v", actor)
log.Printf("user = %v", user)
@ -206,7 +253,6 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
// Comment set/clear
if userstate.Comment != nil {
comment := *userstate.Comment
log.Printf("comment = %v", comment)
// Clearing another user's comment.
if user != actor {
@ -266,9 +312,99 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
userstate.Recording != nil) {
client.Panic("Invalid UserState")
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) {

View file

@ -7,11 +7,13 @@ package main
import (
"log"
"crypto/tls"
"crypto/sha1"
"os"
"net"
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"sync"
"goprotobuf.googlecode.com/hg/proto"
"mumbleproto"
@ -133,7 +135,7 @@ func (server *Server) NewClient(conn net.Conn) (err os.Error) {
// Remove a disconnected client from the server's
// internal representation.
func (server *Server) RemoveClient(client *Client) {
func (server *Server) RemoveClient(client *Client, kicked bool) {
server.hmutex.Lock()
if client.udpaddr != nil {
host := client.udpaddr.IP.String()
@ -153,13 +155,20 @@ func (server *Server) RemoveClient(client *Client) {
// Remove client from channel
channel := client.Channel
channel.RemoveClient(client)
if channel != nil {
channel.RemoveClient(client)
}
err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{
Session: proto.Uint32(client.Session),
})
if err != nil {
// server panic
// If the user was not kicked, broadcast a UserRemove message.
// If the user is disconnect via a kick, the UserRemove message has already been sent
// at this point.
if !kicked {
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
// 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.
client.crypt, err = cryptstate.New()
if err != nil {
@ -289,9 +313,6 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
server.hclients[host] = append(server.hclients[host], client)
server.hmutex.Unlock()
// Broadcast the the user entered a channel
server.root.AddClient(client)
if client.Username == "SuperUser" {
client.UserId = 0
}
@ -304,8 +325,9 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
if client.UserId >= 0 {
userstate.UserId = proto.Uint32(uint32(client.UserId))
}
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
client.Panic(err.String())
server.userEnterChannel(client, server.root, userstate)
if err := server.broadcastProtoMessage(MessageUserState, userstate); err != nil {
// Server panic?
}
server.sendUserList(client)
@ -410,8 +432,9 @@ func (server *Server) sendUserList(client *Client) {
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
Session: proto.Uint32(user.Session),
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 {
// Server panic?
@ -618,6 +641,34 @@ func (s *Server) ClearACLCache() {
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.
func (s *Server) ListenAndMurmur() {

View file

@ -72,6 +72,7 @@ func NewTLSListener(port int) (rl *tls.Listener) {
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0].Certificate = [][]byte{cert.Bytes}
config.Certificates[0].PrivateKey = priv
config.AuthenticateClient = true
l, err := net.ListenTCP("tcp", &net.TCPAddr{
net.ParseIP("0.0.0.0"),