<commit_before>// 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"
	"fmt"
)

// 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) {
}

// Handle channel state change.
func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
	chanstate := &mumbleproto.ChannelState{}
	err := proto.Unmarshal(msg.buf, chanstate)
	if err != nil {
		client.Panic(err.String())
		return
	}

	var channel *Channel
	var parent *Channel
	var ok bool

	// Lookup channel for channel ID
	if chanstate.ChannelId != nil {
		channel, ok = server.Channels[int(*chanstate.ChannelId)]
		if !ok {
			client.Panic("Invalid channel specified in ChannelState message")
			return
		}
	}

	// Lookup parent
	if chanstate.Parent != nil {
		parent, ok = server.Channels[int(*chanstate.Parent)]
		if !ok {
			client.Panic("Invalid parent channel specified in ChannelState message")
			return
		}
	}

	// The server can't receive links through the links field in the ChannelState message,
	// because clients are supposed to send modifications to a channel's link state through
	// the links_add and links_remove fields.
	// Make sure the links field is clear so we can transmit the channel's link state in our reply.
	chanstate.Links = nil

	var name string
	var description string

	// Extract the description and perform sanity checks.
	if chanstate.Description != nil {
		description = *chanstate.Description
		// fixme(mkrautz): Check length
	}

	// Extract the the name of channel and check whether it's valid.
	// A valid channel name is a name that:
	//  a) Isn't already used by a channel at the same level as the channel itself (that is, channels
	//     that have a common parent can't have the same name.
	//  b) A name must be a valid name on the server (it must pass the channel name regexp)
	if chanstate.Name != nil {
		name = *chanstate.Name

		// We don't allow renames for the root channel.
		if channel != nil && channel.Id != 0 {
			// Pick a parent. If the name change is part of a re-parent (a channel move),
			// we must evaluate the parent variable. Since we're explicitly exlcuding the root
			// channel from renames, channels that are the target of renames are guaranteed to have
			// a parent.
			evalp := parent
			if evalp == nil {
				evalp = channel.parent
			}
			for _, iter := range evalp.children {
				if iter.Name == name {
					client.sendPermissionDeniedType("ChannelName")
					return
				}
			}
		}
	}

	// If the channel does not exist already, the ChannelState message is a create operation.
	if channel == nil {
		if parent == nil || len(name) == 0 {
			return
		}

		// Check whether the client has permission to create the channel in parent.
		perm := Permission(NonePermission)
		if *chanstate.Temporary {
			perm = Permission(TempChannelPermission)
		} else {
			perm = Permission(MakeChannelPermission)
		}
		if !server.HasPermission(client, parent, perm) {
			client.sendPermissionDenied(client, parent, perm)
			return
		}

		// Only registered users can create channels.
		if !client.IsRegistered() && !client.HasCertificate() {
			client.sendPermissionDeniedTypeUser("MissingCertificate", client)
			return
		}

		// We can't add channels to a temporary channel
		if parent.Temporary {
			client.sendPermissionDeniedType("TemporaryChannel")
			return
		}

		key := ""
		if len(description) > 0 {
			key, err = globalBlobstore.Put([]byte(description))
			if err != nil {
				log.Panicf("Blobstore error: %v", err.String())
			}
		}

		// Add the new channel
		channel = server.AddChannel(name)
		channel.DescriptionBlob = key
		channel.Temporary = *chanstate.Temporary
		channel.Position = int(*chanstate.Position)
		parent.AddChild(channel)

		// Add the creator to the channel's admin group
		if client.IsRegistered() {
			grp := NewGroup(channel, "admin")
			grp.Add[client.UserId()] = true
			channel.Groups["admin"] = grp
		}

		// If the client wouldn't have WritePermission in the just-created channel,
		// add a +write ACL for the user's hash.
		if !server.HasPermission(client, channel, WritePermission) {
			acl := NewChannelACL(channel)
			acl.ApplyHere = true
			acl.ApplySubs = true
			if client.IsRegistered() {
				acl.UserId = client.UserId()
			} else {
				acl.Group = "$" + client.CertHash
			}
			acl.Deny = Permission(NonePermission)
			acl.Allow = Permission(WritePermission | TraversePermission)

			channel.ACL = append(channel.ACL, acl)

			server.ClearACLCache()
		}

		chanstate.ChannelId = proto.Uint32(uint32(channel.Id))

		// Broadcast channel add
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version < 0x10202
		})

		// Remove description if client knows how to handle blobs.
		if chanstate.Description != nil && channel.HasDescription() {
			chanstate.Description = nil
			chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		}
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version >= 0x10202
		})

		// If it's a temporary channel, move the creator in there.
		if channel.Temporary {
			userstate := &mumbleproto.UserState{}
			userstate.Session = proto.Uint32(client.Session)
			userstate.ChannelId = proto.Uint32(uint32(channel.Id))
			server.userEnterChannel(client, channel, userstate)
			server.broadcastProtoMessage(MessageUserState, userstate)
		}
	} else {
		// Edit existing channel.
		// First, check whether the actor has the neccessary permissions.

		// Name change.
		if chanstate.Name != nil {
			// The client can only rename the channel if it has WritePermission in the channel.
			// Also, clients cannot change the name of the root channel.
			if !server.HasPermission(client, channel, WritePermission) || channel.Id == 0 {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Description change
		if chanstate.Description != nil {
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Position change
		if chanstate.Position != nil {
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Parent change (channel move)
		if parent != nil {
			// No-op?
			if parent == channel.parent {
				return
			}

			// Make sure that channel we're operating on is not a parent of the new parent.
			iter := parent
			for iter != nil {
				if iter == channel {
					client.Panic("Illegal channel reparent")
					return
				}
				iter = iter.parent
			}

			// A temporary channel must not have any subchannels, so deny it.
			if parent.Temporary {
				client.sendPermissionDeniedType("TemporaryChannel")
				return
			}

			// To move a channel, the user must have WritePermission in the channel
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}

			// And the user must also have MakeChannel permission in the new parent
			if !server.HasPermission(client, parent, MakeChannelPermission) {
				client.sendPermissionDenied(client, parent, MakeChannelPermission)
				return
			}

			// If a sibling of parent already has this name, don't allow it.
			for _, iter := range parent.children {
				if iter.Name == channel.Name {
					client.sendPermissionDeniedType("ChannelName")
					return
				}
			}
		}

		// Links
		linkadd := []*Channel{}
		linkremove := []*Channel{}
		if len(chanstate.LinksAdd) > 0 || len(chanstate.LinksRemove) > 0 {
			// Client must have permission to link
			if !server.HasPermission(client, channel, LinkChannelPermission) {
				client.sendPermissionDenied(client, channel, LinkChannelPermission)
				return
			}
			// Add any valid channels to linkremove slice
			for _, cid := range chanstate.LinksRemove {
				if iter, ok := server.Channels[int(cid)]; ok {
					linkremove = append(linkremove, iter)
				}
			}
			// Add any valid channels to linkadd slice
			for _, cid := range chanstate.LinksAdd {
				if iter, ok := server.Channels[int(cid)]; ok {
					if !server.HasPermission(client, iter, LinkChannelPermission) {
						client.sendPermissionDenied(client, iter, LinkChannelPermission)
						return
					}
					linkadd = append(linkadd, iter)
				}
			}
		}

		// Permission checks done!

		// Channel move
		if parent != nil {
			channel.parent.RemoveChild(channel)
			parent.AddChild(channel)
		}

		// Rename
		if chanstate.Name != nil {
			channel.Name = *chanstate.Name
		}

		// Description change
		if chanstate.Description != nil {
			key, err := globalBlobstore.Put([]byte(*chanstate.Description))
			if err != nil {
				log.Panicf("Blobstore error: %v", err.String())
			}
			channel.DescriptionBlob = key
		}

		// Position change
		if chanstate.Position != nil {
			channel.Position = int(*chanstate.Position)
		}

		// Add links
		for _, iter := range linkadd {
			server.LinkChannels(channel, iter)
		}

		// Remove links
		for _, iter := range linkremove {
			server.UnlinkChannels(channel, iter)
		}

		// Broadcast the update
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version < 0x10202
		})

		// Remove description blob when sending to 1.2.2 >= users. Only send the blob hash.
		if channel.HasDescription() {
			chanstate.Description = nil
			chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		}
		chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version >= 0x10202
		})
	}
}

