2019-06-03 10:40:42 +00:00
|
|
|
package xmpp
|
|
|
|
|
|
|
|
import (
|
2019-06-04 15:04:25 +00:00
|
|
|
"encoding/xml"
|
2019-06-03 10:40:42 +00:00
|
|
|
"reflect"
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
type MsgExtension interface{}
|
|
|
|
|
|
|
|
// The Registry for msg and IQ types is a global variable.
|
|
|
|
// TODO: Move to the client init process to remove the dependency on a global variable.
|
|
|
|
// That should make it possible to be able to share the decoder.
|
|
|
|
// TODO: Ensure that a client can add its own custom namespace to the registry (or overload existing ones).
|
2019-06-04 15:04:25 +00:00
|
|
|
|
2019-06-05 08:23:18 +00:00
|
|
|
type PacketType uint8
|
2019-06-04 15:04:25 +00:00
|
|
|
|
|
|
|
const (
|
2019-06-05 08:23:18 +00:00
|
|
|
PKTPresence PacketType = iota
|
2019-06-04 15:04:25 +00:00
|
|
|
PKTMessage
|
|
|
|
PKTIQ
|
|
|
|
)
|
|
|
|
|
2019-06-05 08:23:18 +00:00
|
|
|
var TypeRegistry = newRegistry()
|
2019-06-03 10:40:42 +00:00
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
// We store different registries per packet type and namespace.
|
|
|
|
type registryKey struct {
|
2019-06-05 08:23:18 +00:00
|
|
|
packetType PacketType
|
2019-06-04 15:04:25 +00:00
|
|
|
namespace string
|
|
|
|
}
|
|
|
|
|
|
|
|
type registryForNamespace map[string]reflect.Type
|
2019-06-03 10:40:42 +00:00
|
|
|
|
|
|
|
type registry struct {
|
2019-06-04 15:04:25 +00:00
|
|
|
// We store different registries per packet type and namespace.
|
|
|
|
msgTypes map[registryKey]registryForNamespace
|
|
|
|
// Handle concurrent access
|
2019-06-03 10:40:42 +00:00
|
|
|
msgTypesLock *sync.RWMutex
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
func newRegistry() *registry {
|
|
|
|
return ®istry{
|
|
|
|
msgTypes: make(map[registryKey]registryForNamespace),
|
2019-06-03 10:40:42 +00:00
|
|
|
msgTypesLock: &sync.RWMutex{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
// MapExtension stores extension type for packet payload.
|
2019-06-05 08:23:18 +00:00
|
|
|
// The match is done per PacketType (iq, message, or presence) and XML tag name.
|
2019-06-04 15:04:25 +00:00
|
|
|
// You can use the alias "*" as local XML name to be able to match all unknown tag name for that
|
|
|
|
// packet type and namespace.
|
2019-06-05 08:23:18 +00:00
|
|
|
func (r *registry) MapExtension(pktType PacketType, name xml.Name, extension MsgExtension) {
|
2019-06-04 15:04:25 +00:00
|
|
|
key := registryKey{pktType, name.Space}
|
|
|
|
r.msgTypesLock.RLock()
|
|
|
|
store := r.msgTypes[key]
|
|
|
|
r.msgTypesLock.RUnlock()
|
|
|
|
|
2019-06-03 10:40:42 +00:00
|
|
|
r.msgTypesLock.Lock()
|
|
|
|
defer r.msgTypesLock.Unlock()
|
2019-06-04 15:04:25 +00:00
|
|
|
if store == nil {
|
|
|
|
store = make(map[string]reflect.Type)
|
|
|
|
}
|
|
|
|
store[name.Local] = reflect.TypeOf(extension)
|
|
|
|
r.msgTypes[key] = store
|
2019-06-03 10:40:42 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
// GetExtensionType returns extension type for packet payload, based on packet type and tag name.
|
2019-06-05 08:23:18 +00:00
|
|
|
func (r *registry) GetExtensionType(pktType PacketType, name xml.Name) reflect.Type {
|
2019-06-04 15:04:25 +00:00
|
|
|
key := registryKey{pktType, name.Space}
|
|
|
|
|
2019-06-03 10:40:42 +00:00
|
|
|
r.msgTypesLock.RLock()
|
|
|
|
defer r.msgTypesLock.RUnlock()
|
2019-06-04 15:04:25 +00:00
|
|
|
store := r.msgTypes[key]
|
|
|
|
result := store[name.Local]
|
|
|
|
if result == nil && name.Local != "*" {
|
|
|
|
return store["*"]
|
|
|
|
}
|
|
|
|
return result
|
2019-06-03 10:40:42 +00:00
|
|
|
}
|
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
// GetMsgExtension returns an instance of MsgExtension, by matching packet type and XML
|
|
|
|
// tag name against the registry.
|
|
|
|
func (r *registry) GetMsgExtension(name xml.Name) MsgExtension {
|
|
|
|
if extensionType := r.GetExtensionType(PKTMessage, name); extensionType != nil {
|
2019-06-03 10:40:42 +00:00
|
|
|
val := reflect.New(extensionType)
|
|
|
|
elt := val.Interface()
|
|
|
|
if msgExt, ok := elt.(MsgExtension); ok {
|
|
|
|
return msgExt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-06-04 15:04:25 +00:00
|
|
|
// GetIQExtension returns an instance of IQPayload, by matching packet type and XML
|
|
|
|
// tag name against the registry.
|
|
|
|
func (r *registry) GetIQExtension(name xml.Name) IQPayload {
|
|
|
|
if extensionType := r.GetExtensionType(PKTIQ, name); extensionType != nil {
|
|
|
|
val := reflect.New(extensionType)
|
|
|
|
elt := val.Interface()
|
|
|
|
if iqExt, ok := elt.(IQPayload); ok {
|
|
|
|
return iqExt
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|