forked from jshiffer/go-xmpp
Implement Disco queries against other entities (#124)
* Improve support for XEP-0030 This commit allows the user to query information about the server or a node belonging to the server as per XEP-0030. * Fix broken PubSub functionality
This commit is contained in:
parent
9fc0b1236c
commit
d72a0f3154
54
xmpp.go
54
xmpp.go
@ -733,18 +733,10 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
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
|
||||||
}
|
}
|
||||||
case v.Type == "result" && v.ID == "unsub1":
|
case v.Type == "result":
|
||||||
// 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 {
|
switch v.ID {
|
||||||
case "sub1":
|
case "sub1":
|
||||||
|
if v.Query.XMLName.Local == "pubsub" {
|
||||||
// Subscription or unsubscription was successful
|
// Subscription or unsubscription was successful
|
||||||
var sub clientPubsubSubscription
|
var sub clientPubsubSubscription
|
||||||
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
|
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
|
||||||
@ -758,7 +750,9 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
Node: sub.Node,
|
Node: sub.Node,
|
||||||
Errors: nil,
|
Errors: nil,
|
||||||
}, nil
|
}, nil
|
||||||
|
}
|
||||||
case "unsub1":
|
case "unsub1":
|
||||||
|
if v.Query.XMLName.Local == "pubsub" {
|
||||||
var sub clientPubsubSubscription
|
var sub clientPubsubSubscription
|
||||||
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
|
err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -771,7 +765,44 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
Node: sub.Node,
|
Node: sub.Node,
|
||||||
Errors: nil,
|
Errors: nil,
|
||||||
}, nil
|
}, nil
|
||||||
|
} else {
|
||||||
|
// Unsubscribing MAY contain a pubsub element. But it does
|
||||||
|
// not have to
|
||||||
|
return PubsubUnsubscription{
|
||||||
|
SubID: "",
|
||||||
|
JID: v.From,
|
||||||
|
Node: "",
|
||||||
|
Errors: nil,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
case "info1":
|
||||||
|
if v.Query.XMLName.Space == XMPPNS_DISCO_ITEMS {
|
||||||
|
var itemsQuery clientDiscoItemsQuery
|
||||||
|
err := xml.Unmarshal(v.InnerXML, &itemsQuery)
|
||||||
|
if err != nil {
|
||||||
|
return []DiscoItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiscoItems{
|
||||||
|
Jid: v.From,
|
||||||
|
Items: clientDiscoItemsToReturn(itemsQuery.Items),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
case "info3":
|
||||||
|
if v.Query.XMLName.Space == XMPPNS_DISCO_INFO {
|
||||||
|
var disco clientDiscoQuery
|
||||||
|
err := xml.Unmarshal(v.InnerXML, &disco)
|
||||||
|
if err != nil {
|
||||||
|
return DiscoResult{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return DiscoResult{
|
||||||
|
Features: clientFeaturesToReturn(disco.Features),
|
||||||
|
Identities: clientIdentitiesToReturn(disco.Identities),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
case "items1", "items3":
|
case "items1", "items3":
|
||||||
|
if v.Query.XMLName.Local == "pubsub" {
|
||||||
var p clientPubsubItems
|
var p clientPubsubItems
|
||||||
err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
|
err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -800,6 +831,7 @@ func (c *Client) Recv() (stanza interface{}, err error) {
|
|||||||
pubsubItemsToReturn(p.Items),
|
pubsubItemsToReturn(p.Items),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: XEP-0084 states that metadata and data
|
// Note: XEP-0084 states that metadata and data
|
||||||
// should be fetched with an id of retrieve1.
|
// should be fetched with an id of retrieve1.
|
||||||
// Since we already have PubSub implemented, we
|
// Since we already have PubSub implemented, we
|
||||||
@ -1072,6 +1104,8 @@ type clientIQ struct {
|
|||||||
Query XMLElement `xml:",any"`
|
Query XMLElement `xml:",any"`
|
||||||
Error clientError
|
Error clientError
|
||||||
Bind bindBind
|
Bind bindBind
|
||||||
|
|
||||||
|
InnerXML []byte `xml:",innerxml"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type clientError struct {
|
type clientError struct {
|
||||||
|
99
xmpp_disco.go
Normal file
99
xmpp_disco.go
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
package xmpp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
|
||||||
|
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
|
||||||
|
)
|
||||||
|
|
||||||
|
type clientDiscoFeature struct {
|
||||||
|
XMLName xml.Name `xml:"feature"`
|
||||||
|
Var string `xml:"var,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientDiscoIdentity struct {
|
||||||
|
XMLName xml.Name `xml:"identity"`
|
||||||
|
Category string `xml:"category,attr"`
|
||||||
|
Type string `xml:"type,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientDiscoQuery struct {
|
||||||
|
XMLName xml.Name `xml:"query"`
|
||||||
|
Features []clientDiscoFeature `xml:"feature"`
|
||||||
|
Identities []clientDiscoIdentity `xml:"identity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientDiscoItem struct {
|
||||||
|
XMLName xml.Name `xml:"item"`
|
||||||
|
Jid string `xml:"jid,attr"`
|
||||||
|
Node string `xml:"node,attr"`
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type clientDiscoItemsQuery struct {
|
||||||
|
XMLName xml.Name `xml:"query"`
|
||||||
|
Items []clientDiscoItem `xml:"item"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoIdentity struct {
|
||||||
|
Category string
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoItem struct {
|
||||||
|
Jid string
|
||||||
|
Name string
|
||||||
|
Node string
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoResult struct {
|
||||||
|
Features []string
|
||||||
|
Identities []DiscoIdentity
|
||||||
|
}
|
||||||
|
|
||||||
|
type DiscoItems struct {
|
||||||
|
Jid string
|
||||||
|
Items []DiscoItem
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientFeaturesToReturn(features []clientDiscoFeature) []string {
|
||||||
|
var ret []string
|
||||||
|
|
||||||
|
for _, feature := range features {
|
||||||
|
ret = append(ret, feature.Var)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientIdentitiesToReturn(identities []clientDiscoIdentity) []DiscoIdentity {
|
||||||
|
var ret []DiscoIdentity
|
||||||
|
|
||||||
|
for _, id := range identities {
|
||||||
|
ret = append(ret, DiscoIdentity{
|
||||||
|
Category: id.Category,
|
||||||
|
Type: id.Type,
|
||||||
|
Name: id.Name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func clientDiscoItemsToReturn(items []clientDiscoItem) []DiscoItem {
|
||||||
|
var ret []DiscoItem
|
||||||
|
for _, item := range items {
|
||||||
|
ret = append(ret, DiscoItem{
|
||||||
|
Jid: item.Jid,
|
||||||
|
Name: item.Name,
|
||||||
|
Node: item.Node,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
@ -10,10 +10,26 @@ const IQTypeSet = "set"
|
|||||||
const IQTypeResult = "result"
|
const IQTypeResult = "result"
|
||||||
|
|
||||||
func (c *Client) Discovery() (string, error) {
|
func (c *Client) Discovery() (string, error) {
|
||||||
const namespace = "http://jabber.org/protocol/disco#items"
|
|
||||||
// use getCookie for a pseudo random id.
|
// use getCookie for a pseudo random id.
|
||||||
reqID := strconv.FormatUint(uint64(getCookie()), 10)
|
reqID := strconv.FormatUint(uint64(getCookie()), 10)
|
||||||
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "")
|
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover information about a node
|
||||||
|
func (c *Client) DiscoverNodeInfo(node string) (string, error) {
|
||||||
|
query := fmt.Sprintf("<query xmlns='%s' node='%s'/>", XMPPNS_DISCO_INFO, node)
|
||||||
|
return c.RawInformation(c.jid, c.domain, "info3", IQTypeGet, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover items that the server exposes
|
||||||
|
func (c *Client) DiscoverServerItems() (string, error) {
|
||||||
|
return c.DiscoverEntityItems(c.domain)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discover items that an entity exposes
|
||||||
|
func (c *Client) DiscoverEntityItems(jid string) (string, error) {
|
||||||
|
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_ITEMS)
|
||||||
|
return c.RawInformation(c.jid, jid, "info1", IQTypeGet, query)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawInformationQuery sends an information query request to the server.
|
// RawInformationQuery sends an information query request to the server.
|
||||||
|
Loading…
Reference in New Issue
Block a user