// 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 client to be removed.
	removeClient, 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 client's permissions
	perm := Permission(KickPermission)
	if ban {
		perm = Permission(BanPermission)
	}

	if removeClient.IsSuperUser() || !server.HasPermission(client, server.root, perm) {
		client.sendPermissionDenied(client, server.root, perm)
		return
	}

	if ban {
		// fixme(mkrautz): Implement banning.
		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
	}

	removeClient.ForceDisconnect()
}

// Handle user state changes
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
	userstate := &mumbleproto.UserState{}
	err := proto.Unmarshal(msg.buf, userstate)
	if err != nil {
		client.Panic(err.String())
	}

	actor, ok := server.clients[client.Session]
	if !ok {
		log.Panic("Client not found in server's client map.")
		return
	}
	target := actor
	if userstate.Session != nil {
		target, ok = server.clients[*userstate.Session]
		if !ok {
			client.Panic("Invalid session in UserState message")
			return
		}
	}

	userstate.Session = proto.Uint32(target.Session)
	userstate.Actor = proto.Uint32(actor.Session)

	// Does it have a channel ID?
	if userstate.ChannelId != nil {
		// Destination channel
		dstChan, ok := server.Channels[int(*userstate.ChannelId)]
		if !ok {
			return
		}

		// If the user and the actor aren't the same, check whether the actor has MovePermission on
		// the user's curent channel.
		if actor != target && !server.HasPermission(actor, target.Channel, MovePermission) {
			client.sendPermissionDenied(actor, target.Channel, MovePermission)
			return
		}

		// Check whether the actor has MovePermission on dstChan.  Check whether user has EnterPermission
		// on dstChan.
		if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(target, dstChan, EnterPermission) {
			client.sendPermissionDenied(target, dstChan, EnterPermission)
			return
		}

		// fixme(mkrautz): Check whether the channel is full.
	}

	if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
		// Disallow for SuperUser
		if target.IsSuperUser() {
			client.sendPermissionDeniedType("SuperUser")
			return
		}

		// Check whether the actor has 'mutedeafen' permission on user's channel.
		if !server.HasPermission(actor, target.Channel, MuteDeafenPermission) {
			client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission)
			return
		}

		// Check if this was a suppress operation. Only the server can suppress users.
		if userstate.Suppress != nil {
			client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission)
			return
		}
	}

	// Comment set/clear
	if userstate.Comment != nil {
		comment := *userstate.Comment

		// Clearing another user's comment.
		if target != actor {
			// Check if actor has 'move' permissions on the root channel. It is needed
			// to clear another user's comment.
			if !server.HasPermission(actor, server.root, MovePermission) {
				client.sendPermissionDenied(actor, server.root, MovePermission)
				return
			}

			// Only allow empty text.
			if len(comment) > 0 {
				client.Panic("Cannot clear another user's comment")
				return
			}
		}

		// todo(mkrautz): Check if the text is allowed.
	}

	// Texture change
	if userstate.Texture != nil {
		// Check the length of the texture
	}

	// Registration
	if userstate.UserId != nil {
		// If user == actor, check for SelfRegisterPermission on root channel.
		// If user != actor, check for RegisterPermission permission on root channel.
		permCheck := Permission(NonePermission)
		uid := *userstate.UserId
		if target == actor {
			permCheck = SelfRegisterPermission
		} else {
			permCheck = RegisterPermission
		}
		if uid >= 0 || !server.HasPermission(actor, server.root, SelfRegisterPermission) {
			client.sendPermissionDenied(actor, server.root, permCheck)
			return
		}

		// We can't register a user with an empty hash.
		if len(target.CertHash) == 0 {
			client.sendPermissionDeniedTypeUser("MissingCertificate", target)
		}
	}

	// 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 != target && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
		userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
		userstate.Recording != nil) {
		client.Panic("Invalid UserState")
		return
	}

	broadcast := false

	if userstate.Texture != nil {
		key, err := globalBlobstore.Put(userstate.Texture)
		if err != nil {
			log.Panicf("Blobstore error: %v", err.String())
		}

		if target.user.TextureBlob != key {
			target.user.TextureBlob = key
		} else {
			userstate.Texture = nil
		}

		broadcast = true
	}

	if userstate.SelfDeaf != nil {
		target.SelfDeaf = *userstate.SelfDeaf
		if target.SelfDeaf {
			userstate.SelfDeaf = proto.Bool(true)
			target.SelfMute = true
		}
		broadcast = true
	}

	if userstate.SelfMute != nil {
		target.SelfMute = *userstate.SelfMute
		if !target.SelfMute {
			userstate.SelfDeaf = proto.Bool(false)
			target.SelfDeaf = false
		}
	}

	if userstate.PluginContext != nil {
		target.PluginContext = userstate.PluginContext
	}

	if userstate.PluginIdentity != nil {
		target.PluginIdentity = *userstate.PluginIdentity
	}

	if userstate.Comment != nil {
		key, err := globalBlobstore.Put([]byte(*userstate.Comment))
		if err != nil {
			log.Panicf("Blobstore error: %v", err.String())
		}

		if target.user.CommentBlob != key {
			target.user.CommentBlob = key
		} else {
			userstate.Comment = nil
		}

		broadcast = true
	}

	if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
		if userstate.Deaf != nil {
			target.Deaf = *userstate.Deaf
			if target.Deaf {
				userstate.Mute = proto.Bool(true)
			}
		}
		if userstate.Mute != nil {
			target.Mute = *userstate.Mute
			if !target.Mute {
				userstate.Deaf = proto.Bool(false)
				target.Deaf = false
			}
		}
		if userstate.Suppress != nil {
			target.Suppress = *userstate.Suppress
		}
		if userstate.PrioritySpeaker != nil {
			target.PrioritySpeaker = *userstate.PrioritySpeaker
		}
		broadcast = true
	}

	if userstate.Recording != nil && *userstate.Recording != target.Recording {
		target.Recording = *userstate.Recording

		txtmsg := &mumbleproto.TextMessage{}
		txtmsg.TreeId = append(txtmsg.TreeId, uint32(0))
		if target.Recording {
			txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", target.ShownName()))
		} else {
			txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", target.ShownName()))
		}

		server.broadcastProtoMessageWithPredicate(MessageTextMessage, txtmsg, func(client *Client) bool {
			return client.Version < 0x10203
		})

		broadcast = true
	}

	if userstate.UserId != nil {
		// fixme(mkrautz): Registration is currently unhandled.
		log.Printf("handleUserState: (Self)Register not implemented yet!")
		userstate.UserId = nil
	}

	if userstate.ChannelId != nil {
		channel, ok := server.Channels[int(*userstate.ChannelId)]
		if ok {
			server.userEnterChannel(target, channel, userstate)
			broadcast = true
		}
	}

	if broadcast {
		// This variable denotes the length of a zlib-encoded "old-style" texture.
		// 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.
		texture := userstate.Texture
		texlen := uint32(0)
		if texture != nil && len(texture) > 4 {
			texlen = uint32(texture[0])<<24 | uint32(texture[1])<<16 | uint32(texture[2])<<8 | uint32(texture[3])
		}
		if texture != nil && len(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(client *Client) bool {
				return client.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 = texture
		} else {
			// Old style texture.  We can send the message as-is.
			err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool {
				return client.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.Texture != nil && target.user.HasTexture() {
			userstate.Texture = nil
			userstate.TextureHash = target.user.TextureBlobHashBytes()
		}

		// Ditto for comments.
		if userstate.Comment != nil && target.user.HasComment() {
			userstate.Comment = nil
			userstate.CommentHash = target.user.CommentBlobHashBytes()
		}

		err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool {
			return client.Version >= 0x10203
		})
		if err != nil {
			log.Panic("Unable to broadcast UserState")
		}
	}
}

func (server *Server) handleBanListMessage(client *Client, msg *Message) {
}

// Broadcast text messages
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
	}

	// fixme(mkrautz): Check text message length.
	// fixme(mkrautz): Sanitize text as well.

	clients := make(map[uint32]*Client)

	// Tree
	for _, chanid := range txtmsg.TreeId {
		if channel, ok := server.Channels[int(chanid)]; ok {<commit_msg>Only allow blob-storage for registered users.<commit_after>// 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"
	"fmt"
)

// 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) {
}

