forked from jshiffer/go-xmpp
		
	Compare commits
	
		
			128 Commits
		
	
	
		
			7ec2b8b7de
			...
			370c500a5e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						370c500a5e
	
				 | 
					
					
						|||
| 
						 | 
					7154bfeb76 | ||
| 
						 | 
					243a438354 | ||
| 
						 | 
					e9123cc4b3 | ||
| 
						 | 
					4be597a84a | ||
| 
						 | 
					464fbe04ef | ||
| 
						 | 
					e223dcf94b | ||
| 
						 | 
					321c2b14a5 | ||
| 
						 | 
					9161feef4d | ||
| 
						 | 
					fc3ed9a0b8 | ||
| 
						 | 
					d9df620fa4 | ||
| 
						 | 
					f067814851 | ||
| 
						 | 
					961b7e435e | ||
| 
						 | 
					12a04e0950 | ||
| 
						 | 
					2f331ed19c | ||
| 
						 | 
					b0f55a8f7f | ||
| 
						 | 
					7486b7a363 | ||
| 
						 | 
					da2377ecb0 | ||
| 
						 | 
					44095406a2 | ||
| 
						 | 
					d7aee6b636 | ||
| 
						 | 
					0324b31f56 | ||
| 
						 | 
					ca4e49201e | ||
| 
						 | 
					6e5d6e449e | ||
| 
						 | 
					0ae62a33a2 | ||
| 
						 | 
					ce687243c1 | ||
| 
						 | 
					78d07e9eee | ||
| 
						 | 
					416bb6e7b7 | ||
| 
						 | 
					aef1257ed1 | ||
| 
						 | 
					da17a46e6f | ||
| 
						 | 
					eedd7259cb | ||
| 
						 | 
					07196efcf3 | ||
| 
						 | 
					bbd90cc04b | ||
| 
						 | 
					0c7ee22452 | ||
| 
						 | 
					862c21f845 | ||
| 
						 | 
					bc81053dbc | ||
| 
						 | 
					94ab540b80 | ||
| 
						 | 
					f6a9836fdf | ||
| 
						 | 
					8ab32d885f | ||
| 
						 | 
					73f06c9f3d | ||
| 
						 | 
					9c5e758356 | ||
| 
						 | 
					ea4874e8c9 | ||
| 
						 | 
					dab6865bd2 | ||
| 
						 | 
					c051d69509 | ||
| 
						 | 
					aed021cf3e | ||
| 
						 | 
					2c4708e724 | ||
| 
						 | 
					746409f074 | ||
| 
						 | 
					9684a8ff69 | ||
| 
						 | 
					b7ea9f4be1 | ||
| 
						 | 
					e2bc7bf6d7 | ||
| 
						 | 
					0bcc057225 | ||
| 
						 | 
					49054ca9e9 | ||
| 
						 | 
					b369b7df10 | ||
| 
						 | 
					cc481e54e7 | ||
| 
						 | 
					88855eac82 | ||
| 
						 | 
					0cc0a72c15 | ||
| 
						 | 
					d6e9a15f29 | ||
| 
						 | 
					6ffd595a06 | ||
| 
						 | 
					62928b3483 | ||
| 
						 | 
					d67787ca0f | ||
| 
						 | 
					f8a24505f4 | ||
| 
						 | 
					685570cbd8 | ||
| 
						 | 
					7bfa331758 | ||
| 
						 | 
					3f0cbac307 | ||
| 
						 | 
					7ccad52e63 | ||
| 
						 | 
					705f68d1a5 | ||
| 
						 | 
					b49bdce100 | ||
| 
						 | 
					f4c732fdc7 | ||
| 
						 | 
					d3d16d5db9 | ||
| 
						 | 
					34d683d25a | ||
| 
						 | 
					dffa92c129 | ||
| 
						 | 
					8531e2e36a | ||
| 
						 | 
					e7d5b17113 | ||
| 
						 | 
					c1b9689e75 | ||
| 
						 | 
					424970d23c | ||
| 
						 | 
					5fdcf18a81 | ||
| 
						 | 
					794ed98f9f | ||
| 
						 | 
					2f9bd427e8 | ||
| 
						 | 
					70c2fe6900 | ||
| 
						 | 
					39f5b80375 | ||
| 
						 | 
					2449f4192b | ||
| 
						 | 
					3462085098 | ||
| 
						 | 
					6c9243326e | ||
| 
						 | 
					31c7eb6919 | ||
