forked from jshiffer/go-xmpp
Implement a bit of XEP-0060 (PubSub) (#119)
This squashed series of commits implements basic PubSub functionality like requesting data or subscribing to a PubSub node.
This commit is contained in:
parent
3e4868bd3e
commit
899ef71e80
93
xmpp.go
93
xmpp.go
@ -640,6 +640,11 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
}
|
}
|
||||||
switch v := val.(type) {
|
switch v := val.(type) {
|
||||||
case *clientMessage:
|
case *clientMessage:
|
||||||
|
if v.Event.XMLNS == XMPPNS_PUBSUB_EVENT {
|
||||||
|
// Handle Pubsub notifications
|
||||||
|
return pubsubClientToReturn(v.Event), nil
|
||||||
|
}
|
||||||
|
|
||||||
stamp, _ := time.Parse(
|
stamp, _ := time.Parse(
|
||||||
"2006-01-02T15:04:05Z",
|
"2006-01-02T15:04:05Z",
|
||||||
v.Delay.Stamp,
|
v.Delay.Stamp,
|
||||||
@ -664,19 +669,91 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
case *clientPresence:
|
case *clientPresence:
|
||||||
return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
|
return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
|
||||||
case *clientIQ:
|
case *clientIQ:
|
||||||
|
switch {
|
||||||
|
case v.Query.XMLName.Space == "urn:xmpp:ping":
|
||||||
// TODO check more strictly
|
// TODO check more strictly
|
||||||
if v.Query.XMLName.Space == "urn:xmpp:ping" {
|
|
||||||
err := c.SendResultPing(v.ID, v.From)
|
err := c.SendResultPing(v.ID, v.From)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Chat{}, err
|
return Chat{}, err
|
||||||
}
|
}
|
||||||
|
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
|
||||||
}
|
}
|
||||||
if v.Query.XMLName.Local == "" {
|
|
||||||
|
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
|
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 {
|
default:
|
||||||
// should never occur
|
res, err := xml.Marshal(v.Query)
|
||||||
|
if err != nil {
|
||||||
return Chat{}, err
|
return Chat{}, err
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type,
|
return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type,
|
||||||
Query: res}, nil
|
Query: res}, nil
|
||||||
}
|
}
|
||||||
@ -832,6 +909,9 @@ type clientMessage struct {
|
|||||||
Body string `xml:"body"`
|
Body string `xml:"body"`
|
||||||
Thread string `xml:"thread"`
|
Thread string `xml:"thread"`
|
||||||
|
|
||||||
|
// Pubsub
|
||||||
|
Event clientPubsubEvent `xml:"event"`
|
||||||
|
|
||||||
// Any hasn't matched element
|
// Any hasn't matched element
|
||||||
Other []XMLElement `xml:",any"`
|
Other []XMLElement `xml:",any"`
|
||||||
|
|
||||||
@ -911,8 +991,9 @@ type clientIQ struct {
|
|||||||
type clientError struct {
|
type clientError struct {
|
||||||
XMLName xml.Name `xml:"jabber:client error"`
|
XMLName xml.Name `xml:"jabber:client error"`
|
||||||
Code string `xml:",attr"`
|
Code string `xml:",attr"`
|
||||||
Type string `xml:",attr"`
|
Type string `xml:"type,attr"`
|
||||||
Any xml.Name
|
Any xml.Name
|
||||||
|
InnerXML []byte `xml:",innerxml"`
|
||||||
Text string
|
Text string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
133
xmpp_pubsub.go
Normal file
133
xmpp_pubsub.go
Normal file
@ -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("<pubsub xmlns='%s'>%s</pubsub>",
|
||||||
|
XMPPNS_PUBSUB, body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pubsubSubscriptionStanza(node, jid string) string {
|
||||||
|
body := fmt.Sprintf("<subscribe node='%s' jid='%s'/>",
|
||||||
|
xmlEscape(node),
|
||||||
|
xmlEscape(jid))
|
||||||
|
return pubsubStanza(body)
|
||||||
|
}
|
||||||
|
|
||||||
|
func pubsubUnsubscriptionStanza(node, jid string) string {
|
||||||
|
body := fmt.Sprintf("<unsubscribe node='%s' jid='%s'/>",
|
||||||
|
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("<items node='%s'/>", node)
|
||||||
|
c.RawInformation(c.jid, jid, "items1", "get", pubsubStanza(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) PubsubRequestItem(node, jid, id string) {
|
||||||
|
body := fmt.Sprintf("<items node='%s'><item id='%s'/></items>", node, id)
|
||||||
|
c.RawInformation(c.jid, jid, "items3", "get", pubsubStanza(body))
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user