// Handle channel state change.
func (server *Server) handleChannelStateMessage(client *Client, msg *Message) {
	chanstate := &mumbleproto.ChannelState{}
	err := proto.Unmarshal(msg.buf, chanstate)
	if err != nil {
		client.Panic(err.String())
		return
	}

	var channel *Channel
	var parent *Channel
	var ok bool

	// Lookup channel for channel ID
	if chanstate.ChannelId != nil {
		channel, ok = server.Channels[int(*chanstate.ChannelId)]
		if !ok {
			client.Panic("Invalid channel specified in ChannelState message")
			return
		}
	}

	// Lookup parent
	if chanstate.Parent != nil {
		parent, ok = server.Channels[int(*chanstate.Parent)]
		if !ok {
			client.Panic("Invalid parent channel specified in ChannelState message")
			return
		}
	}

	// The server can't receive links through the links field in the ChannelState message,
	// because clients are supposed to send modifications to a channel's link state through
	// the links_add and links_remove fields.
	// Make sure the links field is clear so we can transmit the channel's link state in our reply.
	chanstate.Links = nil

	var name string
	var description string

	// Extract the description and perform sanity checks.
	if chanstate.Description != nil {
		description = *chanstate.Description
		// fixme(mkrautz): Check length
	}

	// Extract the the name of channel and check whether it's valid.
	// A valid channel name is a name that:
	//  a) Isn't already used by a channel at the same level as the channel itself (that is, channels
	//     that have a common parent can't have the same name.
	//  b) A name must be a valid name on the server (it must pass the channel name regexp)
	if chanstate.Name != nil {
		name = *chanstate.Name

		// We don't allow renames for the root channel.
		if channel != nil && channel.Id != 0 {
			// Pick a parent. If the name change is part of a re-parent (a channel move),
			// we must evaluate the parent variable. Since we're explicitly exlcuding the root
			// channel from renames, channels that are the target of renames are guaranteed to have
			// a parent.
			evalp := parent
			if evalp == nil {
				evalp = channel.parent
			}
			for _, iter := range evalp.children {
				if iter.Name == name {
					client.sendPermissionDeniedType("ChannelName")
					return
				}
			}
		}
	}

	// If the channel does not exist already, the ChannelState message is a create operation.
	if channel == nil {
		if parent == nil || len(name) == 0 {
			return
		}

		// Check whether the client has permission to create the channel in parent.
		perm := Permission(NonePermission)
		if *chanstate.Temporary {
			perm = Permission(TempChannelPermission)
		} else {
			perm = Permission(MakeChannelPermission)
		}
		if !server.HasPermission(client, parent, perm) {
			client.sendPermissionDenied(client, parent, perm)
			return
		}

		// Only registered users can create channels.
		if !client.IsRegistered() && !client.HasCertificate() {
			client.sendPermissionDeniedTypeUser("MissingCertificate", client)
			return
		}

		// We can't add channels to a temporary channel
		if parent.Temporary {
			client.sendPermissionDeniedType("TemporaryChannel")
			return
		}

		key := ""
		if len(description) > 0 {
			key, err = globalBlobstore.Put([]byte(description))
			if err != nil {
				log.Panicf("Blobstore error: %v", err.String())
			}
		}

		// Add the new channel
		channel = server.AddChannel(name)
		channel.DescriptionBlob = key
		channel.Temporary = *chanstate.Temporary
		channel.Position = int(*chanstate.Position)
		parent.AddChild(channel)

		// Add the creator to the channel's admin group
		if client.IsRegistered() {
			grp := NewGroup(channel, "admin")
			grp.Add[client.UserId()] = true
			channel.Groups["admin"] = grp
		}

		// If the client wouldn't have WritePermission in the just-created channel,
		// add a +write ACL for the user's hash.
		if !server.HasPermission(client, channel, WritePermission) {
			acl := NewChannelACL(channel)
			acl.ApplyHere = true
			acl.ApplySubs = true
			if client.IsRegistered() {
				acl.UserId = client.UserId()
			} else {
				acl.Group = "$" + client.CertHash
			}
			acl.Deny = Permission(NonePermission)
			acl.Allow = Permission(WritePermission | TraversePermission)

			channel.ACL = append(channel.ACL, acl)

			server.ClearACLCache()
		}

		chanstate.ChannelId = proto.Uint32(uint32(channel.Id))

		// Broadcast channel add
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version < 0x10202
		})

		// Remove description if client knows how to handle blobs.
		if chanstate.Description != nil && channel.HasDescription() {
			chanstate.Description = nil
			chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		}
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version >= 0x10202
		})

		// If it's a temporary channel, move the creator in there.
		if channel.Temporary {
			userstate := &mumbleproto.UserState{}
			userstate.Session = proto.Uint32(client.Session)
			userstate.ChannelId = proto.Uint32(uint32(channel.Id))
			server.userEnterChannel(client, channel, userstate)
			server.broadcastProtoMessage(MessageUserState, userstate)
		}
	} else {
		// Edit existing channel.
		// First, check whether the actor has the neccessary permissions.

		// Name change.
		if chanstate.Name != nil {
			// The client can only rename the channel if it has WritePermission in the channel.
			// Also, clients cannot change the name of the root channel.
			if !server.HasPermission(client, channel, WritePermission) || channel.Id == 0 {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Description change
		if chanstate.Description != nil {
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Position change
		if chanstate.Position != nil {
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}
		}

		// Parent change (channel move)
		if parent != nil {
			// No-op?
			if parent == channel.parent {
				return
			}

			// Make sure that channel we're operating on is not a parent of the new parent.
			iter := parent
			for iter != nil {
				if iter == channel {
					client.Panic("Illegal channel reparent")
					return
				}
				iter = iter.parent
			}

			// A temporary channel must not have any subchannels, so deny it.
			if parent.Temporary {
				client.sendPermissionDeniedType("TemporaryChannel")
				return
			}

			// To move a channel, the user must have WritePermission in the channel
			if !server.HasPermission(client, channel, WritePermission) {
				client.sendPermissionDenied(client, channel, WritePermission)
				return
			}

			// And the user must also have MakeChannel permission in the new parent
			if !server.HasPermission(client, parent, MakeChannelPermission) {
				client.sendPermissionDenied(client, parent, MakeChannelPermission)
				return
			}

			// If a sibling of parent already has this name, don't allow it.
			for _, iter := range parent.children {
				if iter.Name == channel.Name {
					client.sendPermissionDeniedType("ChannelName")
					return
				}
			}
		}

		// Links
		linkadd := []*Channel{}
		linkremove := []*Channel{}
		if len(chanstate.LinksAdd) > 0 || len(chanstate.LinksRemove) > 0 {
			// Client must have permission to link
			if !server.HasPermission(client, channel, LinkChannelPermission) {
				client.sendPermissionDenied(client, channel, LinkChannelPermission)
				return
			}
			// Add any valid channels to linkremove slice
			for _, cid := range chanstate.LinksRemove {
				if iter, ok := server.Channels[int(cid)]; ok {
					linkremove = append(linkremove, iter)
				}
			}
			// Add any valid channels to linkadd slice
			for _, cid := range chanstate.LinksAdd {
				if iter, ok := server.Channels[int(cid)]; ok {
					if !server.HasPermission(client, iter, LinkChannelPermission) {
						client.sendPermissionDenied(client, iter, LinkChannelPermission)
						return
					}
					linkadd = append(linkadd, iter)
				}
			}
		}

		// Permission checks done!

		// Channel move
		if parent != nil {
			channel.parent.RemoveChild(channel)
			parent.AddChild(channel)
		}

		// Rename
		if chanstate.Name != nil {
			channel.Name = *chanstate.Name
		}

		// Description change
		if chanstate.Description != nil {
			key, err := globalBlobstore.Put([]byte(*chanstate.Description))
			if err != nil {
				log.Panicf("Blobstore error: %v", err.String())
			}
			channel.DescriptionBlob = key
		}

		// Position change
		if chanstate.Position != nil {
			channel.Position = int(*chanstate.Position)
		}

		// Add links
		for _, iter := range linkadd {
			server.LinkChannels(channel, iter)
		}

		// Remove links
		for _, iter := range linkremove {
			server.UnlinkChannels(channel, iter)
		}

		// Broadcast the update
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version < 0x10202
		})

		// Remove description blob when sending to 1.2.2 >= users. Only send the blob hash.
		if channel.HasDescription() {
			chanstate.Description = nil
			chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		}
		chanstate.DescriptionHash = channel.DescriptionBlobHashBytes()
		server.broadcastProtoMessageWithPredicate(MessageChannelState, chanstate, func(client *Client) bool {
			return client.Version >= 0x10202
		})
	}
}

