mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-19 21:59:59 -08:00
Support for UserState blobs (textures and comments)
This commit is contained in:
parent
a1bd184e50
commit
2f5328cbab
3 changed files with 170 additions and 32 deletions
22
client.go
22
client.go
|
|
@ -43,6 +43,12 @@ type Client struct {
|
||||||
OSName string
|
OSName string
|
||||||
OSVersion string
|
OSVersion string
|
||||||
|
|
||||||
|
// Blobs
|
||||||
|
Comment string
|
||||||
|
CommentHash []byte
|
||||||
|
Texture []byte
|
||||||
|
TextureHash []byte
|
||||||
|
|
||||||
// Personal
|
// Personal
|
||||||
UserId int
|
UserId int
|
||||||
Session uint32
|
Session uint32
|
||||||
|
|
@ -141,11 +147,19 @@ func (c *Client) sendProtoMessage(kind uint16, msg interface{}) (err os.Error) {
|
||||||
|
|
||||||
// Send permission denied by type
|
// Send permission denied by type
|
||||||
func (c *Client) sendPermissionDeniedType(kind string) {
|
func (c *Client) sendPermissionDeniedType(kind string) {
|
||||||
|
c.sendPermissionDeniedTypeUser(kind, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send permission denied by type (and user)
|
||||||
|
func (c *Client) sendPermissionDeniedTypeUser(kind string, user *Client) {
|
||||||
val, ok := mumbleproto.PermissionDenied_DenyType_value[kind]
|
val, ok := mumbleproto.PermissionDenied_DenyType_value[kind]
|
||||||
if ok {
|
if ok {
|
||||||
d, err := proto.Marshal(&mumbleproto.PermissionDenied{
|
pd := &mumbleproto.PermissionDenied{}
|
||||||
Type: mumbleproto.NewPermissionDenied_DenyType(val),
|
pd.Type = mumbleproto.NewPermissionDenied_DenyType(val)
|
||||||
})
|
if user != nil {
|
||||||
|
pd.Session = proto.Uint32(uint32(user.Session))
|
||||||
|
}
|
||||||
|
d, err := proto.Marshal(pd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Panic(err.String())
|
c.Panic(err.String())
|
||||||
return
|
return
|
||||||
|
|
@ -155,7 +169,7 @@ func (c *Client) sendPermissionDeniedType(kind string) {
|
||||||
kind: MessagePermissionDenied,
|
kind: MessagePermissionDenied,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Unknown permission denied type.")
|
log.Panic("Unknown permission denied type.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
178
message.go
178
message.go
|
|
@ -9,7 +9,9 @@ import (
|
||||||
"mumbleproto"
|
"mumbleproto"
|
||||||
"goprotobuf.googlecode.com/hg/proto"
|
"goprotobuf.googlecode.com/hg/proto"
|
||||||
"net"
|
"net"
|
||||||
|
"crypto/sha1"
|
||||||
"cryptstate"
|
"cryptstate"
|
||||||
|
"fmt"
|
||||||
)
|
)
|
||||||
|
|
||||||
// These are the different kinds of messages
|
// These are the different kinds of messages
|
||||||
|
|
@ -164,6 +166,7 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ban {
|
if ban {
|
||||||
|
// fixme(mkrautz): Implement banning.
|
||||||
log.Printf("handleUserRemove: Banning is not yet implemented.")
|
log.Printf("handleUserRemove: Banning is not yet implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,8 +179,8 @@ func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) {
|
||||||
user.ForceDisconnect()
|
user.ForceDisconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle user state changes
|
||||||
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
log.Printf("UserState!")
|
|
||||||
userstate := &mumbleproto.UserState{}
|
userstate := &mumbleproto.UserState{}
|
||||||
err := proto.Unmarshal(msg.buf, userstate)
|
err := proto.Unmarshal(msg.buf, userstate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -186,25 +189,22 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
actor, ok := server.clients[client.Session]
|
actor, ok := server.clients[client.Session]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("handleUserState: !")
|
log.Panic("Client not found in server's client map.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
user := actor
|
user := actor
|
||||||
if userstate.Session != nil {
|
if userstate.Session != nil {
|
||||||
user, ok = server.clients[*userstate.Session]
|
user, ok = server.clients[*userstate.Session]
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Printf("Invalid session in UserState message")
|
client.Panic("Invalid session in UserState message")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("actor = %v", actor)
|
|
||||||
log.Printf("user = %v", user)
|
|
||||||
|
|
||||||
userstate.Session = proto.Uint32(user.Session)
|
userstate.Session = proto.Uint32(user.Session)
|
||||||
userstate.Actor = proto.Uint32(actor.Session)
|
userstate.Actor = proto.Uint32(actor.Session)
|
||||||
|
|
||||||
// Has a channel ID
|
// Does it have a channel ID?
|
||||||
if userstate.ChannelId != nil {
|
if userstate.ChannelId != nil {
|
||||||
// Destination channel
|
// Destination channel
|
||||||
dstChan, ok := server.channels[int(*userstate.ChannelId)]
|
dstChan, ok := server.channels[int(*userstate.ChannelId)]
|
||||||
|
|
@ -212,22 +212,21 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the user and the actor aren't the same, check whether the actor has the 'move' permission
|
// If the user and the actor aren't the same, check whether the actor has MovePermission on
|
||||||
// on the user's channel to move.
|
// the user's curent channel.
|
||||||
if actor != user && !server.HasPermission(actor, user.Channel, MovePermission) {
|
if actor != user && !server.HasPermission(actor, user.Channel, MovePermission) {
|
||||||
client.sendPermissionDenied(actor, user.Channel, MovePermission)
|
client.sendPermissionDenied(actor, user.Channel, MovePermission)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the actor has 'move' permissions on dstChan. Check whether user has 'enter'
|
// Check whether the actor has MovePermission on dstChan. Check whether user has EnterPermission
|
||||||
// permissions on dstChan.
|
// on dstChan.
|
||||||
if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(user, dstChan, EnterPermission) {
|
if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(user, dstChan, EnterPermission) {
|
||||||
client.sendPermissionDenied(user, dstChan, EnterPermission)
|
client.sendPermissionDenied(user, dstChan, EnterPermission)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether the channel is full.
|
// fixme(mkrautz): Check whether the channel is full.
|
||||||
// fixme(mkrautz): See above.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
|
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
|
||||||
|
|
@ -265,6 +264,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Only allow empty text.
|
// Only allow empty text.
|
||||||
if len(comment) > 0 {
|
if len(comment) > 0 {
|
||||||
|
client.Panic("Cannot clear another user's comment")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -273,6 +273,9 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Only set the comment if it is different from the current
|
// Only set the comment if it is different from the current
|
||||||
// user comment.
|
// user comment.
|
||||||
|
if comment == user.Comment {
|
||||||
|
userstate.Comment = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texture change
|
// Texture change
|
||||||
|
|
@ -282,8 +285,8 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
// Registration
|
// Registration
|
||||||
if userstate.UserId != nil {
|
if userstate.UserId != nil {
|
||||||
// If user == actor, check for 'selfregister' permission on root channel.
|
// If user == actor, check for SelfRegisterPermission on root channel.
|
||||||
// If user != actor, check for 'register' permission on root channel.
|
// If user != actor, check for RegisterPermission permission on root channel.
|
||||||
permCheck := Permission(NonePermission)
|
permCheck := Permission(NonePermission)
|
||||||
uid := *userstate.UserId
|
uid := *userstate.UserId
|
||||||
if user == actor {
|
if user == actor {
|
||||||
|
|
@ -296,8 +299,10 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If user's hash is empty, deny...
|
// We can't register a user with an empty hash.
|
||||||
// fixme(mkrautz)
|
if len(user.Hash) == 0 {
|
||||||
|
client.sendPermissionDeniedTypeUser("MissingCertificate", user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent self-targetting state changes to be applied to other users
|
// Prevent self-targetting state changes to be applied to other users
|
||||||
|
|
@ -316,10 +321,17 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("handleUserState: In the out-figuring state")
|
|
||||||
|
|
||||||
broadcast := false
|
broadcast := false
|
||||||
|
|
||||||
if userstate.Texture != nil {
|
if userstate.Texture != nil {
|
||||||
|
user.Texture = userstate.Texture
|
||||||
|
if len(user.Texture) >= 128 {
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write(user.Texture)
|
||||||
|
user.TextureHash = hash.Sum()
|
||||||
|
} else {
|
||||||
|
user.TextureHash = []byte{}
|
||||||
|
}
|
||||||
broadcast = true
|
broadcast = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,7 +361,14 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if userstate.Comment != nil {
|
if userstate.Comment != nil {
|
||||||
log.Printf("handleUserState: Comment unhandled")
|
user.Comment = *userstate.Comment
|
||||||
|
if len(user.Comment) >= 128 {
|
||||||
|
hash := sha1.New()
|
||||||
|
hash.Write([]byte(user.Comment))
|
||||||
|
user.CommentHash = hash.Sum()
|
||||||
|
} else {
|
||||||
|
user.CommentHash = []byte{}
|
||||||
|
}
|
||||||
broadcast = true
|
broadcast = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -378,14 +397,26 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
|
|
||||||
if userstate.Recording != nil && *userstate.Recording != user.Recording {
|
if userstate.Recording != nil && *userstate.Recording != user.Recording {
|
||||||
user.Recording = *userstate.Recording
|
user.Recording = *userstate.Recording
|
||||||
// fixme(mkrautz): Notify older clients of recording state change.
|
|
||||||
|
txtmsg := &mumbleproto.TextMessage{}
|
||||||
|
txtmsg.TreeId = append(txtmsg.TreeId, uint32(0))
|
||||||
|
if user.Recording {
|
||||||
|
txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", user.Username))
|
||||||
|
} else {
|
||||||
|
txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", user.Username))
|
||||||
|
}
|
||||||
|
|
||||||
|
server.broadcastProtoMessageWithPredicate(MessageTextMessage, txtmsg, func(version uint32) bool {
|
||||||
|
return version < 0x10203
|
||||||
|
})
|
||||||
|
|
||||||
broadcast = true
|
broadcast = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if userstate.UserId != nil {
|
if userstate.UserId != nil {
|
||||||
log.Printf("handleUserState: SelfRegister unhandled")
|
// fixme(mkrautz): Registration is currently unhandled.
|
||||||
|
log.Printf("handleUserState: (Self)Register not implemented yet!")
|
||||||
userstate.UserId = nil
|
userstate.UserId = nil
|
||||||
broadcast = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if userstate.ChannelId != nil {
|
if userstate.ChannelId != nil {
|
||||||
|
|
@ -396,12 +427,54 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("broadcast = %v", broadcast)
|
|
||||||
if broadcast {
|
if broadcast {
|
||||||
log.Printf("sending broadcast!")
|
// This variable denotes the length of a zlib-encoded "old-style" texture.
|
||||||
err := server.broadcastProtoMessage(MessageUserState, userstate)
|
// Mumble and Murmur used qCompress and qUncompress from Qt to compress
|
||||||
|
// textures that were sent over the wire. We can use this to determine
|
||||||
|
// whether a texture is a "new style" or an "old style" texture.
|
||||||
|
texlen := uint32(0)
|
||||||
|
if user.Texture != nil && len(user.Texture) > 4 {
|
||||||
|
texlen = uint32(user.Texture[0])<<24 | uint32(user.Texture[1])<<16 | uint32(user.Texture[2])<<8 | uint32(user.Texture[3])
|
||||||
|
}
|
||||||
|
if userstate.Texture != nil && len(user.Texture) > 4 && texlen != 600*60*4 {
|
||||||
|
// The sent texture is a new-style texture. Strip it from the message
|
||||||
|
// we send to pre-1.2.2 clients.
|
||||||
|
userstate.Texture = nil
|
||||||
|
err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(version uint32) bool {
|
||||||
|
return version < 0x10202
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("Unable to broadcast UserState")
|
||||||
|
}
|
||||||
|
// Re-add it to the message, so that 1.2.2+ clients *do* get the new-style texture.
|
||||||
|
userstate.Texture = user.Texture
|
||||||
|
} else {
|
||||||
|
// Old style texture. We can send the message as-is.
|
||||||
|
err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(version uint32) bool {
|
||||||
|
return version < 0x10202
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Panic("Unable to broadcast UserState")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a texture hash is set on user, we transmit that instead of
|
||||||
|
// the texture itself. This allows the client to intelligently fetch
|
||||||
|
// the blobs that it does not already have in its local storage.
|
||||||
|
if userstate != nil && len(user.TextureHash) > 0 {
|
||||||
|
userstate.Texture = nil
|
||||||
|
userstate.TextureHash = user.TextureHash
|
||||||
|
}
|
||||||
|
// Ditto for comments.
|
||||||
|
if userstate.Comment != nil && len(user.CommentHash) > 0 {
|
||||||
|
userstate.Comment = nil
|
||||||
|
userstate.CommentHash = user.CommentHash
|
||||||
|
}
|
||||||
|
|
||||||
|
err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(version uint32) bool {
|
||||||
|
return version >= 0x10203
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("handleUserState: failed to broadcast userstate")
|
|
||||||
log.Panic("Unable to broadcast UserState")
|
log.Panic("Unable to broadcast UserState")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -712,3 +785,54 @@ func (server *Server) handlePermissionQuery(client *Client, msg *Message) {
|
||||||
channel := server.channels[int(*query.ChannelId)]
|
channel := server.channels[int(*query.ChannelId)]
|
||||||
server.sendClientPermissions(client, channel)
|
server.sendClientPermissions(client, channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request big blobs from the server
|
||||||
|
func (server *Server) handleRequestBlob(client *Client, msg *Message) {
|
||||||
|
blobreq := &mumbleproto.RequestBlob{}
|
||||||
|
err := proto.Unmarshal(msg.buf, blobreq)
|
||||||
|
if err != nil {
|
||||||
|
client.Panic(err.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userstate := &mumbleproto.UserState{}
|
||||||
|
|
||||||
|
// Request for user textures
|
||||||
|
if len(blobreq.SessionTexture) > 0 {
|
||||||
|
for _, sid := range blobreq.SessionTexture {
|
||||||
|
if user, ok := server.clients[sid]; ok {
|
||||||
|
if len(user.Texture) > 0 {
|
||||||
|
userstate.Reset()
|
||||||
|
userstate.Session = proto.Uint32(uint32(user.Session))
|
||||||
|
userstate.Texture = user.Texture
|
||||||
|
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
|
||||||
|
client.Panic(err.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request for user comments
|
||||||
|
if len(blobreq.SessionComment) > 0 {
|
||||||
|
for _, sid := range blobreq.SessionComment {
|
||||||
|
if user, ok := server.clients[sid]; ok {
|
||||||
|
if len(user.Comment) > 0 {
|
||||||
|
userstate.Reset()
|
||||||
|
userstate.Session = proto.Uint32(uint32(user.Session))
|
||||||
|
userstate.Comment = proto.String(user.Comment)
|
||||||
|
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
|
||||||
|
client.Panic(err.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request for channel descriptions
|
||||||
|
if len(blobreq.ChannelDescription) > 0 {
|
||||||
|
log.Printf("No support for ChannelDescriptions in BlobRequest.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -526,7 +526,7 @@ func (server *Server) handleIncomingMessage(client *Client, msg *Message) {
|
||||||
case MessageUserStats:
|
case MessageUserStats:
|
||||||
server.handleUserStatsMessage(msg.client, msg)
|
server.handleUserStatsMessage(msg.client, msg)
|
||||||
case MessageRequestBlob:
|
case MessageRequestBlob:
|
||||||
log.Printf("MessageRequestBlob from client")
|
server.handleRequestBlob(msg.client, msg)
|
||||||
case MessageServerConfig:
|
case MessageServerConfig:
|
||||||
log.Printf("MessageServerConfig from client")
|
log.Printf("MessageServerConfig from client")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue