summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/matterbridge/go-xmpp
diff options
context:
space:
mode:
authorWim <wim@42.be>2020-03-28 23:05:09 +0100
committerWim <wim@42.be>2020-03-29 15:03:24 +0200
commita30bb8fed072331238db3d6bdb94ee034e777bff (patch)
tree25136d224d3d1ffab0745429321173db5fc18ed7 /vendor/github.com/matterbridge/go-xmpp
parent092ca1cd678c44fa078bba11d4d219e61498342a (diff)
downloadmatterbridge-msglm-a30bb8fed072331238db3d6bdb94ee034e777bff.tar.gz
matterbridge-msglm-a30bb8fed072331238db3d6bdb94ee034e777bff.tar.bz2
matterbridge-msglm-a30bb8fed072331238db3d6bdb94ee034e777bff.zip
Sync matterbridge/go-xmpp with upstream
Diffstat (limited to 'vendor/github.com/matterbridge/go-xmpp')
-rw-r--r--vendor/github.com/matterbridge/go-xmpp/xmpp.go191
-rw-r--r--vendor/github.com/matterbridge/go-xmpp/xmpp_muc.go82
-rw-r--r--vendor/github.com/matterbridge/go-xmpp/xmpp_pubsub.go133
3 files changed, 327 insertions, 79 deletions
diff --git a/vendor/github.com/matterbridge/go-xmpp/xmpp.go b/vendor/github.com/matterbridge/go-xmpp/xmpp.go
index 39aa2d8c..cc13218e 100644
--- a/vendor/github.com/matterbridge/go-xmpp/xmpp.go
+++ b/vendor/github.com/matterbridge/go-xmpp/xmpp.go
@@ -1,4 +1,4 @@
-// Copyright 2011 The Go Authors. All rights reserved.
+// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
@@ -45,6 +45,9 @@ const (
// Default TLS configuration options
var DefaultConfig tls.Config
+// DebugWriter is the writer used to write debugging output to.
+var DebugWriter io.Writer = os.Stderr
+
// Cookie is a unique XMPP session identifier
type Cookie uint64
@@ -191,21 +194,40 @@ type Options struct {
// Status message
StatusMessage string
-
- // Logger
- Logger io.Writer
}
// NewClient establishes a new Client connection based on a set of Options.
func (o Options) NewClient() (*Client, error) {
host := o.Host
+ if strings.TrimSpace(host) == "" {
+ a := strings.SplitN(o.User, "@", 2)
+ if len(a) == 2 {
+ if _, addrs, err := net.LookupSRV("xmpp-client", "tcp", a[1]); err == nil {
+ if len(addrs) > 0 {
+ // default to first record
+ host = fmt.Sprintf("%s:%d", addrs[0].Target, addrs[0].Port)
+ defP := addrs[0].Priority
+ for _, adr := range addrs {
+ if adr.Priority < defP {
+ host = fmt.Sprintf("%s:%d", adr.Target, adr.Port)
+ defP = adr.Priority
+ }
+ }
+ } else {
+ host = a[1]
+ }
+ } else {
+ host = a[1]
+ }
+ }
+ }
c, err := connect(host, o.User, o.Password)
if err != nil {
return nil, err
}
- if strings.LastIndex(o.Host, ":") > 0 {
- host = host[:strings.LastIndex(o.Host, ":")]
+ if strings.LastIndex(host, ":") > 0 {
+ host = host[:strings.LastIndex(host, ":")]
}
client := new(Client)
@@ -487,6 +509,8 @@ func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string
case f.StartTLS == nil:
// the server does not support STARTTLS
return f, nil
+ case !o.StartTLS && f.StartTLS.Required == nil:
+ return f, nil
case f.StartTLS.Required != nil:
// the server requires STARTTLS.
case !o.StartTLS:
@@ -527,7 +551,7 @@ func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string
// will be returned.
func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
if o.Debug {
- c.p = xml.NewDecoder(tee{c.conn, o.Logger})
+ c.p = xml.NewDecoder(tee{c.conn, DebugWriter})
} else {
c.p = xml.NewDecoder(c.conn)
}
@@ -618,6 +642,11 @@ func (c *Client) Recv() (stanza interface{}, err error) {
}
switch v := val.(type) {
case *clientMessage:
+ if v.Event.XMLNS == XMPPNS_PUBSUB_EVENT {
+ // Handle Pubsub notifications
+ return pubsubClientToReturn(v.Event), nil
+ }
+
stamp, _ := time.Parse(
"2006-01-02T15:04:05Z",
v.Delay.Stamp,
@@ -644,25 +673,101 @@ func (c *Client) Recv() (stanza interface{}, err error) {
case *clientPresence:
return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
case *clientIQ:
- // TODO check more strictly
- if bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns='urn:xmpp:ping'/>`)) || bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns="urn:xmpp:ping"/>`)) {
+ switch {
+ case v.Query.XMLName.Space == "urn:xmpp:ping":
+ // TODO check more strictly
err := c.SendResultPing(v.ID, v.From)
if err != nil {
return Chat{}, err
}
+ fallthrough
+ case v.Type == "error":
+ switch v.ID {
+ case "sub1":
+ // Pubsub subscription failed
+ var errs []clientPubsubError
+ err := xml.Unmarshal([]byte(v.Error.InnerXML), &errs)
+ if err != nil {
+ return PubsubSubscription{}, err
+ }
+
+ var errsStr []string
+ for _, e := range errs {
+ errsStr = append(errsStr, e.XMLName.Local)
+ }
+
+ return PubsubSubscription{
+ Errors: errsStr,
+ }, nil
+ }
+ case v.Type == "result" && v.ID == "unsub1":
+ // Unsubscribing MAY contain a pubsub element. But it does
+ // not have to
+ return PubsubUnsubscription{
+ SubID: "",
+ JID: v.From,
+ Node: "",
+ Errors: nil,
+ }, nil
+ case v.Query.XMLName.Local == "pubsub":
+ switch v.ID {
+ case "sub1":
+ // Subscription or unsubscription was successful
+ var sub clientPubsubSubscription
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
+ if err != nil {
+ return PubsubSubscription{}, err
+ }
+
+ return PubsubSubscription{
+ SubID: sub.SubID,
+ JID: sub.JID,
+ Node: sub.Node,
+ Errors: nil,
+ }, nil
+ case "unsub1":
+ var sub clientPubsubSubscription
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &sub)
+ if err != nil {
+ return PubsubUnsubscription{}, err
+ }
+
+ return PubsubUnsubscription{
+ SubID: sub.SubID,
+ JID: v.From,
+ Node: sub.Node,
+ Errors: nil,
+ }, nil
+ case "items1", "items3":
+ var p clientPubsubItems
+ err := xml.Unmarshal([]byte(v.Query.InnerXML), &p)
+ if err != nil {
+ return PubsubItems{}, err
+ }
+
+ return PubsubItems{
+ p.Node,
+ pubsubItemsToReturn(p.Items),
+ }, nil
+ }
+ case v.Query.XMLName.Local == "":
+ return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type}, nil
+ default:
+ res, err := xml.Marshal(v.Query)
+ if err != nil {
+ return Chat{}, err
+ }
+
+ return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type,
+ Query: res}, nil
}
- return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type, Query: v.Query}, nil
}
}
}
// Send sends the message wrapped inside an XMPP message stanza body.
func (c *Client) Send(chat Chat) (n int, err error) {
- var subtext = ``
- var thdtext = ``
- var oobtext = ``
- var msgidtext = ``
- var msgcorrecttext = ``
+ var subtext, thdtext, oobtext, msgidtext, msgcorrecttext string
if chat.Subject != `` {
subtext = `<subject>` + xmlEscape(chat.Subject) + `</subject>`
}
@@ -676,20 +781,25 @@ func (c *Client) Send(chat Chat) (n int, err error) {
}
oobtext += `</x>`
}
+
if chat.ID != `` {
msgidtext = `id='` + xmlEscape(chat.ID) + `'`
+ } else {
+ msgidtext = `id='` + cnonce() + `'`
}
+
if chat.ReplaceID != `` {
msgcorrecttext = `<replace id='` + xmlEscape(chat.ReplaceID) + `' xmlns='urn:xmpp:message-correct:0'/>`
}
- return fmt.Fprintf(c.conn, "<message to='%s' type='%s' " + msgidtext + " xml:lang='en'>" + subtext + "<body>%s</body>" + msgcorrecttext + oobtext + thdtext + "</message>",
- xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
+
+ stanza := "<message to='%s' type='%s' " + msgidtext + " xml:lang='en'>" + subtext + "<body>%s</body>" + msgcorrecttext + oobtext + thdtext + "</message>"
+
+ return fmt.Fprintf(c.conn, stanza, xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text))
}
// SendOOB sends OOB data wrapped inside an XMPP message stanza, without actual body.
func (c *Client) SendOOB(chat Chat) (n int, err error) {
- var thdtext = ``
- var oobtext = ``
+ var thdtext, oobtext string
if chat.Thread != `` {
thdtext = `<thread>` + xmlEscape(chat.Thread) + `</thread>`
}
@@ -700,8 +810,8 @@ func (c *Client) SendOOB(chat Chat) (n int, err error) {
}
oobtext += `</x>`
}
- return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>" + oobtext + thdtext + "</message>",
- xmlEscape(chat.Remote), xmlEscape(chat.Type))
+ return fmt.Fprintf(c.conn, "<message to='%s' type='%s' id='%s' xml:lang='en'>"+oobtext+thdtext+"</message>",
+ xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce())
}
// SendOrg sends the original text without being wrapped in an XMPP message stanza.
@@ -800,8 +910,8 @@ type bindBind struct {
}
type clientMessageCorrect struct {
- XMLName xml.Name `xml:"urn:xmpp:message-correct:0 replace"`
- ID string `xml:"id,attr"`
+ XMLName xml.Name `xml:"urn:xmpp:message-correct:0 replace"`
+ ID string `xml:"id,attr"`
}
// RFC 3921 B.1 jabber:client
@@ -813,11 +923,14 @@ type clientMessage struct {
Type string `xml:"type,attr"` // chat, error, groupchat, headline, or normal
// These should technically be []clientText, but string is much more convenient.
- Subject string `xml:"subject"`
- Body string `xml:"body"`
- Thread string `xml:"thread"`
+ Subject string `xml:"subject"`
+ Body string `xml:"body"`
+ Thread string `xml:"thread"`
ReplaceID clientMessageCorrect
+ // Pubsub
+ Event clientPubsubEvent `xml:"event"`
+
// Any hasn't matched element
Other []XMLElement `xml:",any"`
@@ -882,23 +995,25 @@ type clientPresence struct {
Error *clientError
}
-type clientIQ struct { // info/query
- XMLName xml.Name `xml:"jabber:client iq"`
- From string `xml:"from,attr"`
- ID string `xml:"id,attr"`
- To string `xml:"to,attr"`
- Type string `xml:"type,attr"` // error, get, result, set
- Query []byte `xml:",innerxml"`
+type clientIQ struct {
+ // info/query
+ XMLName xml.Name `xml:"jabber:client iq"`
+ From string `xml:"from,attr"`
+ ID string `xml:"id,attr"`
+ To string `xml:"to,attr"`
+ Type string `xml:"type,attr"` // error, get, result, set
+ Query XMLElement `xml:",any"`
Error clientError
Bind bindBind
}
type clientError struct {
- XMLName xml.Name `xml:"jabber:client error"`
- Code string `xml:",attr"`
- Type string `xml:",attr"`
- Any xml.Name
- Text string
+ XMLName xml.Name `xml:"jabber:client error"`
+ Code string `xml:",attr"`
+ Type string `xml:"type,attr"`
+ Any xml.Name
+ InnerXML []byte `xml:",innerxml"`
+ Text string
}
type clientQuery struct {
diff --git a/vendor/github.com/matterbridge/go-xmpp/xmpp_muc.go b/vendor/github.com/matterbridge/go-xmpp/xmpp_muc.go
index 7b50c128..840f4f97 100644
--- a/vendor/github.com/matterbridge/go-xmpp/xmpp_muc.go
+++ b/vendor/github.com/matterbridge/go-xmpp/xmpp_muc.go
@@ -8,19 +8,19 @@
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.
@@ -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" +
+ return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
+ "<x xmlns='%s' />\n"+
"</presence>",
- xmlEscape(jid), xmlEscape(nick), nsMUC)
+ xmlEscape(jid), xmlEscape(nick), nsMUC)
case CharHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case StanzaHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ xmlEscape(jid), xmlEscape(nick), nsMUC, history)
case SecondsHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ 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" +
+ 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))
+ 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" +
+ 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))
+ xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
case CharHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case StanzaHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
case SecondsHistory:
- return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n" +
- "<x xmlns='%s'>\n" +
+ 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)
+ 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" +
+ return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
+ "<x xmlns='%s'>\n"+
"<password>%s</password>\n"+
- "<history since='%s'/></x>\n" +
+ "<history since='%s'/></x>\n"+
"</presence>",
- xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
+ xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
}
}
return 0, errors.New("Unknown history option")
diff --git a/vendor/github.com/matterbridge/go-xmpp/xmpp_pubsub.go b/vendor/github.com/matterbridge/go-xmpp/xmpp_pubsub.go
new file mode 100644
index 00000000..74e60d86
--- /dev/null
+++ b/vendor/github.com/matterbridge/go-xmpp/xmpp_pubsub.go
@@ -0,0 +1,133 @@
+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 clientPubsub struct {
+ XMLName xml.Name `xml:"pubsub"`
+ Items clientPubsubItems `xml:"items"`
+}
+
+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))
+}