Compare commits

..

No commits in common. "370c500a5e52d1d5a07a6d77ca5eae042950ad28" and "7ec2b8b7def69491528689aeb58253e9421a4b73" have entirely different histories.

14 changed files with 322 additions and 1833 deletions

View File

@ -3,4 +3,4 @@ go-xmpp
go xmpp library (original was written by russ cox )
[Documentation](https://godoc.org/github.com/xmppo/go-xmpp)
[Documentation](https://godoc.org/github.com/mattn/go-xmpp)

View File

@ -2,12 +2,11 @@ package main
import (
"crypto/tls"
"github.com/mattn/go-gtk/gtk"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
"github.com/matterbridge/go-xmpp"
"github.com/mattn/go-gtk/gtk"
)
func main() {

View File

@ -5,23 +5,20 @@ import (
"crypto/tls"
"flag"
"fmt"
"github.com/mattn/go-xmpp"
"log"
"os"
"strings"
"github.com/matterbridge/go-xmpp"
)
var (
server = flag.String("server", "talk.google.com:443", "server")
username = flag.String("username", "", "username")
password = flag.String("password", "", "password")
status = flag.String("status", "xa", "status")
statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
notls = flag.Bool("notls", false, "No TLS")
debug = flag.Bool("debug", false, "debug output")
session = flag.Bool("session", false, "use server session")
)
var server = flag.String("server", "talk.google.com:443", "server")
var username = flag.String("username", "", "username")
var password = flag.String("password", "", "password")
var status = flag.String("status", "xa", "status")
var statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
var notls = flag.Bool("notls", false, "No TLS")
var debug = flag.Bool("debug", false, "debug output")
var session = flag.Bool("session", false, "use server session")
func serverName(host string) string {
return strings.Split(host, ":")[0]
@ -51,8 +48,7 @@ func main() {
var talk *xmpp.Client
var err error
options := xmpp.Options{
Host: *server,
options := xmpp.Options{Host: *server,
User: *username,
Password: *password,
NoTLS: *notls,
@ -63,6 +59,7 @@ func main() {
}
talk, err = options.NewClient()
if err != nil {
log.Fatal(err)
}

8
go.mod
View File

@ -1,8 +0,0 @@
module github.com/matterbridge/go-xmpp
go 1.21.5
require (
golang.org/x/crypto v0.23.0
golang.org/x/net v0.25.0
)

4
go.sum
View File

@ -1,4 +0,0 @@
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=

1531
xmpp.go

File diff suppressed because it is too large Load Diff

View File

@ -1,125 +0,0 @@
package xmpp
import (
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"errors"
"strconv"
)
const (
XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data"
XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata"
)
type clientAvatarData struct {
XMLName xml.Name `xml:"data"`
Data []byte `xml:",innerxml"`
}
type clientAvatarInfo struct {
XMLName xml.Name `xml:"info"`
Bytes string `xml:"bytes,attr"`
Width string `xml:"width,attr"`
Height string `xml:"height,attr"`
ID string `xml:"id,attr"`
Type string `xml:"type,attr"`
URL string `xml:"url,attr"`
}
type clientAvatarMetadata struct {
XMLName xml.Name `xml:"metadata"`
XMLNS string `xml:"xmlns,attr"`
Info clientAvatarInfo `xml:"info"`
}
type AvatarData struct {
Data []byte
From string
}
type AvatarMetadata struct {
From string
Bytes int
Width int
Height int
ID string
Type string
URL string
}
func handleAvatarData(itemsBody []byte, from, id string) (AvatarData, error) {
var data clientAvatarData
err := xml.Unmarshal(itemsBody, &data)
if err != nil {
return AvatarData{}, err
}
// Base64-decode the avatar data to check its SHA1 hash
dataRaw, err := base64.StdEncoding.DecodeString(
string(data.Data))
if err != nil {
return AvatarData{}, err
}
hash := sha1.Sum(dataRaw)
hashStr := hex.EncodeToString(hash[:])
if hashStr != id {
return AvatarData{}, errors.New("SHA1 hashes do not match")
}
return AvatarData{
Data: dataRaw,
From: from,
}, nil
}
func handleAvatarMetadata(body []byte, from string) (AvatarMetadata, error) {
var meta clientAvatarMetadata
err := xml.Unmarshal(body, &meta)
if err != nil {
return AvatarMetadata{}, err
}
return AvatarMetadata{
From: from,
Bytes: atoiw(meta.Info.Bytes),
Width: atoiw(meta.Info.Width),
Height: atoiw(meta.Info.Height),
ID: meta.Info.ID,
Type: meta.Info.Type,
URL: meta.Info.URL,
}, nil
}
// A wrapper for atoi which just returns -1 if an error occurs
func atoiw(str string) int {
i, err := strconv.Atoi(str)
if err != nil {
return -1
}
return i
}
func (c *Client) AvatarSubscribeMetadata(jid string) {
c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarUnsubscribeMetadata(jid string) {
c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarRequestData(jid string) {
c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid)
}
func (c *Client) AvatarRequestDataByID(jid, id string) {
c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id)
}
func (c *Client) AvatarRequestMetadata(jid string) {
c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid)
}

View File

@ -1,99 +0,0 @@
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
}

View File

@ -5,45 +5,27 @@ import (
"strconv"
)
const (
IQTypeGet = "get"
IQTypeSet = "set"
IQTypeResult = "result"
)
const IQTypeGet = "get"
const IQTypeSet = "set"
const IQTypeResult = "result"
func (c *Client) Discovery() (string, error) {
// use UUIDv4 for a pseudo random id.
const namespace = "http://jabber.org/protocol/disco#items"
// use getCookie for a pseudo random id.
reqID := strconv.FormatUint(uint64(getCookie()), 10)
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)
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, namespace, "")
}
// RawInformationQuery sends an information query request to the server.
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>\n"
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
return id, err
}
// rawInformation send a IQ request with the payload body to the server
// rawInformation send a IQ request with the the payload body to the server
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>\n"
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>"
_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
return id, err
}

View File

@ -8,24 +8,24 @@
package xmpp
import (
"errors"
"fmt"
"time"
"errors"
)
const (
nsMUC = "http://jabber.org/protocol/muc"
nsMUCUser = "http://jabber.org/protocol/muc#user"
NoHistory = 0
CharHistory = 1
StanzaHistory = 2
nsMUC = "http://jabber.org/protocol/muc"
nsMUCUser = "http://jabber.org/protocol/muc#user"
NoHistory = 0
CharHistory = 1
StanzaHistory = 2
SecondsHistory = 3
SinceHistory = 4
SinceHistory = 4
)
// Send sends room topic wrapped inside an XMPP message stanza body.
func (c *Client) SendTopic(chat Chat) (n int, err error) {
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>\n",
return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
@ -33,10 +33,10 @@ func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
if nick == "" {
nick = c.jid
}
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
"<x xmlns='%s'>"+
"<history maxchars='0'/></x>"+
"</presence>\n",
"<history maxchars='0'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC)
}
@ -47,35 +47,35 @@ func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_da
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s' />"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s' />\n" +
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC)
case CharHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case StanzaHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SecondsHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<history since='%s'/></x>\n" +
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
}
}
return 0, errors.New("Unknown history option")
@ -88,41 +88,41 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"</x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>" +
"</x>\n" +
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
case CharHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history maxchars='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case StanzaHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history maxstanzas='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SecondsHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history seconds='%d'/></x>\n"+
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
"<x xmlns='%s'>\n" +
"<password>%s</password>\n"+
"<history since='%s'/></x>\n" +
"</presence>",
xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
}
}
return 0, errors.New("Unknown history option")
@ -130,6 +130,6 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
// xep-0045 7.14
func (c *Client) LeaveMUC(jid string) (n int, err error) {
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />\n",
return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />",
c.jid, xmlEscape(jid))
}

View File

@ -11,23 +11,23 @@ func (c *Client) PingC2S(jid, server string) error {
if server == "" {
server = c.domain
}
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(jid), xmlEscape(server))
return err
}
func (c *Client) PingS2S(fromServer, toServer string) error {
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
"<ping xmlns='urn:xmpp:ping'/>\n"+
"</iq>",
xmlEscape(fromServer), xmlEscape(toServer))
return err
}
func (c *Client) SendResultPing(id, toServer string) error {
_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>\n",
_, err := fmt.Fprintf(c.conn, "<iq type='result' to='%s' id='%s'/>",
xmlEscape(toServer), xmlEscape(id))
return err
}

View File

@ -1,128 +0,0 @@
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 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))
}

View File

@ -5,21 +5,16 @@ import (
)
func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>\n",
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
xmlEscape(jid))
}
func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='unsubscribed'/>\n",
xmlEscape(jid))
}
func (c *Client) RetrieveSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribe'/>\n",
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
xmlEscape(jid))
}
func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>\n",
fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
xmlEscape(jid))
}

View File

@ -85,14 +85,12 @@ func TestStanzaError(t *testing.T) {
"\n\t\t\n\t\t\n\t",
},
OtherElem: []XMLElement{
{
XMLElement{
XMLName: xml.Name{Space: "google:mobile:data", Local: "gcm"},
Attr: []xml.Attr{{Name: xml.Name{Space: "", Local: "xmlns"}, Value: "google:mobile:data"}},
InnerXML: "\n\t\t{\"random\": \"&lt;text&gt;\"}\n\t",
},
{
XMLElement{
XMLName: xml.Name{Space: "jabber:client", Local: "error"},
Attr: []xml.Attr{{Name: xml.Name{Space: "", Local: "code"}, Value: "400"}, {Name: xml.Name{Space: "", Local: "type"}, Value: "modify"}},
InnerXML: `
<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
@ -116,28 +114,3 @@ func TestEOFError(t *testing.T) {
t.Errorf("Recv() did not return io.EOF on end of input stream")
}
}
var emptyPubSub = strings.TrimSpace(`
<iq xmlns="jabber:client" type='result' from='juliet@capulet.lit' id='items3'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<items node='urn:xmpp:avatar:data'></items>
</pubsub>
</iq>
`)
func TestEmptyPubsub(t *testing.T) {
var c Client
c.conn = tConnect(emptyPubSub)
c.p = xml.NewDecoder(c.conn)
m, err := c.Recv()
switch m.(type) {
case AvatarData:
if err == nil {
t.Errorf("Expected an error to be returned")
}
default:
t.Errorf("Recv() = %v", m)
t.Errorf("Expected a return value of AvatarData")
}
}