| 
						 | 
					9dcf67c0ad | ||
| 
						 | 
					4c385a334c | ||
| 
						 | 
					24e0f536cb | ||
| 
						 | 
					a6b124c9b2 | ||
| 
						 | 
					6138e9dbe5 | ||
| 
						 | 
					98ff0d4df7 | ||
| 
						 | 
					bef3e549f7 | ||
| 
						 | 
					9129a110df | ||
| 
						 | 
					d72a0f3154 | ||
| 
						 | 
					9fc0b1236c | ||
| 
						 | 
					05cd75074a | ||
| 
						 | 
					369824c83a | ||
| 
						 | 
					2eb234970c | ||
| 
						 | 
					3b26f73300 | ||
| 
						 | 
					1411b9cc8b | ||
| 
						 | 
					99ddfc1aa4 | ||
| 
						 | 
					e773596ea0 | ||
| 
						 | 
					912ba61489 | ||
| 
						 | 
					3871461df9 | ||
| 
						 | 
					db1339b3a5 | ||
| 
						 | 
					b40e129499 | ||
| 
						 | 
					42ee290fc5 | ||
| 
						 | 
					da2b7586cd | ||
| 
						 | 
					37fa6ef92f | ||
| 
						 | 
					899ef71e80 | ||
| 
						 | 
					3e4868bd3e | ||
| 
						 | 
					a86b6abcb3 | ||
| 
						 | 
					ac4c216a42 | ||
| 
						 | 
					6093f50721 | ||
| 
						 | 
					1f614e5b8d | ||
| 
						 | 
					ef6a1a617c | ||
| 
						 | 
					65fd08aee2 | ||
| 
						 | 
					a79a0e59ef | ||
| 
						 | 
					5709ddefa8 | ||
| 
						 | 
					51b558cd2c | ||
| 
						 | 
					66c008d798 | ||
| 
						 | 
					224305b3ef | ||
| 
						 | 
					1e7b50b41c | ||
| 
						 | 
					c18873b880 | ||
| 
						 | 
					2c5079ea28 | ||
| 
						 | 
					113d9c0420 | ||
| 
						 | 
					e543ad3fcd | ||
| 
						 | 
					4fdbee9ac5 | ||
| 
						 | 
					8a5843171f | ||
| 
						 | 
					04ea54f191 | 
@@ -3,4 +3,4 @@ go-xmpp
 | 
			
		||||
 | 
			
		||||
go xmpp library (original was written by russ cox  )
 | 
			
		||||
 | 
			
		||||
