forked from jshiffer/go-xmpp
parent
4a4c4850d1
commit
8f7b4ba8a4
@ -81,7 +81,7 @@ func (msg *Message) XMPPFormat() string {
|
|||||||
return string(out)
|
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 {
|
func (msg *Message) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
msg.XMLName = start.Name
|
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)
|
||||||
|
}
|
||||||
|
}
|
103
presence.go
103
presence.go
@ -1,6 +1,9 @@
|
|||||||
package xmpp
|
package xmpp
|
||||||
|
|
||||||
import "encoding/xml"
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Presence Packet
|
// Presence Packet
|
||||||
@ -13,6 +16,7 @@ type Presence struct {
|
|||||||
Status string `xml:"status,omitempty"`
|
Status string `xml:"status,omitempty"`
|
||||||
Priority int8 `xml:"priority,omitempty"` // default: 0
|
Priority int8 `xml:"priority,omitempty"` // default: 0
|
||||||
Error Err `xml:"error,omitempty"`
|
Error Err `xml:"error,omitempty"`
|
||||||
|
Extensions []PresExtension `xml:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (Presence) Name() string {
|
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{}
|
type presenceDecoder struct{}
|
||||||
|
|
||||||
var presence presenceDecoder
|
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)
|
// TODO Add default presence type (when omitted)
|
||||||
return packet, err
|
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 MsgExtension interface{}
|
||||||
|
type PresExtension interface{}
|
||||||
|
|
||||||
// The Registry for msg and IQ types is a global variable.
|
// 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.
|
// 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
|
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
|
// GetMsgExtension returns an instance of MsgExtension, by matching packet type and XML
|
||||||
// tag name against the registry.
|
// tag name against the registry.
|
||||||
func (r *registry) GetMsgExtension(name xml.Name) MsgExtension {
|
func (r *registry) GetMsgExtension(name xml.Name) MsgExtension {
|
||||||
|
Loading…
Reference in New Issue
Block a user