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  )
 | 
					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 (
 | 
					import (
 | 
				
			||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"github.com/mattn/go-gtk/gtk"
 | 
					 | 
				
			||||||
	"github.com/mattn/go-xmpp"
 | 
					 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/matterbridge/go-xmpp"
 | 
				
			||||||
 | 
						"github.com/mattn/go-gtk/gtk"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,20 +5,23 @@ import (
 | 
				
			|||||||
	"crypto/tls"
 | 
						"crypto/tls"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"github.com/mattn/go-xmpp"
 | 
					 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/matterbridge/go-xmpp"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var server = flag.String("server", "talk.google.com:443", "server")
 | 
					var (
 | 
				
			||||||
var username = flag.String("username", "", "username")
 | 
						server        = flag.String("server", "talk.google.com:443", "server")
 | 
				
			||||||
var password = flag.String("password", "", "password")
 | 
						username      = flag.String("username", "", "username")
 | 
				
			||||||
var status = flag.String("status", "xa", "status")
 | 
						password      = flag.String("password", "", "password")
 | 
				
			||||||
var statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
 | 
						status        = flag.String("status", "xa", "status")
 | 
				
			||||||
var notls = flag.Bool("notls", false, "No TLS")
 | 
						statusMessage = flag.String("status-msg", "I for one welcome our new codebot overlords.", "status message")
 | 
				
			||||||
var debug = flag.Bool("debug", false, "debug output")
 | 
						notls         = flag.Bool("notls", false, "No TLS")
 | 
				
			||||||
var session = flag.Bool("session", false, "use server session")
 | 
						debug         = flag.Bool("debug", false, "debug output")
 | 
				
			||||||
 | 
						session       = flag.Bool("session", false, "use server session")
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func serverName(host string) string {
 | 
					func serverName(host string) string {
 | 
				
			||||||
	return strings.Split(host, ":")[0]
 | 
						return strings.Split(host, ":")[0]
 | 
				
			||||||
@@ -48,7 +51,8 @@ func main() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	var talk *xmpp.Client
 | 
						var talk *xmpp.Client
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	options := xmpp.Options{Host: *server,
 | 
						options := xmpp.Options{
 | 
				
			||||||
 | 
							Host:          *server,
 | 
				
			||||||
		User:          *username,
 | 
							User:          *username,
 | 
				
			||||||
		Password:      *password,
 | 
							Password:      *password,
 | 
				
			||||||
		NoTLS:         *notls,
 | 
							NoTLS:         *notls,
 | 
				
			||||||
@@ -59,7 +63,6 @@ func main() {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	talk, err = options.NewClient()
 | 
						talk, err = options.NewClient()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							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"
 | 
						"strconv"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const IQTypeGet = "get"
 | 
					const (
 | 
				
			||||||
const IQTypeSet = "set"
 | 
						IQTypeGet    = "get"
 | 
				
			||||||
const IQTypeResult = "result"
 | 
						IQTypeSet    = "set"
 | 
				
			||||||
 | 
						IQTypeResult = "result"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) Discovery() (string, error) {
 | 
					func (c *Client) Discovery() (string, error) {
 | 
				
			||||||
	const namespace = "http://jabber.org/protocol/disco#items"
 | 
						// use UUIDv4 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.
 | 
				
			||||||
func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
 | 
					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>"
 | 
						const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>\n"
 | 
				
			||||||
	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
 | 
						_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
 | 
				
			||||||
	return id, err
 | 
						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) {
 | 
					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>"
 | 
						const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>\n"
 | 
				
			||||||
	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
 | 
						_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
 | 
				
			||||||
	return id, err
 | 
						return id, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										100
									
								
								xmpp_muc.go
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								xmpp_muc.go
									
									
									
									
									
								
							@@ -8,9 +8,9 @@
 | 
				
			|||||||
package xmpp
 | 
					package xmpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
	"errors"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -25,7 +25,7 @@ const (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Send sends room topic wrapped inside an XMPP message stanza body.
 | 
					// Send sends room topic wrapped inside an XMPP message stanza body.
 | 
				
			||||||
func (c *Client) SendTopic(chat Chat) (n int, err error) {
 | 
					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))
 | 
							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 == "" {
 | 
						if nick == "" {
 | 
				
			||||||
		nick = c.jid
 | 
							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'>"+
 | 
							"<x xmlns='%s'>"+
 | 
				
			||||||
		"<history maxchars='0'/></x>\n"+
 | 
							"<history maxchars='0'/></x>"+
 | 
				
			||||||
		"</presence>",
 | 
							"</presence>\n",
 | 
				
			||||||
		xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
							xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -47,34 +47,34 @@ func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_da
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	switch history_type {
 | 
						switch history_type {
 | 
				
			||||||
	case NoHistory:
 | 
						case NoHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s' />\n" +
 | 
								"<x xmlns='%s' />"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC)
 | 
				
			||||||
	case CharHistory:
 | 
						case CharHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<history maxchars='%d'/></x>\n"+
 | 
								"<history maxchars='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
				
			||||||
	case StanzaHistory:
 | 
						case StanzaHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<history maxstanzas='%d'/></x>\n"+
 | 
								"<history maxstanzas='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
				
			||||||
	case SecondsHistory:
 | 
						case SecondsHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<history seconds='%d'/></x>\n"+
 | 
								"<history seconds='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, history)
 | 
				
			||||||
	case SinceHistory:
 | 
						case SinceHistory:
 | 
				
			||||||
		if history_date != nil {
 | 
							if history_date != nil {
 | 
				
			||||||
			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
								return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
				"<x xmlns='%s'>\n" +
 | 
									"<x xmlns='%s'>"+
 | 
				
			||||||
				"<history since='%s'/></x>\n" +
 | 
									"<history since='%s'/></x>"+
 | 
				
			||||||
				"</presence>",
 | 
									"</presence>\n",
 | 
				
			||||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
 | 
									xmlEscape(jid), xmlEscape(nick), nsMUC, history_date.Format(time.RFC3339))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -88,40 +88,40 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	switch history_type {
 | 
						switch history_type {
 | 
				
			||||||
	case NoHistory:
 | 
						case NoHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<password>%s</password>" +
 | 
								"<password>%s</password>"+
 | 
				
			||||||
			"</x>\n" +
 | 
								"</x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
 | 
				
			||||||
	case CharHistory:
 | 
						case CharHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<password>%s</password>\n"+
 | 
								"<password>%s</password>"+
 | 
				
			||||||
			"<history maxchars='%d'/></x>\n"+
 | 
								"<history maxchars='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
				
			||||||
	case StanzaHistory:
 | 
						case StanzaHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<password>%s</password>\n"+
 | 
								"<password>%s</password>"+
 | 
				
			||||||
			"<history maxstanzas='%d'/></x>\n"+
 | 
								"<history maxstanzas='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
				
			||||||
	case SecondsHistory:
 | 
						case SecondsHistory:
 | 
				
			||||||
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
							return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
			"<x xmlns='%s'>\n" +
 | 
								"<x xmlns='%s'>"+
 | 
				
			||||||
			"<password>%s</password>\n"+
 | 
								"<password>%s</password>"+
 | 
				
			||||||
			"<history seconds='%d'/></x>\n"+
 | 
								"<history seconds='%d'/></x>"+
 | 
				
			||||||
			"</presence>",
 | 
								"</presence>\n",
 | 
				
			||||||
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
								xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
 | 
				
			||||||
	case SinceHistory:
 | 
						case SinceHistory:
 | 
				
			||||||
		if history_date != nil {
 | 
							if history_date != nil {
 | 
				
			||||||
			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
 | 
								return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
 | 
				
			||||||
				"<x xmlns='%s'>\n" +
 | 
									"<x xmlns='%s'>"+
 | 
				
			||||||
				"<password>%s</password>\n"+
 | 
									"<password>%s</password>"+
 | 
				
			||||||
				"<history since='%s'/></x>\n" +
 | 
									"<history since='%s'/></x>"+
 | 
				
			||||||
				"</presence>",
 | 
									"</presence>\n",
 | 
				
			||||||
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
 | 
									xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -130,6 +130,6 @@ func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_typ
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// xep-0045 7.14
 | 
					// xep-0045 7.14
 | 
				
			||||||
func (c *Client) LeaveMUC(jid string) (n int, err error) {
 | 
					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))
 | 
							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 == "" {
 | 
						if server == "" {
 | 
				
			||||||
		server = c.domain
 | 
							server = c.domain
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='c2s1' type='get'>\n"+
 | 
						_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='c2s1' type='get'>"+
 | 
				
			||||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
							"<ping xmlns='urn:xmpp:ping'/>"+
 | 
				
			||||||
		"</iq>",
 | 
							"</iq>\n",
 | 
				
			||||||
		xmlEscape(jid), xmlEscape(server))
 | 
							xmlEscape(jid), xmlEscape(server))
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) PingS2S(fromServer, toServer string) error {
 | 
					func (c *Client) PingS2S(fromServer, toServer string) error {
 | 
				
			||||||
	_, err := fmt.Fprintf(c.conn, "<iq from='%s' to='%s' id='s2s1' type='get'>\n"+
 | 
						_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='s2s1' type='get'>"+
 | 
				
			||||||
		"<ping xmlns='urn:xmpp:ping'/>\n"+
 | 
							"<ping xmlns='urn:xmpp:ping'/>"+
 | 
				
			||||||
		"</iq>",
 | 
							"</iq>\n",
 | 
				
			||||||
		xmlEscape(fromServer), xmlEscape(toServer))
 | 
							xmlEscape(fromServer), xmlEscape(toServer))
 | 
				
			||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) SendResultPing(id, toServer string) error {
 | 
					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))
 | 
							xmlEscape(toServer), xmlEscape(id))
 | 
				
			||||||
	return err
 | 
						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) {
 | 
					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))
 | 
							xmlEscape(jid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) RevokeSubscription(jid string) {
 | 
					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))
 | 
							xmlEscape(jid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) RequestSubscription(jid string) {
 | 
					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))
 | 
							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",
 | 
								"\n\t\t\n\t\t\n\t",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		OtherElem: []XMLElement{
 | 
							OtherElem: []XMLElement{
 | 
				
			||||||
			XMLElement{
 | 
								{
 | 
				
			||||||
				XMLName:  xml.Name{Space: "google:mobile:data", Local: "gcm"},
 | 
									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",
 | 
									InnerXML: "\n\t\t{\"random\": \"<text>\"}\n\t",
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			XMLElement{
 | 
								{
 | 
				
			||||||
				XMLName: xml.Name{Space: "jabber:client", Local: "error"},
 | 
									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: `
 | 
									InnerXML: `
 | 
				
			||||||
		<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
 | 
							<bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/>
 | 
				
			||||||
		<text 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")
 | 
							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