diff --git a/xmpp.go b/xmpp.go
index 372aaf6..f801e29 100644
--- a/xmpp.go
+++ b/xmpp.go
@@ -640,6 +640,11 @@ func (c *Client) Recv() (stanza interface{}, err error) {
}
switch v := val.(type) {
case *clientMessage:
+ if v.Event.XMLNS == XMPPNS_PUBSUB_EVENT {
+ // Handle Pubsub notifications
+ return pubsubClientToReturn(v.Event), nil
+ }
+
stamp, _ := time.Parse(
"2006-01-02T15:04:05Z",
v.Delay.Stamp,
@@ -664,19 +669,91 @@ func (c *Client) Recv() (stanza interface{}, err error) {
case *clientPresence:
return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
case *clientIQ:
- // TODO check more strictly
- if v.Query.XMLName.Space == "urn:xmpp:ping" {
+ switch {
+ case v.Query.XMLName.Space == "urn:xmpp:ping":
+ // TODO check more strictly
err := c.SendResultPing(v.ID, v.From)
if err != nil {
return Chat{}, err
}
- }
- if v.Query.XMLName.Local == "" {
+ fallthrough
+ case v.Type == "error":
+ switch v.ID {
+ case "sub1":
+ // Pubsub subscription failed
+ var errs []clientPubsubError
+ err := xml.Unmarshal([]byte(v.Error.InnerXML), &errs)
+ if err != nil {
+ return PubsubSubscription{}, err
+ }
+
+ var errsStr []string
+ for _, e := range errs {
+ errsStr = append(errsStr, e.XMLName.Local)
+ }
+
+ return PubsubSubscription{
+ Errors: errsStr,
+ }, nil
+ }
+ case v.Type == "result" && v.ID == "unsub1":
+ // Unsubscribing MAY contain a pubsub element. But it does
+ // not have to
+ return PubsubUnsubscription{
+ SubID: "",
+ JID: v.From,
+ Node: "",
+ Errors: nil,
+ }, nil
+ case v.Query.XMLName.Local == "pubsub":
+ switch v.ID {
+ case "sub1":
+ // Subscription or unsubscription was successful
+ var sub clientPubsubSubscription
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
+ if err != nil {
+ return PubsubSubscription{}, err
+ }
+
+ return PubsubSubscription{
+ SubID: sub.SubID,
+ JID: sub.JID,
+ Node: sub.Node,
+ Errors: nil,
+ }, nil
+ case "unsub1":
+ var sub clientPubsubSubscription
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
+ if err != nil {
+ return PubsubUnsubscription{}, err
+ }
+
+ return PubsubUnsubscription{
+ SubID: sub.SubID,
+ JID: v.From,
+ Node: sub.Node,
+ Errors: nil,
+ }, nil
+ case "items1", "items3":
+ var p clientPubsubItems
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
+ if err != nil {
+ return PubsubItems{}, err
+ }
+
+ return PubsubItems{
+ p.Node,
+ pubsubItemsToReturn(p.Items),
+ }, nil
+ }
+ case v.Query.XMLName.Local == "":
return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type}, nil
- } else if res, err := xml.Marshal(v.Query); err != nil {
- // should never occur
- return Chat{}, err
- } else {
+ default:
+ res, err := xml.Marshal(v.Query)
+ if err != nil {
+ return Chat{}, err
+ }
+
return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type,
Query: res}, nil
}
@@ -832,6 +909,9 @@ type clientMessage struct {
Body string `xml:"body"`
Thread string `xml:"thread"`
+ // Pubsub
+ Event clientPubsubEvent `xml:"event"`
+
// Any hasn't matched element
Other []XMLElement `xml:",any"`
@@ -909,11 +989,12 @@ type clientIQ struct {
}
type clientError struct {
- XMLName xml.Name `xml:"jabber:client error"`
- Code string `xml:",attr"`
- Type string `xml:",attr"`
- Any xml.Name
- Text string
+ XMLName xml.Name `xml:"jabber:client error"`
+ Code string `xml:",attr"`
+ Type string `xml:"type,attr"`
+ Any xml.Name
+ InnerXML []byte `xml:",innerxml"`
+ Text string
}
type clientQuery struct {
diff --git a/xmpp_pubsub.go b/xmpp_pubsub.go
new file mode 100644
index 0000000..74e60d8
--- /dev/null
+++ b/xmpp_pubsub.go
@@ -0,0 +1,133 @@
+package xmpp
+
+import (
+ "encoding/xml"
+ "fmt"
+)
+
+const (
+ XMPPNS_PUBSUB = "http://jabber.org/protocol/pubsub"
+ XMPPNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event"
+)
+
+type clientPubsubItem struct {
+ XMLName xml.Name `xml:"item"`
+ ID string `xml:"id,attr"`
+ Body []byte `xml:",innerxml"`
+}
+
+type clientPubsubItems struct {
+ XMLName xml.Name `xml:"items"`
+ Node string `xml:"node,attr"`
+ Items []clientPubsubItem `xml:"item"`
+}
+
+type clientPubsub struct {
+ XMLName xml.Name `xml:"pubsub"`
+ Items clientPubsubItems `xml:"items"`
+}
+
+type clientPubsubEvent struct {
+ XMLName xml.Name `xml:"event"`
+ XMLNS string `xml:"xmlns,attr"`
+ Items clientPubsubItems `xml:"items"`
+}
+
+type clientPubsubError struct {
+ XMLName xml.Name
+}
+
+type clientPubsubSubscription struct {
+ XMLName xml.Name `xml:"subscription"`
+ Node string `xml:"node,attr"`
+ JID string `xml:"jid,attr"`
+ SubID string `xml:"subid,attr"`
+}
+
+type PubsubEvent struct {
+ Node string
+ Items []PubsubItem
+}
+
+type PubsubSubscription struct {
+ SubID string
+ JID string
+ Node string
+ Errors []string
+}
+type PubsubUnsubscription PubsubSubscription
+
+type PubsubItem struct {
+ ID string
+ InnerXML []byte
+}
+
+type PubsubItems struct {
+ Node string
+ Items []PubsubItem
+}
+
+// Converts []clientPubsubItem to []PubsubItem
+func pubsubItemsToReturn(items []clientPubsubItem) []PubsubItem {
+ var tmp []PubsubItem
+ for _, i := range items {
+ tmp = append(tmp, PubsubItem{
+ ID: i.ID,
+ InnerXML: i.Body,
+ })
+ }
+
+ return tmp
+}
+
+func pubsubClientToReturn(event clientPubsubEvent) PubsubEvent {
+ return PubsubEvent{
+ Node: event.Items.Node,
+ Items: pubsubItemsToReturn(event.Items.Items),
+ }
+}
+
+func pubsubStanza(body string) string {
+ return fmt.Sprintf("%s",
+ XMPPNS_PUBSUB, body)
+}
+
+func pubsubSubscriptionStanza(node, jid string) string {
+ body := fmt.Sprintf("",
+ xmlEscape(node),
+ xmlEscape(jid))
+ return pubsubStanza(body)
+}
+
+func pubsubUnsubscriptionStanza(node, jid string) string {
+ body := fmt.Sprintf("",
+ xmlEscape(node),
+ xmlEscape(jid))
+ return pubsubStanza(body)
+}
+
+func (c *Client) PubsubSubscribeNode(node, jid string) {
+ c.RawInformation(c.jid,
+ jid,
+ "sub1",
+ "set",
+ pubsubSubscriptionStanza(node, c.jid))
+}
+
+func (c *Client) PubsubUnsubscribeNode(node, jid string) {
+ c.RawInformation(c.jid,
+ jid,
+ "unsub1",
+ "set",
+ pubsubUnsubscriptionStanza(node, c.jid))
+}
+
+func (c *Client) PubsubRequestLastItems(node, jid string) {
+ body := fmt.Sprintf("", node)
+ c.RawInformation(c.jid, jid, "items1", "get", pubsubStanza(body))
+}
+
+func (c *Client) PubsubRequestItem(node, jid, id string) {
+ body := fmt.Sprintf(" ", node, id)
+ c.RawInformation(c.jid, jid, "items3", "get", pubsubStanza(body))
+}