grumble/message.go

278 lines
7.1 KiB
Go

// Grumble - an implementation of Murmur in Go
// Copyright (c) 2010 The Grumble Authors
// The use of this source code is goverened by a BSD-style
// license that can be found in the LICENSE-file.
package main
import (
"log"
"mumbleproto"
"goprotobuf.googlecode.com/hg/proto"
"net"
"cryptstate"
)
// These are the different kinds of messages
// that are defined for the Mumble protocol
const (
MessageVersion = iota
MessageUDPTunnel
MessageAuthenticate
MessagePing
MessageReject
MessageServerSync
MessageChannelRemove
MessageChannelState
MessageUserRemove
MessageUserState
MessageBanList
MessageTextMessage
MessagePermissionDenied
MessageACL
MessageQueryUsers
MessageCryptSetup
MessageContextActionAdd
MessageContextAction
MessageUserList
MessageVoiceTarget
MessagePermissionQuery
MessageCodecVersion
MessageUserStats
MessageRequestBlob
MessageServerConfig
)
const (
UDPMessageVoiceCELTAlpha = iota
UDPMessagePing
UDPMessageVoiceSpeex
UDPMessageVoiceCELTBeta
)
type Message struct {
buf []byte
// Kind denotes a message kind for TCP packets. This field
// is ignored for UDP packets.
kind uint16
// For UDP datagrams one of these fields have to be filled out.
// If there is no connection established, address must be used.
// If the datagram comes from an already-connected client, the
// client field should point to that client.
client *Client
address net.Addr
}
type VoiceBroadcast struct {
// The client who is performing the broadcast
client *Client
// The VoiceTarget identifier.
target byte
// The voice packet itself.
buf []byte
}
func (server *Server) handleCryptSetup(client *Client, msg *Message) {
cs := &mumbleproto.CryptSetup{}
err := proto.Unmarshal(msg.buf, cs)
if err != nil {
client.Panic(err.String())
return
}
// No client nonce. This means the client
// is requesting that we re-sync our nonces.
if len(cs.ClientNonce) == 0 {
log.Printf("Requested crypt-nonce resync")
cs.ClientNonce = make([]byte, cryptstate.AESBlockSize)
if copy(cs.ClientNonce, client.crypt.EncryptIV[0:]) != cryptstate.AESBlockSize {
return
}
client.sendProtoMessage(MessageCryptSetup, cs)
} else {
log.Printf("Received client nonce")
if len(cs.ClientNonce) != cryptstate.AESBlockSize {
return
}
client.crypt.Resync += 1
if copy(client.crypt.DecryptIV[0:], cs.ClientNonce) != cryptstate.AESBlockSize {
return
}
log.Printf("Crypt re-sync successful")
}
}
func (server *Server) handlePingMessage(client *Client, msg *Message) {
ping := &mumbleproto.Ping{}
err := proto.Unmarshal(msg.buf, ping)
if err != nil {
client.Panic(err.String())
return
}
// Phony response for ping messages. We don't keep stats
// for this yet.
client.sendProtoMessage(MessagePing, &mumbleproto.Ping{
Timestamp: ping.Timestamp,
Good: proto.Uint32(uint32(client.crypt.Good)),
Late: proto.Uint32(uint32(client.crypt.Late)),
Lost: proto.Uint32(uint32(client.crypt.Lost)),
Resync: proto.Uint32(uint32(client.crypt.Resync)),
})
}
func (server *Server) handleChannelAddMessage(client *Client, msg *Message) {
}
func (server *Server) handleChannelRemoveMessage(client *Client, msg *Message) {
}
func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
}
func (server *Server) handleUserRemoveMessage(client *Client, msg *Message) {
}
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
log.Printf("UserState!")
userstate := &mumbleproto.UserState{}
err := proto.Unmarshal(msg.buf, userstate)
if err != nil {
client.Panic(err.String())
}
if userstate.Session == nil {
log.Printf("UserState without session.")
return
}
actor := server.clients[client.Session]
user := server.clients[*userstate.Session]
log.Printf("actor = %v", actor)
log.Printf("user = %v", user)
userstate.Session = proto.Uint32(user.Session)
userstate.Actor = proto.Uint32(actor.Session)
// Has a channel ID
if userstate.ChannelId != nil {
// Destination channel
dstChan := server.channels[int(*userstate.ChannelId)]
log.Printf("dstChan = %v", dstChan)
// If the user and the actor aren't the same, check whether the actor has the 'move' permission
// on the user's channel to move.
// Check whether the actor has 'move' permissions on dstChan. Check whether user has 'enter'
// permissions on dstChan.
// Check whether the channel is full.
}
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
// Disallow for SuperUser
// Check whether the actor has 'mutedeafen' permission on user's channel.
// Check if this was a suppress operation. Only the server can suppress users.
}
// Comment set/clear
if userstate.Comment != nil {
comment := *userstate.Comment
log.Printf("comment = %v", comment)
// Clearing another user's comment.
if user != actor {
// Check if actor has 'move' permissions on the root channel. It is needed
// to clear another user's comment.
// Only allow empty text.
}
// Check if the text is allowed.
// Only set the comment if it is different from the current
// user comment.
}
// Texture change
if userstate.Texture != nil {
// Check the length of the texture
}
// Registration
if userstate.UserId != nil {
// If user == actor, check for 'selfregister' permission on root channel.
// If user != actor, check for 'register' permission on root channel.
// Check if the UserId in the message is >= 0. A registration attempt
// must use a negative UserId.
}
// Prevent self-targetting state changes to be applied to other users
// That is, if actor != user, then:
// Discard message if it has any of the following things set:
// - SelfDeaf
// - SelfMute
// - Texture
// - PluginContext
// - PluginIdentity
// - Recording
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
userstate.Recording != nil) {
return
}
}
func (server *Server) handleBanListMessage(client *Client, msg *Message) {
}
func (server *Server) handleTextMessage(client *Client, msg *Message) {
txtmsg := &mumbleproto.TextMessage{}
err := proto.Unmarshal(msg.buf, txtmsg)
if err != nil {
client.Panic(err.String())
return
}
users := []*Client{}
for i := 0; i < len(txtmsg.Session); i++ {
user, ok := server.clients[txtmsg.Session[i]]
if !ok {
log.Panic("Could not look up client by session")
}
users = append(users, user)
}
for _, user := range users {
user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{
Actor: proto.Uint32(client.Session),
Message: txtmsg.Message,
})
}
}
func (server *Server) handleAclMessage(client *Client, msg *Message) {
}
// User query
func (server *Server) handleQueryUsers(client *Client, msg *Message) {
}
// User stats message. Shown in the Mumble client when a
// user right clicks a user and selects 'User Information'.
func (server *Server) handleUserStatsMessage(client *Client, msg *Message) {
stats := &mumbleproto.UserStats{}
err := proto.Unmarshal(msg.buf, stats)
if err != nil {
client.Panic(err.String())
}
log.Printf("UserStatsMessage")
}