[Documentation](https://godoc.org/github.com/mattn/go-xmpp)
 | 
			
		||||
[Documentation](https://godoc.org/github.com/xmppo/go-xmpp)
 | 
			
		||||
 
 | 
			
		||||
@@ -2,11 +2,12 @@ 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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,20 +5,23 @@ 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")
 | 
			
		||||
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")
 | 
			
		||||
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")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func serverName(host string) string {
 | 
			
		||||
	return strings.Split(host, ":")[0]
 | 
			
		||||
@@ -48,7 +51,8 @@ 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,
 | 
			
		||||
@@ -59,7 +63,6 @@ func main() {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	talk, err = options.NewClient()
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								go.mod
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
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=
 | 
			
		||||
							
								
								
									
										125
									
								
								xmpp_avatar.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								xmpp_avatar.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
}
 | 
			
		||||
@@ -5,27 +5,45 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const IQTypeGet = "get"
 | 
			
		||||
const IQTypeSet = "set"
 | 
			
		||||
const IQTypeResult = "result"
 | 
			
		||||
const (
 | 
			
		||||
	IQTypeGet    = "get"
 | 
			
		||||
	IQTypeSet    = "set"
 | 
			
		||||
	IQTypeResult = "result"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Client) Discovery() (string, error) {
 | 
			
		||||
	const namespace = "http://jabber.org/protocol/disco#items"
 | 
			
		||||
	// use getCookie for a pseudo random id.
 | 
			
		||||
	// use UUIDv4 for a pseudo random id.
 | 
			
		||||
	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.
 | 
			
		||||
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>"
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, 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>\n"
 | 
			
		||||
	_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
 | 
			
		||||
	return id, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// rawInformation send a IQ request with the the payload body to the server
 | 
			
		||||
// rawInformation send a IQ request with 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>"
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
 | 
			
		||||
	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)
 | 
			
		||||
	return id, err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								xmpp_muc.go
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								xmpp_muc.go
									
									
									
									
									
								
							@@ -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.conn, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>",
 | 
			
		||||
	return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>\n",
 | 
			
		||||
		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.conn, "<presence to='%s/%s'>\n"+
 | 
			
		||||
	return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
			
		||||
		"<x xmlns='%s'>"+
 | 
			
		||||
		"<history maxchars='0'/></x>\n"+
 | 
			
		||||
		"</presence>",
 | 
			
		||||
		"<history maxchars='0'/></x>"+
 | 
			
		||||
		"</presence>\n",
 | 
			
		||||
		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.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s' />\n" +
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
			
		||||
		return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
			
		||||
			"<x xmlns='%s' />"+
 | 
			
		||||
			"</presence>\n",
 | 
			
		||||
			xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
			
		||||
	case CharHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
			
		||||
			"<x xmlns='%s'>"+
 | 
			
		||||
			"<history maxchars='%d'/></x>"+
 | 
			
		||||
			"</presence>\n",
 | 
			
		||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case StanzaHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
			
		||||
			"<x xmlns='%s'>"+
 | 
			
		||||
			"<history maxstanzas='%d'/></x>"+
 | 
			
		||||
			"</presence>\n",
 | 
			
		||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case SecondsHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
			
		||||
			"<x xmlns='%s'>"+
 | 
			
		||||
			"<history seconds='%d'/></x>"+
 | 
			
		||||
			"</presence>\n",
 | 
			
		||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
			
		||||
	case SinceHistory:
 | 
			
		||||
		if history_date != nil {
 | 
			
		||||
			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 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 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.conn, "<presence to='%s/%s'>\n" +
 | 
			
		||||
			"<x xmlns='%s'>\n" +
 | 
			
		||||
			"<password>%s</password>" +
 | 
			
		||||
			"</x>\n" +
 | 
			
		||||
			"</presence>",
 | 
			
		||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
 | 
			
		||||
		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))
 | 
			
		||||
	case CharHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		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)
 | 
			
		||||
	case StanzaHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		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)
 | 
			
		||||
	case SecondsHistory:
 | 
			
		||||
		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)
 | 
			
		||||
		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)
 | 
			
		||||
	case SinceHistory:
 | 
			
		||||
		if history_date != nil {
 | 
			
		||||
			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 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 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.conn, "<presence from='%s' to='%s' type='unavailable' />",
 | 
			
		||||
	return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />\n",
 | 
			
		||||
		c.jid, xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										14
									
								
								xmpp_ping.go
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								xmpp_ping.go
									
									
									
									
									
								
							@@ -11,23 +11,23 @@ func (c *Client) PingC2S(jid, server string) error {
 | 
			
		||||
	if server == "" {
 | 
			
		||||
		server = c.domain
 | 
			
		||||
	}
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
			
		||||
		"</iq>",
 | 
			
		||||
	_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>"+
 | 
			
		||||
		"</iq>\n",
 | 
			
		||||
		xmlEscape(jid), xmlEscape(server))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) PingS2S(fromServer, toServer string) error {
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
			
		||||
		"</iq>",
 | 
			
		||||
	_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>"+
 | 
			
		||||
		"<ping xmlns='urn:xmpp:ping'/>"+
 | 
			
		||||
		"</iq>\n",
 | 
			
		||||
		xmlEscape(fromServer), xmlEscape(toServer))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) SendResultPing(id, toServer string) error {
 | 
			
		||||
	_, err := fmt.Fprintf(c.conn, "<iq type='result' to='%s' id='%s'/>",
 | 
			
		||||
	_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>\n",
 | 
			
		||||
		xmlEscape(toServer), xmlEscape(id))
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										128
									
								
								xmpp_pubsub.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								xmpp_pubsub.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
			
		||||
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))
 | 
			
		||||
}
 | 
			
		||||
@@ -5,16 +5,21 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Client) ApproveSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribed'/>",
 | 
			
		||||
	fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>\n",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) RevokeSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribed'/>",
 | 
			
		||||
	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",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Client) RequestSubscription(jid string) {
 | 
			
		||||
	fmt.Fprintf(c.conn, "<presence to='%s' type='subscribe'/>",
 | 
			
		||||
	fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>\n",
 | 
			
		||||
		xmlEscape(jid))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								xmpp_test.go
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								xmpp_test.go
									
									
									
									
									
								
							@@ -85,12 +85,14 @@ 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\": \"<text>\"}\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">
 | 
			
		||||
@@ -114,3 +116,28 @@ 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")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user