// 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 client to be removed.
	removeClient, 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 client's permissions
	perm := Permission(KickPermission)
	if ban {
		perm = Permission(BanPermission)
	}

	if removeClient.IsSuperUser() || !server.HasPermission(client, server.root, perm) {
		client.sendPermissionDenied(client, server.root, perm)
		return
	}

	if ban {
		// fixme(mkrautz): Implement banning.
		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
	}

	removeClient.ForceDisconnect()
}

// Handle user state changes
func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
	userstate := &mumbleproto.UserState{}
	err := proto.Unmarshal(msg.buf, userstate)
	if err != nil {
		client.Panic(err.String())
	}

	actor, ok := server.clients[client.Session]
	if !ok {
		log.Panic("Client not found in server's client map.")
		return
	}
	target := actor
	if userstate.Session != nil {
		target, ok = server.clients[*userstate.Session]
		if !ok {
			client.Panic("Invalid session in UserState message")
			return
		}
	}

	userstate.Session = proto.Uint32(target.Session)
	userstate.Actor = proto.Uint32(actor.Session)

	// Does it have a channel ID?
	if userstate.ChannelId != nil {
		// Destination channel
		dstChan, ok := server.Channels[int(*userstate.ChannelId)]
		if !ok {
			return
		}

		// If the user and the actor aren't the same, check whether the actor has MovePermission on
		// the user's curent channel.
		if actor != target && !server.HasPermission(actor, target.Channel, MovePermission) {
			client.sendPermissionDenied(actor, target.Channel, MovePermission)
			return
		}

		// Check whether the actor has MovePermission on dstChan.  Check whether user has EnterPermission
		// on dstChan.
		if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(target, dstChan, EnterPermission) {
			client.sendPermissionDenied(target, dstChan, EnterPermission)
			return
		}

		// fixme(mkrautz): Check whether the channel is full.
	}

	if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
		// Disallow for SuperUser
		if target.IsSuperUser() {
			client.sendPermissionDeniedType("SuperUser")
			return
		}

		// Check whether the actor has 'mutedeafen' permission on user's channel.
		if !server.HasPermission(actor, target.Channel, MuteDeafenPermission) {
			client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission)
			return
		}

		// Check if this was a suppress operation. Only the server can suppress users.
		if userstate.Suppress != nil {
			client.sendPermissionDenied(actor, target.Channel, MuteDeafenPermission)
			return
		}
	}

	// Comment set/clear
	if userstate.Comment != nil {
		comment := *userstate.Comment

		// Clearing another user's comment.
		if target != actor {
			// Check if actor has 'move' permissions on the root channel. It is needed
			// to clear another user's comment.
			if !server.HasPermission(actor, server.root, MovePermission) {
				client.sendPermissionDenied(actor, server.root, MovePermission)
				return
			}

			// Only allow empty text.
			if len(comment) > 0 {
				client.Panic("Cannot clear another user's comment")
				return
			}
		}

		// todo(mkrautz): Check if the text is allowed.
	}

	// Texture change
	if userstate.Texture != nil {
		// Check the length of the texture
	}

	// Registration
	if userstate.UserId != nil {
		// If user == actor, check for SelfRegisterPermission on root channel.
		// If user != actor, check for RegisterPermission permission on root channel.
		permCheck := Permission(NonePermission)
		uid := *userstate.UserId
		if target == actor {
			permCheck = SelfRegisterPermission
		} else {
			permCheck = RegisterPermission
		}
		if uid >= 0 || !server.HasPermission(actor, server.root, SelfRegisterPermission) {
			client.sendPermissionDenied(actor, server.root, permCheck)
			return
		}

		// We can't register a user with an empty hash.
		if len(target.CertHash) == 0 {
			client.sendPermissionDeniedTypeUser("MissingCertificate", target)
		}
	}

	// 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 != target && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
		userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
		userstate.Recording != nil) {
		client.Panic("Invalid UserState")
		return
	}

	broadcast := false

	if userstate.Texture != nil && target.user != nil {
		key, err := globalBlobstore.Put(userstate.Texture)
		if err != nil {
			log.Panicf("Blobstore error: %v", err.String())
		}

		if target.user.TextureBlob != key {
			target.user.TextureBlob = key
		} else {
			userstate.Texture = nil
		}

		broadcast = true
	}

	if userstate.SelfDeaf != nil {
		target.SelfDeaf = *userstate.SelfDeaf
		if target.SelfDeaf {
			userstate.SelfDeaf = proto.Bool(true)
			target.SelfMute = true
		}
		broadcast = true
	}

	if userstate.SelfMute != nil {
		target.SelfMute = *userstate.SelfMute
		if !target.SelfMute {
			userstate.SelfDeaf = proto.Bool(false)
			target.SelfDeaf = false
		}
	}

	if userstate.PluginContext != nil {
		target.PluginContext = userstate.PluginContext
	}

	if userstate.PluginIdentity != nil {
		target.PluginIdentity = *userstate.PluginIdentity
	}

	if userstate.Comment != nil && target.user != nil {
		key, err := globalBlobstore.Put([]byte(*userstate.Comment))
		if err != nil {
			log.Panicf("Blobstore error: %v", err.String())
		}

		if target.user.CommentBlob != key {
			target.user.CommentBlob = key
		} else {
			userstate.Comment = nil
		}

		broadcast = true
	}

	if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
		if userstate.Deaf != nil {
			target.Deaf = *userstate.Deaf
			if target.Deaf {
				userstate.Mute = proto.Bool(true)
			}
		}
		if userstate.Mute != nil {
			target.Mute = *userstate.Mute
			if !target.Mute {
				userstate.Deaf = proto.Bool(false)
				target.Deaf = false
			}
		}
		if userstate.Suppress != nil {
			target.Suppress = *userstate.Suppress
		}
		if userstate.PrioritySpeaker != nil {
			target.PrioritySpeaker = *userstate.PrioritySpeaker
		}
		broadcast = true
	}

	if userstate.Recording != nil && *userstate.Recording != target.Recording {
		target.Recording = *userstate.Recording

		txtmsg := &mumbleproto.TextMessage{}
		txtmsg.TreeId = append(txtmsg.TreeId, uint32(0))
		if target.Recording {
			txtmsg.Message = proto.String(fmt.Sprintf("User '%s' started recording", target.ShownName()))
		} else {
			txtmsg.Message = proto.String(fmt.Sprintf("User '%s' stopped recording", target.ShownName()))
		}

		server.broadcastProtoMessageWithPredicate(MessageTextMessage, txtmsg, func(client *Client) bool {
			return client.Version < 0x10203
		})

		broadcast = true
	}

	if userstate.UserId != nil {
		// fixme(mkrautz): Registration is currently unhandled.
		log.Printf("handleUserState: (Self)Register not implemented yet!")
		userstate.UserId = nil
	}

	if userstate.ChannelId != nil {
		channel, ok := server.Channels[int(*userstate.ChannelId)]
		if ok {
			server.userEnterChannel(target, channel, userstate)
			broadcast = true
		}
	}

	if broadcast {
		// This variable denotes the length of a zlib-encoded "old-style" texture.
		// 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.
		texture := userstate.Texture
		texlen := uint32(0)
		if texture != nil && len(texture) > 4 {
			texlen = uint32(texture[0])<<24 | uint32(texture[1])<<16 | uint32(texture[2])<<8 | uint32(texture[3])
		}
		if texture != nil && len(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(client *Client) bool {
				return client.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 = texture
		} else {
			// Old style texture.  We can send the message as-is.
			err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool {
				return client.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.Texture != nil && target.user != nil && target.user.HasTexture() {
			userstate.Texture = nil
			userstate.TextureHash = target.user.TextureBlobHashBytes()
		} else if target.user == nil {
			userstate.Texture = nil
			userstate.TextureHash = nil
		}

		// Ditto for comments.
		if userstate.Comment != nil && target.user.HasComment() {
			userstate.Comment = nil
			userstate.CommentHash = target.user.CommentBlobHashBytes()
		} else if target.user == nil {
			userstate.Comment = nil
			userstate.CommentHash = nil
		}

		err := server.broadcastProtoMessageWithPredicate(MessageUserState, userstate, func(client *Client) bool {
			return client.Version >= 0x10203
		})
		if err != nil {
			log.Panic("Unable to broadcast UserState")
		}
	}
}

func (server *Server) handleBanListMessage(client *Client, msg *Message) {
}

// Broadcast text messages
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
	}

	// fixme(mkrautz): Check text message length.
	// fixme(mkrautz): Sanitize text as well.

	clients := make(map[uint32]*Client)

	// Tree
	for _, chanid := range txtmsg.TreeId {
		if channel, ok := server.Channels[int(chanid)]; ok {
{'fraction_non_alphanumeric': 0.10704790040092847, 'fraction_numerical': 0.006035028487022579, 'mean_word_length': 3.534589991388384, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6379984', 'n_tokens_mistral': 16484, 'n_tokens_neox': 14377, 'n_words': 4193}