parent
4a4c4850d1
commit
8f7b4ba8a4
|
@ -81,7 +81,7 @@ func (msg *Message) XMPPFormat() string {
|
|||
return string(out)
|
||||
}
|
||||
|
||||
// UnmarshalXML implements custom parsing for IQs
|
||||
// UnmarshalXML implements custom parsing for messages
|
||||
func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
msg.XMLName = start.Name
|
||||
|
||||
|
|
29
pres_muc.go
Normal file
29
pres_muc.go
Normal file
|
@ -0,0 +1,29 @@
|
|||
package xmpp
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// MUC Presence extension
|
||||
|
||||
// MucPresence implements XEP-0045: Multi-User Chat - 19.1
|
||||
type MucPresence struct {
|
||||
PresExtension
|
||||
XMLName xml.Name `xml:"http://jabber.org/protocol/muc x"`
|
||||
Password string `xml:"password,omitempty"`
|
||||
History History `xml:"history,omitempty"`
|
||||
}
|
||||
|
||||
// History implements XEP-0045: Multi-User Chat - 19.1
|
||||
type History struct {
|
||||
MaxChars int `xml:"maxchars,attr,omitempty"`
|
||||
MaxStanzas int `xml:"maxstanzas,attr,omitempty"`
|
||||
Seconds int `xml:"seconds,attr,omitempty"`
|
||||
Since time.Time `xml:"since,attr,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
TypeRegistry.MapExtension(PKTPresence, xml.Name{"http://jabber.org/protocol/muc", "x"}, MucPresence{})
|
||||
}
|
60
pres_muc_test.go
Normal file
60
pres_muc_test.go
Normal file
|
@ -0,0 +1,60 @@
|
|||
package xmpp_test
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"testing"
|
||||
|
||||
"gosrc.io/xmpp"
|
||||
)
|
||||
|
||||
// https://xmpp.org/extensions/xep-0045.html#example-27
|
||||
func TestMucPassword(t *testing.T) {
|
||||
str := `<presence
|
||||
from='hag66@shakespeare.lit/pda'
|
||||
id='djn4714'
|
||||
to='coven@chat.shakespeare.lit/thirdwitch'>
|
||||
<x xmlns='http://jabber.org/protocol/muc'>
|
||||
<password>cauldronburn</password>
|
||||
</x>
|
||||
</presence>`
|
||||
|
||||
var parsedPresence xmpp.Presence
|
||||
if err := xml.Unmarshal([]byte(str), &parsedPresence); err != nil {
|
||||
t.Errorf("Unmarshal(%s) returned error", str)
|
||||
}
|
||||
|
||||
var muc xmpp.MucPresence
|
||||
if ok := parsedPresence.Get(&muc); !ok {
|
||||
t.Error("muc presence extension was not found")
|
||||
}
|
||||
|
||||
if muc.Password != "cauldronburn" {
|
||||
t.Errorf("incorrect password: '%s'", muc.Password)
|
||||
}
|
||||
}
|
||||
|
||||
// https://xmpp.org/extensions/xep-0045.html#example-37
|
||||
func TestMucHistory(t *testing.T) {
|
||||
str := `<presence
|
||||
from='hag66@shakespeare.lit/pda'
|
||||
id='n13mt3l'
|
||||
to='coven@chat.shakespeare.lit/thirdwitch'>
|
||||
<x xmlns='http://jabber.org/protocol/muc'>
|
||||
<history maxstanzas='20'/>
|
||||
</x>
|
||||
</presence>`
|
||||
|
||||
var parsedPresence xmpp.Presence
|
||||
if err := xml.Unmarshal([]byte(str), &parsedPresence); err != nil {
|
||||
t.Errorf("Unmarshal(%s) returned error", str)
|
||||
}
|
||||
|
||||
var muc xmpp.MucPresence
|
||||
if ok := parsedPresence.Get(&muc); !ok {
|
||||
t.Error("muc presence extension was not found")
|
||||
}
|
||||
|
||||
if muc.History.MaxStanzas != 20 {
|
||||
t.Errorf("incorrect max stanza: '%d'", muc.History.MaxStanzas)
|
||||
}
|
||||
}
|
111
presence.go
111
presence.go
|
@ -1,6 +1,9 @@
|
|||
package xmpp
|
||||
|
||||
import "encoding/xml"
|
||||
import (
|
||||
"encoding/xml"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Presence Packet
|
||||
|
@ -9,10 +12,11 @@ import "encoding/xml"
|
|||
type Presence struct {
|
||||
XMLName xml.Name `xml:"presence"`
|
||||
Attrs
|
||||
Show PresenceShow `xml:"show,omitempty"`
|
||||
Status string `xml:"status,omitempty"`
|
||||
Priority int8 `xml:"priority,omitempty"` // default: 0
|
||||
Error Err `xml:"error,omitempty"`
|
||||
Show PresenceShow `xml:"show,omitempty"`
|
||||
Status string `xml:"status,omitempty"`
|
||||
Priority int8 `xml:"priority,omitempty"` // default: 0
|
||||
Error Err `xml:"error,omitempty"`
|
||||
Extensions []PresExtension `xml:",omitempty"`
|
||||
}
|
||||
|
||||
func (Presence) Name() string {
|
||||
|
@ -26,6 +30,37 @@ func NewPresence(a Attrs) Presence {
|
|||
}
|
||||
}
|
||||
|
||||
// Get search and extracts a specific extension on a presence stanza.
|
||||
// It receives a pointer to an PresExtension. It will panic if the caller
|
||||
// does not pass a pointer.
|
||||
// It will return true if the passed extension is found and set the pointer
|
||||
// to the extension passed as parameter to the found extension.
|
||||
// It will return false if the extension is not found on the presence.
|
||||
//
|
||||
// Example usage:
|
||||
// var muc xmpp.MucPresence
|
||||
// if ok := msg.Get(&muc); ok {
|
||||
// // muc presence extension has been found
|
||||
// }
|
||||
func (pres *Presence) Get(ext PresExtension) bool {
|
||||
target := reflect.ValueOf(ext)
|
||||
if target.Kind() != reflect.Ptr {
|
||||
panic("you must pass a pointer to the message Get method")
|
||||
}
|
||||
|
||||
for _, e := range pres.Extensions {
|
||||
if reflect.TypeOf(e) == target.Type() {
|
||||
source := reflect.ValueOf(e)
|
||||
if source.Kind() != reflect.Ptr {
|
||||
source = source.Elem()
|
||||
}
|
||||
target.Elem().Set(source.Elem())
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type presenceDecoder struct{}
|
||||
|
||||
var presence presenceDecoder
|
||||
|
@ -36,3 +71,69 @@ func (presenceDecoder) decode(p *xml.Decoder, se xml.StartElement) (Presence, er
|
|||
// TODO Add default presence type (when omitted)
|
||||
return packet, err
|
||||
}
|
||||
|
||||
// UnmarshalXML implements custom parsing for presence stanza
|
||||
func (pres *Presence) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||
pres.XMLName = start.Name
|
||||
|
||||
// Extract packet attributes
|
||||
for _, attr := range start.Attr {
|
||||
if attr.Name.Local == "id" {
|
||||
pres.Id = attr.Value
|
||||
}
|
||||
if attr.Name.Local == "type" {
|
||||
pres.Type = StanzaType(attr.Value)
|
||||
}
|
||||
if attr.Name.Local == "to" {
|
||||
pres.To = attr.Value
|
||||
}
|
||||
if attr.Name.Local == "from" {
|
||||
pres.From = attr.Value
|
||||
}
|
||||
if attr.Name.Local == "lang" {
|
||||
pres.Lang = attr.Value
|
||||
}
|
||||
}
|
||||
|
||||
// decode inner elements
|
||||
for {
|
||||
t, err := d.Token()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch tt := t.(type) {
|
||||
|
||||
case xml.StartElement:
|
||||
if presExt := TypeRegistry.GetPresExtension(tt.Name); presExt != nil {
|
||||
// Decode message extension
|
||||
err = d.DecodeElement(presExt, &tt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pres.Extensions = append(pres.Extensions, presExt)
|
||||
} else {
|
||||
// Decode standard message sub-elements
|
||||
var err error
|
||||
switch tt.Name.Local {
|
||||
case "show":
|
||||
err = d.DecodeElement(&pres.Show, &tt)
|
||||
case "status":
|
||||
err = d.DecodeElement(&pres.Status, &tt)
|
||||
case "priority":
|
||||
err = d.DecodeElement(&pres.Priority, &tt)
|
||||
case "error":
|
||||
err = d.DecodeElement(&pres.Error, &tt)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
case xml.EndElement:
|
||||
if tt == start.End() {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
registry.go
14
registry.go
|
@ -7,6 +7,7 @@ import (
|
|||
)
|
||||
|
||||
type MsgExtension interface{}
|
||||
type PresExtension 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.
|
||||
|
@ -78,6 +79,19 @@ func (r *registry) GetExtensionType(pktType PacketType, name xml.Name) reflect.T
|
|||
return result
|
||||
}
|
||||
|
||||
// GetPresExtension returns an instance of PresExtension, by matching packet type and XML
|
||||
// tag name against the registry.
|
||||
func (r *registry) GetPresExtension(name xml.Name) PresExtension {
|
||||
if extensionType := r.GetExtensionType(PKTPresence, name); extensionType != nil {
|
||||
val := reflect.New(extensionType)
|
||||
elt := val.Interface()
|
||||
if presExt, ok := elt.(PresExtension); ok {
|
||||
return presExt
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
|
Loading…
Reference in a new issue