[WIP] Fix ban by software version, some other under-hood changes

- Use updated version of upstream go-xmpp + fixes on client-side
- Introduce golangci-lint v2 + fixes
- Move especially noisy debugging stuff to separate config handles to be able to switch them off
- Add a bit debug to ban by software path
This commit is contained in:
eleksir
2025-11-30 14:19:01 +03:00
parent a128b473ff
commit 3d85dbcb58
55 changed files with 4165 additions and 13190 deletions

5
vendor/github.com/xmppo/go-xmpp/.travis.yml generated vendored Normal file
View File

@@ -0,0 +1,5 @@
language: go
go:
- tip
script:
- go test

27
vendor/github.com/xmppo/go-xmpp/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,27 @@
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

6
vendor/github.com/xmppo/go-xmpp/README.md generated vendored Normal file
View File

@@ -0,0 +1,6 @@
go-xmpp
=======
go xmpp library (original was written by russ cox )
[Documentation](https://godoc.org/github.com/xmppo/go-xmpp)

51
vendor/github.com/xmppo/go-xmpp/const.go generated vendored Normal file
View File

@@ -0,0 +1,51 @@
package xmpp
const (
Version = "0.3.1"
// FAST Mechanisms
HT_SHA_256_ENDP = "HT-SHA-256-ENDP"
HT_SHA_256_EXPR = "HT-SHA-256-EXPR"
HT_SHA_256_NONE = "HT-SHA-256-NONE"
HT_SHA_256_UNIQ = "HT-SHA-256-UNIQ"
// IQ Types
IQTypeError = "error"
IQTypeGet = "get"
IQTypeResult = "result"
IQTypeSet = "set"
// SCRAM Mechanisms
SCRAM_SHA_1_PLUS = "SCRAM-SHA-1-PLUS"
SCRAM_SHA_1 = "SCRAM-SHA-1"
SCRAM_SHA_256_PLUS = "SCRAM-SHA-256-PLUS"
SCRAM_SHA_256 = "SCRAM-SHA-256"
SCRAM_SHA_512_PLUS = "SCRAM-SHA-512-PLUS"
SCRAM_SHA_512 = "SCRAM-SHA-512"
UPGR_SCRAM_SHA_256 = "UPGR-SCRAM-SHA-256"
UPGR_SCRAM_SHA_512 = "UPGR-SCRAM-SHA-512"
// XMPP Namespaces
XMPPNS_AVATAR_PEP_DATA = "urn:xmpp:avatar:data"
XMPPNS_AVATAR_PEP_METADATA = "urn:xmpp:avatar:metadata"
XMPPNS_BIND_0 = "urn:xmpp:bind:0"
XMPPNS_CLIENT = "jabber:client"
XMPPNS_DISCO_INFO = "http://jabber.org/protocol/disco#info"
XMPPNS_DISCO_ITEMS = "http://jabber.org/protocol/disco#items"
XMPPNS_FAST_0 = "urn:xmpp:fast:0"
XMPPNS_HTTP_UPLOAD_0 = "urn:xmpp:http:upload:0"
XMPPNS_IQ_VERSION = "jabber:iq:version"
XMPPNS_MUC = "http://jabber.org/protocol/muc"
XMPPNS_MUC_USER = "http://jabber.org/protocol/muc#user"
XMPPNS_PING = "urn:xmpp:ping"
XMPPNS_PUBSUB_EVENT = "http://jabber.org/protocol/pubsub#event"
XMPPNS_PUBSUB = "http://jabber.org/protocol/pubsub"
XMPPNS_SASL_2 = "urn:xmpp:sasl:2"
XMPPNS_SASL_CB_0 = "urn:xmpp:sasl-cb:0"
XMPPNS_SASL_UPGRADE_0 = "urn:xmpp:sasl:upgrade:0"
XMPPNS_XMPP_SASL = "urn:ietf:params:xml:ns:xmpp-sasl"
XMPPNS_SCRAM_UPGRADE_0 = "urn:xmpp:scram-upgrade:0"
XMPPNS_SID_0 = "urn:xmpp:sid:0"
XMPPNS_STREAM = "http://etherx.jabber.org/streams"
XMPPNS_STREAM_LIMITS_0 = "urn:xmpp:stream-limits:0"
XMPPNS_TIME = "urn:xmpp:time"
XMPPNS_XMPP_TLS = "urn:ietf:params:xml:ns:xmpp-tls"
XMPPNS_XMPP_BIND = "urn:ietf:params:xml:ns:xmpp-bind"
XMPPNS_XMPP_SESSION = "urn:ietf:params:xml:ns:xmpp-session"
)

2460
vendor/github.com/xmppo/go-xmpp/xmpp.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

120
vendor/github.com/xmppo/go-xmpp/xmpp_avatar.go generated vendored Normal file
View File

@@ -0,0 +1,120 @@
package xmpp
import (
"crypto/sha1"
"encoding/base64"
"encoding/hex"
"encoding/xml"
"errors"
"strconv"
)
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) //nolint: gosec,G401 // It is not about security.
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) error {
return c.PubsubSubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarUnsubscribeMetadata(jid string) error {
return c.PubsubUnsubscribeNode(XMPPNS_AVATAR_PEP_METADATA, jid)
}
func (c *Client) AvatarRequestData(jid string) error {
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_DATA, jid)
}
func (c *Client) AvatarRequestDataByID(jid, id string) error {
return c.PubsubRequestItem(XMPPNS_AVATAR_PEP_DATA, jid, id)
}
func (c *Client) AvatarRequestMetadata(jid string) error {
return c.PubsubRequestLastItems(XMPPNS_AVATAR_PEP_METADATA, jid)
}

110
vendor/github.com/xmppo/go-xmpp/xmpp_disco.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
package xmpp
import (
"encoding/xml"
)
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"`
X []DiscoX `xml:"x"`
}
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 {
ID string
From string
To string
Features []string
Identities []DiscoIdentity
X []DiscoX
}
type DiscoX struct {
XMLName xml.Name `xml:"x"`
Field []DiscoXField `xml:"field"`
}
type DiscoXField struct {
Type string `xml:"type,attr"`
Var string `xml:"var,attr"`
Value []string `xml:"value"`
}
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
}

57
vendor/github.com/xmppo/go-xmpp/xmpp_error.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package xmpp
import (
"fmt"
)
// ErrorServiceUnavailable implements error response about a feature that is not available. Currently implemented for
// xep-0030.
// QueryXmlns is about incoming xmlns attribute in query tag.
// Node is about incoming node attribute in query tag (looks like it used only in disco#commands).
//
// If queried feature is not here on purpose, standards suggest to answer with this stanza.
func (c *Client) ErrorServiceUnavailable(v IQ, queryXmlns, node string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s' ", queryXmlns)
if node != "" {
query += fmt.Sprintf("node='%s' />", node)
} else {
query += "/>"
}
query += "<error type='cancel'>"
query += "<service-unavailable xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />"
query += "</error>"
return c.RawInformation(
v.To,
v.From,
v.ID,
IQTypeError,
query,
)
}
// ErrorNotImplemented implements error response about a feature that is not (yet?) implemented.
// Xmlns is about not implemented feature.
//
// If queried feature is not here because of it under development or for similar reasons, standards suggest to answer with
// this stanza.
func (c *Client) ErrorNotImplemented(v IQ, xmlns, feature string) (string, error) {
query := "<error type='cancel'>"
query += "<feature-not-implemented xmlns='urn:ietf:params:xml:ns:xmpp-stanzas' />"
query += fmt.Sprintf(
"<unsupported xmlns='%s' feature='%s' />",
xmlns,
feature,
)
query += "</error>"
return c.RawInformation(
v.To,
v.From,
v.ID,
IQTypeError,
query,
)
}

38
vendor/github.com/xmppo/go-xmpp/xmpp_http_upload.go generated vendored Normal file
View File

@@ -0,0 +1,38 @@
package xmpp
import (
"encoding/xml"
)
type Slot struct {
// TODO: Maybe this doesn't belong here
ID string
XMLName xml.Name `xml:"slot"`
Put Put
Get Get
}
type Put struct {
XMLName xml.Name `xml:"put"`
Url string `xml:"url,attr"`
Headers []Header `xml:"header"`
}
type Get struct {
XMLName xml.Name `xml:"get"`
Url string `xml:"url,attr"`
}
type Header struct {
XMLName xml.Name `xml:"header"`
Name string `xml:"name,attr"`
Value string `xml:",innerxml"`
}
// Oob is an out-of-band url/description, used in file uploads.
// See https://xmpp.org/extensions/xep-0066.html
type Oob struct {
XMLName xml.Name `xml:"x,xmlns:jabber:x:oob"`
Url string `xml:"url"`
Desc string `xml:"desc"`
}

View File

@@ -0,0 +1,97 @@
package xmpp
import (
"fmt"
"time"
)
func (c *Client) Discovery() (string, error) {
// use UUIDv4 for a pseudo random id.
reqID := getUUID()
return c.RawInformationQuery(c.jid, c.domain, reqID, IQTypeGet, XMPPNS_DISCO_ITEMS, "")
}
// Discover information about a node. Empty node queries info about server itself.
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, getUUID(), IQTypeGet, query)
}
// Discover information about given item from given jid.
func (c *Client) DiscoverInfo(to string) (string, error) {
query := fmt.Sprintf("<query xmlns='%s'/>", XMPPNS_DISCO_INFO)
return c.RawInformation(c.jid, to, getUUID(), 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, getUUID(), 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>\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 payload body to the server
func (c *Client) RawInformation(from, to, id, iqType, body string) (string, error) {
const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'>%s</iq>\n"
_, err := fmt.Fprintf(c.stanzaWriter, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, body)
return id, err
}
// UrnXMPPTimeResponse implements response to query entity's current time (xep-0202).
func (c *Client) UrnXMPPTimeResponse(v IQ, timezoneOffset string) (string, error) {
query := fmt.Sprintf(
"<time xmlns=\"%s\"><tzo>%s</tzo><utc>%s</utc></time>",
XMPPNS_TIME,
timezoneOffset,
time.Now().UTC().Format(time.RFC3339),
)
return c.RawInformation(
v.To,
v.From,
v.ID,
IQTypeResult,
query,
)
}
// IqVersionResponse responding with software version, according to xep-0092.
func (c *Client) IqVersionResponse(v IQ, name string, version string, os string) (string, error) {
if name == "" {
name = "go-xmpp"
version = Version
}
if version == "" {
version = "undefined"
}
query := fmt.Sprintf("<query xmlns=\"%s\">", XMPPNS_IQ_VERSION)
query += fmt.Sprintf("<name>%s</name>", name)
query += fmt.Sprintf("<version>%s</version>", version)
if os != "" {
query += fmt.Sprintf("<os>%s</os>", os)
}
query += "</query>"
return c.RawInformation(
v.To,
v.From,
v.ID,
IQTypeResult,
query,
)
}

133
vendor/github.com/xmppo/go-xmpp/xmpp_muc.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright 2013 Flo Lauber <dev@qatfy.at>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// TODO(flo):
// - support password protected MUC rooms
// - cleanup signatures of join/leave functions
package xmpp
import (
"errors"
"fmt"
"time"
)
const (
NoHistory = 0
CharHistory = 1
StanzaHistory = 2
SecondsHistory = 3
SinceHistory = 4
)
// Send sends room topic wrapped inside an XMPP message stanza body.
func (c *Client) SendTopic(chat Chat) (n int, err error) {
return fmt.Fprintf(c.stanzaWriter, "<message to='%s' type='%s' xml:lang='en'>"+"<subject>%s</subject></message>\n",
xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
func (c *Client) JoinMUCNoHistory(jid, nick string) (n int, err error) {
if nick == "" {
nick = c.jid
}
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxchars='0'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC)
}
// xep-0045 7.2
func (c *Client) JoinMUC(jid, nick string, history_type, history int, history_date *time.Time) (n int, err error) {
if nick == "" {
nick = c.jid
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s' />"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC)
case CharHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
case StanzaHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
case SecondsHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, history_date.Format(time.RFC3339))
}
}
return 0, errors.New("unknown history option")
}
// xep-0045 7.2.6
func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) {
if nick == "" {
nick = c.jid
}
switch history_type {
case NoHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"</x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password))
case CharHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxchars='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
case StanzaHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history maxstanzas='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
case SecondsHistory:
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history seconds='%d'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history)
case SinceHistory:
if history_date != nil {
return fmt.Fprintf(c.stanzaWriter, "<presence to='%s/%s'>"+
"<x xmlns='%s'>"+
"<password>%s</password>"+
"<history since='%s'/></x>"+
"</presence>\n",
xmlEscape(jid), xmlEscape(nick), XMPPNS_MUC, xmlEscape(password), history_date.Format(time.RFC3339))
}
}
return 0, errors.New("unknown history option")
}
// xep-0045 7.14
func (c *Client) LeaveMUC(jid string) (n int, err error) {
return fmt.Fprintf(c.stanzaWriter, "<presence from='%s' to='%s' type='unavailable' />\n",
c.jid, xmlEscape(jid))
}

