diff --git a/component.go b/component.go index 7bd6a1b..c1d41c3 100644 --- a/component.go +++ b/component.go @@ -216,6 +216,40 @@ func (handshakeDecoder) decode(p *xml.Decoder, se xml.StartElement) (Handshake, return packet, err } +// ============================================================================ +// Component delegation +// XEP-0355 + +// Delegation can be used both on message (for delegated) and IQ (for Forwarded), +// depending on the context. +type Delegation struct { + MsgExtension + XMLName xml.Name `xml:"urn:xmpp:delegation:1 delegation"` + Forwarded Forwarded // This is used in iq to wrap delegated iqs + Delegated Delegated // This is used in a message to confirm delegated namespace +} + +func (d *Delegation) Namespace() string { + return d.XMLName.Space +} + +type Forwarded struct { + XMLName xml.Name `xml:"urn:xmpp:forward:0 forwarded"` + IQ IQ + Message Message + Presence Presence +} + +type Delegated struct { + XMLName xml.Name `xml:"delegated"` + Namespace string `xml:"namespace,attr,omitempty"` +} + +func init() { + TypeRegistry.MapExtension(PKTMessage, xml.Name{"urn:xmpp:delegation:1", "delegation"}, Delegation{}) + TypeRegistry.MapExtension(PKTIQ, xml.Name{"urn:xmpp:delegation:1", "delegation"}, Delegation{}) +} + /* TODO: Add support for discovery management directly in component TODO: Support multiple identities on disco info diff --git a/component_test.go b/component_test.go index 20e3772..ee933aa 100644 --- a/component_test.go +++ b/component_test.go @@ -1,6 +1,9 @@ package xmpp // import "gosrc.io/xmpp" -import "testing" +import ( + "encoding/xml" + "testing" +) func TestHandshake(t *testing.T) { opts := ComponentOptions{ @@ -21,3 +24,72 @@ func TestHandshake(t *testing.T) { func TestGenerateHandshake(t *testing.T) { // TODO } + +// We should be able to properly parse delegation confirmation messages +func TestParsingDelegationMessage(t *testing.T) { + packetStr := ` + + + +` + var msg Message + data := []byte(packetStr) + if err := xml.Unmarshal(data, &msg); err != nil { + t.Errorf("Unmarshal(%s) returned error", data) + } + + // Check that we have extracted the delegation info as MsgExtension + var nsDelegated string + for _, ext := range msg.Extensions { + if delegation, ok := ext.(*Delegation); ok { + nsDelegated = delegation.Delegated.Namespace + } + } + if nsDelegated != "http://jabber.org/protocol/pubsub" { + t.Errorf("Could not find delegated namespace in delegation: %#v\n", msg) + } +} + +// Check that we can parse a delegation IQ. +// The most important thing is to be able to +func TestParsingDelegationIQ(t *testing.T) { + packetStr := ` + + + + + + + + + + + + + + + +` + var iq IQ + data := []byte(packetStr) + if err := xml.Unmarshal(data, &iq); err != nil { + t.Errorf("Unmarshal(%s) returned error", data) + } + + // Check that we have extracted the delegation info as IQPayload + var node string + for _, ext := range iq.Payload { + if delegation, ok := ext.(*Delegation); ok { + payload := delegation.Forwarded.IQ.Payload + if len(payload) > 0 { + payload := delegation.Forwarded.IQ.Payload[0] + if pubsub, ok := payload.(*PubSub); ok { + node = pubsub.Publish.Node + } + } + } + } + if node != "http://jabber.org/protocol/mood" { + t.Errorf("Could not find mood node name on delegated publish: %#v\n", iq) + } +} diff --git a/pep.go b/pep.go index 6ead8ae..91c0188 100644 --- a/pep.go +++ b/pep.go @@ -1,25 +1,11 @@ package xmpp // import "gosrc.io/xmpp" +// TODO: Move to a pubsub file + import ( "encoding/xml" ) -type PubSub struct { - XMLName xml.Name `xml:"http://jabber.org/protocol/pubsub pubsub"` - Publish Publish -} - -type Publish struct { - XMLName xml.Name `xml:"publish"` - Node string `xml:"node,attr"` - Item Item -} - -type Item struct { - XMLName xml.Name `xml:"item"` - Tune Tune -} - type Tune struct { XMLName xml.Name `xml:"http://jabber.org/protocol/tune tune"` Artist string `xml:"artist,omitempty"` @@ -31,53 +17,9 @@ type Tune struct { Uri string `xml:"uri,omitempty"` } -/* -type PubsubPublish struct { - XMLName xml.Name `xml:"publish"` - node string `xml:"node,attr"` - item PubSubItem +type Mood struct { + XMLName xml.Name `xml:"http://jabber.org/protocol/mood mood"` + // TODO: Custom parsing to extract mood type from tag name + // Mood type + Text string `xml:"text,omitempty"` } - -type PubSubItem struct { - xmlName xml.Name `xml:"item"` -} - -type Thing2 struct { - XMLName xml.Name `xml:"publish"` - node string `xml:"node,attr"` - tune string `xml:"http://jabber.org/protocol/tune item>tune"` -} - -type Tune struct { - artist string - length int - rating int - source string - title string - track string - uri string -} -*/ - -/* -func (*Tune) XMPPFormat() string { - return fmt.Sprintf( - ` - - - - - %s - %i - %i - %s - %s - %s - %s - - - - -`) -} -*/ diff --git a/pubsub.go b/pubsub.go new file mode 100644 index 0000000..341c54f --- /dev/null +++ b/pubsub.go @@ -0,0 +1,39 @@ +package xmpp // import "gosrc.io/xmpp" + +import ( + "encoding/xml" +) + +type PubSub struct { + XMLName xml.Name `xml:"http://jabber.org/protocol/pubsub pubsub"` + Publish Publish + Retract Retract + // TODO +} + +func (p *PubSub) Namespace() string { + return p.XMLName.Space +} + +type Publish struct { + XMLName xml.Name `xml:"publish"` + Node string `xml:"node,attr"` + Item Item +} + +type Item struct { + XMLName xml.Name `xml:"item"` + Id string `xml:"id,attr,omitempty"` + Tune Tune +} + +type Retract struct { + XMLName xml.Name `xml:"retract"` + Node string `xml:"node,attr"` + Notify string `xml:"notify,attr"` + Item Item +} + +func init() { + TypeRegistry.MapExtension(PKTIQ, xml.Name{"http://jabber.org/protocol/pubsub", "pubsub"}, PubSub{}) +} diff --git a/router.go b/router.go index b1286f2..92b1594 100644 --- a/router.go +++ b/router.go @@ -8,6 +8,11 @@ import ( The XMPP router helps client and component developers select which XMPP they would like to process, and associate processing code depending on the router configuration. +Here are important rules to keep in mind while setting your routes and matchers: +- Routes are evaluated in the order they are set. +- When a route matches, it is executed and all others routes are ignored. For each packet, only a single + route is executed. + TODO: Automatically reply to IQ that do not match any route, to comply to XMPP standard. */