57
vendor/github.com/xmppo/go-xmpp/xmpp_ping.go generated vendored Normal file
View File

@@ -0,0 +1,57 @@
package xmpp
import (
"fmt"
"time"
)
func (c *Client) PingC2S(jid, server string) error {
if jid == "" {
jid = c.jid
}
if server == "" {
server = c.domain
}
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
xmlEscape(jid), xmlEscape(server), getUUID())
return err
}
func (c *Client) PingS2S(fromServer, toServer string) error {
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/>"+
"</iq>\n",
xmlEscape(fromServer), xmlEscape(toServer), getUUID())
return err
}
func (c *Client) SendResultPing(id, toServer string) error {
_, err := fmt.Fprintf(c.stanzaWriter, "<iq type='result' to='%s' id='%s'/>\n",
xmlEscape(toServer), xmlEscape(id))
return err
}
func (c *Client) sendPeriodicPings() {
for range c.periodicPingTicker.C {
// Reset ticker for periodic pings if configured.
if c.periodicPings {
c.periodicPingTicker.Reset(c.periodicPingPeriod)
}
c.periodicPingID = getUUID()
c.periodicPingReply = false
_, err := fmt.Fprintf(c.stanzaWriter, "<iq from='%s' to='%s' id='%s' type='get'>"+
"<ping xmlns='urn:xmpp:ping'/></iq>\n",
xmlEscape(c.jid), xmlEscape(c.domain), c.periodicPingID)
if err != nil {
c.Close()
}
time.Sleep(c.periodicPingTimeout)
if !c.periodicPingReply {
c.shutdown = true
fmt.Fprintf(c.stanzaWriter, "</stream:stream>\n")
c.conn.Close()
}
}
}

135
vendor/github.com/xmppo/go-xmpp/xmpp_pubsub.go generated vendored Normal file
View File

@@ -0,0 +1,135 @@
package xmpp
import (
"encoding/xml"
"fmt"
)
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) error {
id := getUUID()
c.subIDs = append(c.subIDs, id)
_, err := c.RawInformation(c.jid,
jid,
id,
"set",
pubsubSubscriptionStanza(node, c.jid))
return err
}
func (c *Client) PubsubUnsubscribeNode(node, jid string) error {
id := getUUID()
c.unsubIDs = append(c.unsubIDs, id)
_, err := c.RawInformation(c.jid,
jid,
id,
"set",
pubsubUnsubscriptionStanza(node, c.jid))
return err
}
func (c *Client) PubsubRequestLastItems(node, jid string) error {
id := getUUID()
c.itemsIDs = append(c.itemsIDs, id)
body := fmt.Sprintf("<items node='%s'/>", node)
_, err := c.RawInformation(c.jid, jid, id, "get", pubsubStanza(body))
return err
}
func (c *Client) PubsubRequestItem(node, jid, id string) error {
stanzaID := getUUID()
c.itemsIDs = append(c.itemsIDs, stanzaID)
body := fmt.Sprintf("<items node='%s'><item id='%s'/></items>", node, id)
_, err := c.RawInformation(c.jid, jid, stanzaID, "get", pubsubStanza(body))
return err
}

30
vendor/github.com/xmppo/go-xmpp/xmpp_subscription.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package xmpp
import (
"fmt"
)
func (c *Client) ApproveSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribed'/>\n",
xmlEscape(jid))
}
func (c *Client) RevokeSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='unsubscribed'/>\n",
xmlEscape(jid))
}
// Deprecated: Use RevertSubscription instead.
func (c *Client) RetrieveSubscription(jid string) {
c.RevertSubscription(jid)
}
func (c *Client) RevertSubscription(jid string) {
fmt.Fprintf(c.conn, "<presence to='%s' type='unsubscribe'/>\n",
xmlEscape(jid))
}
func (c *Client) RequestSubscription(jid string) {
fmt.Fprintf(c.stanzaWriter, "<presence to='%s' type='subscribe'/>\n",
xmlEscape(jid))
}